summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/riscv/boot/dts/starfive/Makefile7
-rw-r--r--arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2-ac108.dts64
-rw-r--r--arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2-amp.dts59
-rw-r--r--arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2-tdm.dts59
-rw-r--r--arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2-wm8960.dts71
-rw-r--r--arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi450
-rw-r--r--arch/riscv/boot/dts/starfive/jh7110.dtsi1
-rw-r--r--arch/riscv/boot/dts/starfive/vf2-overlay/Makefile3
-rw-r--r--arch/riscv/boot/dts/starfive/vf2-overlay/vf2-overlay-can.dtso23
-rw-r--r--arch/riscv/boot/dts/starfive/vf2-overlay/vf2-overlay-uart3-i2c.dtso75
-rw-r--r--arch/riscv/configs/starfive_visionfive2_defconfig574
-rw-r--r--drivers/bluetooth/Kconfig4
-rw-r--r--drivers/bluetooth/Makefile1
-rwxr-xr-xdrivers/bluetooth/aic_btusb/Makefile80
-rw-r--r--drivers/bluetooth/aic_btusb/aic_btusb.c5031
-rw-r--r--drivers/bluetooth/aic_btusb/aic_btusb.h753
-rw-r--r--drivers/bluetooth/aic_btusb/aic_btusb_external_featrue.c126
-rw-r--r--drivers/bluetooth/aic_btusb/aic_btusb_external_featrue.h3
-rw-r--r--drivers/gpu/drm/panel/Kconfig10
-rw-r--r--drivers/gpu/drm/panel/Makefile1
-rw-r--r--drivers/gpu/drm/panel/panel-starfive-jadard.c828
-rw-r--r--drivers/gpu/drm/verisilicon/Kconfig2
-rw-r--r--drivers/gpu/drm/verisilicon/starfive_drm_dsi.c202
-rw-r--r--drivers/gpu/drm/verisilicon/vs_dc.c6
-rw-r--r--drivers/gpu/drm/verisilicon/vs_dc.h2
-rw-r--r--drivers/input/touchscreen/goodix.c4
-rw-r--r--drivers/media/platform/starfive/v4l2_driver/Readme.txt5
-rw-r--r--drivers/media/platform/starfive/v4l2_driver/sc2235.c2
-rw-r--r--drivers/media/platform/starfive/v4l2_driver/stf_common.h2
-rw-r--r--drivers/media/platform/starfive/v4l2_driver/stf_csi.c6
-rw-r--r--drivers/media/platform/starfive/v4l2_driver/stf_csi.h1
-rw-r--r--drivers/media/platform/starfive/v4l2_driver/stf_csi_hw_ops.c18
-rw-r--r--drivers/media/platform/starfive/v4l2_driver/stf_csiphy_hw_ops.c12
-rw-r--r--drivers/net/wireless/Kconfig2
-rw-r--r--drivers/net/wireless/Makefile4
-rwxr-xr-xdrivers/net/wireless/aic8800/Kconfig10
-rwxr-xr-xdrivers/net/wireless/aic8800/Makefile71
-rw-r--r--drivers/net/wireless/aic8800/aic8800_fdrv/Kconfig36
-rw-r--r--drivers/net/wireless/aic8800/aic8800_fdrv/Makefile373
-rw-r--r--drivers/net/wireless/aic8800/aic8800_fdrv/aic_br_ext.c1569
-rw-r--r--drivers/net/wireless/aic8800/aic8800_fdrv/aic_br_ext.h73
-rw-r--r--drivers/net/wireless/aic8800/aic8800_fdrv/aic_vendor.c879
-rw-r--r--drivers/net/wireless/aic8800/aic8800_fdrv/aic_vendor.h346
-rw-r--r--drivers/net/wireless/aic8800/aic8800_fdrv/aicwf_compat_8800d80.c66
-rw-r--r--drivers/net/wireless/aic8800/aic8800_fdrv/aicwf_compat_8800d80.h6
-rw-r--r--drivers/net/wireless/aic8800/aic8800_fdrv/aicwf_compat_8800dc.c2329
-rw-r--r--drivers/net/wireless/aic8800/aic8800_fdrv/aicwf_compat_8800dc.h16
-rw-r--r--drivers/net/wireless/aic8800/aic8800_fdrv/aicwf_debug.h52
-rw-r--r--drivers/net/wireless/aic8800/aic8800_fdrv/aicwf_rx_prealloc.h27
-rw-r--r--drivers/net/wireless/aic8800/aic8800_fdrv/aicwf_sdio.c1251
-rw-r--r--drivers/net/wireless/aic8800/aic8800_fdrv/aicwf_sdio.h99
-rw-r--r--drivers/net/wireless/aic8800/aic8800_fdrv/aicwf_txrxif.c1227
-rw-r--r--drivers/net/wireless/aic8800/aic8800_fdrv/aicwf_txrxif.h291
-rw-r--r--drivers/net/wireless/aic8800/aic8800_fdrv/aicwf_usb.c2318
-rw-r--r--drivers/net/wireless/aic8800/aic8800_fdrv/aicwf_usb.h173
-rw-r--r--drivers/net/wireless/aic8800/aic8800_fdrv/aicwf_wext_linux.c1201
-rw-r--r--drivers/net/wireless/aic8800/aic8800_fdrv/aicwf_wext_linux.h11
-rw-r--r--drivers/net/wireless/aic8800/aic8800_fdrv/hal_desc.h376
-rw-r--r--drivers/net/wireless/aic8800/aic8800_fdrv/ipc_compat.h25
-rw-r--r--drivers/net/wireless/aic8800/aic8800_fdrv/ipc_host.c771
-rw-r--r--drivers/net/wireless/aic8800/aic8800_fdrv/ipc_host.h508
-rw-r--r--drivers/net/wireless/aic8800/aic8800_fdrv/ipc_shared.h802
-rw-r--r--drivers/net/wireless/aic8800/aic8800_fdrv/lmac_mac.h588
-rw-r--r--drivers/net/wireless/aic8800/aic8800_fdrv/lmac_msg.h3023
-rw-r--r--drivers/net/wireless/aic8800/aic8800_fdrv/lmac_types.h62
-rw-r--r--drivers/net/wireless/aic8800/aic8800_fdrv/md5.c161
-rw-r--r--drivers/net/wireless/aic8800/aic8800_fdrv/md5.h48
-rw-r--r--drivers/net/wireless/aic8800/aic8800_fdrv/reg_access.h161
-rw-r--r--drivers/net/wireless/aic8800/aic8800_fdrv/reg_ipc_app.h299
-rw-r--r--drivers/net/wireless/aic8800/aic8800_fdrv/regdb.c2873
-rw-r--r--drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_bfmer.c105
-rw-r--r--drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_bfmer.h100
-rw-r--r--drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_cfgfile.c237
-rw-r--r--drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_cfgfile.h35
-rw-r--r--drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_cmds.c541
-rw-r--r--drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_cmds.h120
-rw-r--r--drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_compat.h428
-rw-r--r--drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_debugfs.c2251
-rw-r--r--drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_debugfs.h203
-rw-r--r--drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_defs.h767
-rw-r--r--drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_dini.c294
-rw-r--r--drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_dini.h20
-rw-r--r--drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_events.h1243
-rw-r--r--drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_fw_dump.c568
-rw-r--r--drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_fw_trace.c47
-rw-r--r--drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_fw_trace.h35
-rw-r--r--drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_gki.c18
-rw-r--r--drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_gki.h11
-rw-r--r--drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_irqs.c67
-rw-r--r--drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_irqs.h20
-rw-r--r--drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_main.c9565
-rw-r--r--drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_main.h38
-rw-r--r--drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_mesh.c42
-rw-r--r--drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_mesh.h45
-rw-r--r--drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_mod_params.c1755
-rw-r--r--drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_mod_params.h70
-rw-r--r--drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_msg_rx.c1566
-rw-r--r--drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_msg_rx.h19
-rw-r--r--drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_msg_tx.c3753
-rw-r--r--drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_msg_tx.h181
-rw-r--r--drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_mu_group.c659
-rw-r--r--drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_mu_group.h179
-rw-r--r--drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_pci.c94
-rw-r--r--drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_pci.h17
-rw-r--r--drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_platform.c2306
-rw-r--r--drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_platform.h142
-rw-r--r--drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_prof.h133
-rw-r--r--drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_radar.c1644
-rw-r--r--drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_radar.h160
-rw-r--r--drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_rx.c2219
-rw-r--r--drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_rx.h402
-rw-r--r--drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_strs.c261
-rw-r--r--drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_strs.h31
-rw-r--r--drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_tdls.c796
-rw-r--r--drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_tdls.h54
-rw-r--r--drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_testmode.c226
-rw-r--r--drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_testmode.h64
-rw-r--r--drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_tx.c2143
-rw-r--r--drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_tx.h198
-rw-r--r--drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_txq.c1364
-rw-r--r--drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_txq.h397
-rw-r--r--drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_utils.c39
-rw-r--r--drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_utils.h142
-rw-r--r--drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_v7.c192
-rw-r--r--drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_v7.h20
-rw-r--r--drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_version.h11
-rw-r--r--drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_version_gen.h5
-rw-r--r--drivers/net/wireless/aic8800/aic8800_fdrv/sdio_host.c142
-rw-r--r--drivers/net/wireless/aic8800/aic8800_fdrv/sdio_host.h42
-rw-r--r--drivers/net/wireless/aic8800/aic8800_fdrv/usb_host.c154
-rw-r--r--drivers/net/wireless/aic8800/aic8800_fdrv/usb_host.h43
-rw-r--r--drivers/net/wireless/aic8800/aic_load_fw/Kconfig5
-rw-r--r--drivers/net/wireless/aic8800/aic_load_fw/Makefile105
-rw-r--r--drivers/net/wireless/aic8800/aic_load_fw/aic_bluetooth_main.c71
-rw-r--r--drivers/net/wireless/aic8800/aic_load_fw/aic_compat_8800d80.c331
-rw-r--r--drivers/net/wireless/aic8800/aic_load_fw/aic_compat_8800d80.h37
-rw-r--r--drivers/net/wireless/aic8800/aic_load_fw/aic_txrxif.c486
-rw-r--r--drivers/net/wireless/aic8800/aic_load_fw/aic_txrxif.h216
-rw-r--r--drivers/net/wireless/aic8800/aic_load_fw/aicbluetooth.c1125
-rw-r--r--drivers/net/wireless/aic8800/aic_load_fw/aicbluetooth.h22
-rw-r--r--drivers/net/wireless/aic8800/aic_load_fw/aicbluetooth_cmds.c472
-rw-r--r--drivers/net/wireless/aic8800/aic_load_fw/aicbluetooth_cmds.h298
-rw-r--r--drivers/net/wireless/aic8800/aic_load_fw/aicwf_debug.h52
-rw-r--r--drivers/net/wireless/aic8800/aic_load_fw/aicwf_rx_prealloc.c120
-rw-r--r--drivers/net/wireless/aic8800/aic_load_fw/aicwf_rx_prealloc.h26
-rw-r--r--drivers/net/wireless/aic8800/aic_load_fw/aicwf_txq_prealloc.c61
-rw-r--r--drivers/net/wireless/aic8800/aic_load_fw/aicwf_txq_prealloc.h4
-rw-r--r--drivers/net/wireless/aic8800/aic_load_fw/aicwf_usb.c1716
-rw-r--r--drivers/net/wireless/aic8800/aic_load_fw/aicwf_usb.h187
-rw-r--r--drivers/net/wireless/aic8800/aic_load_fw/md5.c161
-rw-r--r--drivers/net/wireless/aic8800/aic_load_fw/md5.h48
-rw-r--r--drivers/net/wireless/aic8800/aic_load_fw/rwnx_version_gen.h4
-rw-r--r--drivers/net/wireless/eswin/Kconfig6
-rw-r--r--drivers/net/wireless/eswin/Makefile145
-rw-r--r--drivers/net/wireless/eswin/README.md119
-rw-r--r--drivers/net/wireless/eswin/ble_netconfig/btgatt-server.c1685
-rw-r--r--drivers/net/wireless/eswin/compile_test.sh29
-rw-r--r--drivers/net/wireless/eswin/ecrnx_bfmer.c105
-rw-r--r--drivers/net/wireless/eswin/ecrnx_bfmer.h100
-rw-r--r--drivers/net/wireless/eswin/ecrnx_cfgfile.c288
-rw-r--r--drivers/net/wireless/eswin/ecrnx_cfgfile.h37
-rw-r--r--drivers/net/wireless/eswin/ecrnx_cmds.c325
-rw-r--r--drivers/net/wireless/eswin/ecrnx_cmds.h102
-rw-r--r--drivers/net/wireless/eswin/ecrnx_compat.h529
-rw-r--r--drivers/net/wireless/eswin/ecrnx_debug.c46
-rw-r--r--drivers/net/wireless/eswin/ecrnx_debug.h106
-rw-r--r--drivers/net/wireless/eswin/ecrnx_debugfs.c2576
-rw-r--r--drivers/net/wireless/eswin/ecrnx_debugfs.h198
-rw-r--r--drivers/net/wireless/eswin/ecrnx_events.h1308
-rw-r--r--drivers/net/wireless/eswin/ecrnx_fw_dump.c573
-rw-r--r--drivers/net/wireless/eswin/ecrnx_fw_trace.c895
-rw-r--r--drivers/net/wireless/eswin/ecrnx_fw_trace.h161
-rw-r--r--drivers/net/wireless/eswin/ecrnx_iwpriv.c396
-rw-r--r--drivers/net/wireless/eswin/ecrnx_mod_params.c1408
-rw-r--r--drivers/net/wireless/eswin/ecrnx_mod_params.h86
-rw-r--r--drivers/net/wireless/eswin/ecrnx_msg_rx.c1604
-rw-r--r--drivers/net/wireless/eswin/ecrnx_msg_rx.h18
-rw-r--r--drivers/net/wireless/eswin/ecrnx_msg_tx.c3173
-rw-r--r--drivers/net/wireless/eswin/ecrnx_msg_tx.h198
-rw-r--r--drivers/net/wireless/eswin/ecrnx_mu_group.c659
-rw-r--r--drivers/net/wireless/eswin/ecrnx_mu_group.h179
-rw-r--r--drivers/net/wireless/eswin/ecrnx_platform.c1107
-rw-r--r--drivers/net/wireless/eswin/ecrnx_platform.h142
-rw-r--r--drivers/net/wireless/eswin/ecrnx_prof.h136
-rw-r--r--drivers/net/wireless/eswin/ecrnx_radar.c1647
-rw-r--r--drivers/net/wireless/eswin/ecrnx_radar.h160
-rw-r--r--drivers/net/wireless/eswin/ecrnx_strs.c252
-rw-r--r--drivers/net/wireless/eswin/ecrnx_strs.h31
-rw-r--r--drivers/net/wireless/eswin/ecrnx_testmode.c226
-rw-r--r--drivers/net/wireless/eswin/ecrnx_testmode.h64
-rw-r--r--drivers/net/wireless/eswin/ecrnx_txq.c1772
-rw-r--r--drivers/net/wireless/eswin/ecrnx_txq.h506
-rw-r--r--drivers/net/wireless/eswin/ecrnx_utils.c1333
-rw-r--r--drivers/net/wireless/eswin/ecrnx_utils.h209
-rw-r--r--drivers/net/wireless/eswin/ecrnx_version.h10
-rw-r--r--drivers/net/wireless/eswin/eswin_port/eswin_utils.c194
-rw-r--r--drivers/net/wireless/eswin/eswin_port/eswin_utils.h123
-rw-r--r--drivers/net/wireless/eswin/feature_config/6600_config87
-rw-r--r--drivers/net/wireless/eswin/feature_config/6600u_config98
-rw-r--r--drivers/net/wireless/eswin/fullmac/Makefile167
-rw-r--r--drivers/net/wireless/eswin/fullmac/ecrnx_amt.c122
-rw-r--r--drivers/net/wireless/eswin/fullmac/ecrnx_amt.h21
-rw-r--r--drivers/net/wireless/eswin/fullmac/ecrnx_calibration_data.c27
-rw-r--r--drivers/net/wireless/eswin/fullmac/ecrnx_calibration_data.h80
-rw-r--r--drivers/net/wireless/eswin/fullmac/ecrnx_debugfs_custom.c1171
-rw-r--r--drivers/net/wireless/eswin/fullmac/ecrnx_debugfs_custom.h20
-rw-r--r--drivers/net/wireless/eswin/fullmac/ecrnx_debugfs_func.c995
-rw-r--r--drivers/net/wireless/eswin/fullmac/ecrnx_debugfs_func.h212
-rw-r--r--drivers/net/wireless/eswin/fullmac/ecrnx_defs.h1099
-rw-r--r--drivers/net/wireless/eswin/fullmac/ecrnx_main.c4492
-rw-r--r--drivers/net/wireless/eswin/fullmac/ecrnx_main.h19
-rw-r--r--drivers/net/wireless/eswin/fullmac/ecrnx_mesh.c42
-rw-r--r--drivers/net/wireless/eswin/fullmac/ecrnx_mesh.h45
-rw-r--r--drivers/net/wireless/eswin/fullmac/ecrnx_p2p.c72
-rw-r--r--drivers/net/wireless/eswin/fullmac/ecrnx_p2p.h60
-rw-r--r--drivers/net/wireless/eswin/fullmac/ecrnx_rx.c2541
-rw-r--r--drivers/net/wireless/eswin/fullmac/ecrnx_rx.h125
-rw-r--r--drivers/net/wireless/eswin/fullmac/ecrnx_tdls.c788
-rw-r--r--drivers/net/wireless/eswin/fullmac/ecrnx_tdls.h54
-rw-r--r--drivers/net/wireless/eswin/fullmac/ecrnx_tx.c1877
-rw-r--r--drivers/net/wireless/eswin/fullmac/ecrnx_tx.h285
-rw-r--r--drivers/net/wireless/eswin/fw_head_check.c258
-rw-r--r--drivers/net/wireless/eswin/fw_head_check.h43
-rw-r--r--drivers/net/wireless/eswin/hal_desc.c189
-rw-r--r--drivers/net/wireless/eswin/hal_desc.h670
-rw-r--r--drivers/net/wireless/eswin/ipc_compat.h25
-rw-r--r--drivers/net/wireless/eswin/ipc_host.c784
-rw-r--r--drivers/net/wireless/eswin/ipc_host.h517
-rw-r--r--drivers/net/wireless/eswin/ipc_shared.h836
-rw-r--r--drivers/net/wireless/eswin/lmac_mac.h467
-rw-r--r--drivers/net/wireless/eswin/lmac_msg.h2767
-rw-r--r--drivers/net/wireless/eswin/lmac_types.h69
-rw-r--r--drivers/net/wireless/eswin/reg_access.h156
-rw-r--r--drivers/net/wireless/eswin/reg_ipc_app.h299
-rw-r--r--drivers/net/wireless/eswin/sdio/core.c774
-rw-r--r--drivers/net/wireless/eswin/sdio/core.h364
-rw-r--r--drivers/net/wireless/eswin/sdio/debug.c202
-rw-r--r--drivers/net/wireless/eswin/sdio/debug.h31
-rw-r--r--drivers/net/wireless/eswin/sdio/ecrnx_sdio.c559
-rw-r--r--drivers/net/wireless/eswin/sdio/ecrnx_sdio.h173
-rw-r--r--drivers/net/wireless/eswin/sdio/fw.c158
-rw-r--r--drivers/net/wireless/eswin/sdio/fw.h19
-rw-r--r--drivers/net/wireless/eswin/sdio/sdio.c925
-rw-r--r--drivers/net/wireless/eswin/sdio/sdio.h156
-rw-r--r--drivers/net/wireless/eswin/sdio/sdio_host_interface.h29
-rw-r--r--drivers/net/wireless/eswin/slave_log_buf.c149
-rw-r--r--drivers/net/wireless/eswin/slave_log_buf.h37
-rw-r--r--drivers/net/wireless/eswin/usb/core.c212
-rw-r--r--drivers/net/wireless/eswin/usb/core.h297
-rw-r--r--drivers/net/wireless/eswin/usb/debug.c187
-rw-r--r--drivers/net/wireless/eswin/usb/debug.h21
-rw-r--r--drivers/net/wireless/eswin/usb/ecrnx_usb.c447
-rw-r--r--drivers/net/wireless/eswin/usb/ecrnx_usb.h110
-rw-r--r--drivers/net/wireless/eswin/usb/fw.c266
-rw-r--r--drivers/net/wireless/eswin/usb/fw.h19
-rw-r--r--drivers/net/wireless/eswin/usb/usb.c1041
-rw-r--r--drivers/net/wireless/eswin/usb/usb.h101
-rw-r--r--drivers/net/wireless/eswin/usb/usb_host_interface.h29
-rw-r--r--drivers/net/wireless/eswin/wifi_ecr6600u.cfg3
-rw-r--r--drivers/phy/m31/7110-m31-dphy.h23
-rw-r--r--drivers/phy/m31/phy-m31-dphy-tx0.c365
-rw-r--r--drivers/regulator/axp20x-regulator.c1
262 files changed, 127372 insertions, 453 deletions
diff --git a/arch/riscv/boot/dts/starfive/Makefile b/arch/riscv/boot/dts/starfive/Makefile
index a9289046f637..e75298cb833d 100644
--- a/arch/riscv/boot/dts/starfive/Makefile
+++ b/arch/riscv/boot/dts/starfive/Makefile
@@ -9,8 +9,13 @@ DTC_FLAGS_jh7110-evb := -@
dtb-$(CONFIG_ARCH_STARFIVE) += jh7100-beaglev-starlight.dtb
dtb-$(CONFIG_ARCH_STARFIVE) += jh7100-starfive-visionfive-v1.dtb
+subdir-y += vf2-overlay
dtb-$(CONFIG_ARCH_STARFIVE) += jh7110-starfive-visionfive-2-v1.2a.dtb
-dtb-$(CONFIG_ARCH_STARFIVE) += jh7110-starfive-visionfive-2-v1.3b.dtb
+dtb-$(CONFIG_ARCH_STARFIVE) += jh7110-starfive-visionfive-2-v1.3b.dtb \
+ jh7110-starfive-visionfive-2-ac108.dtb \
+ jh7110-starfive-visionfive-2-amp.dtb \
+ jh7110-starfive-visionfive-2-tdm.dtb \
+ jh7110-starfive-visionfive-2-wm8960.dtb
subdir-y += evb-overlay
dtb-$(CONFIG_ARCH_STARFIVE) += jh7110-evb.dtb \
diff --git a/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2-ac108.dts b/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2-ac108.dts
new file mode 100644
index 000000000000..3f027a138859
--- /dev/null
+++ b/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2-ac108.dts
@@ -0,0 +1,64 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+/*
+ * Copyright (C) 2022 StarFive Technology Co., Ltd.
+ * Copyright (C) 2022 Hal Feng <hal.feng@starfivetech.com>
+ */
+
+/dts-v1/;
+#include "jh7110-starfive-visionfive-2-v1.3b.dts"
+
+/ {
+ /* i2s + ac108 */
+ sound0: snd-card0 {
+ compatible = "simple-audio-card";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ simple-audio-card,name = "Starfive-AC108-Sound-Card";
+ simple-audio-card,dai-link@0 {
+ reg = <0>;
+ format = "i2s";
+ bitclock-master = <&sndcodec1>;
+ frame-master = <&sndcodec1>;
+
+ widgets =
+ "Microphone", "Mic Jack",
+ "Line", "Line In",
+ "Line", "Line Out",
+ "Speaker", "Speaker",
+ "Headphone", "Headphone Jack";
+ routing =
+ "Headphone Jack", "HP_L",
+ "Headphone Jack", "HP_R",
+ "Speaker", "SPK_LP",
+ "Speaker", "SPK_LN",
+ "LINPUT1", "Mic Jack",
+ "LINPUT3", "Mic Jack",
+ "RINPUT1", "Mic Jack",
+ "RINPUT2", "Mic Jack";
+
+ cpu {
+ sound-dai = <&i2srx_mst>;
+ };
+
+ sndcodec1: codec {
+ sound-dai = <&ac108>;
+ clocks = <&ac108_mclk>;
+ clock-names = "mclk";
+ };
+ };
+ };
+};
+
+&i2c0 {
+ ac108: ac108@3b {
+ compatible = "x-power,ac108_0";
+ reg = <0x3b>;
+ #sound-dai-cells = <0>;
+ data-protocol = <0>;
+ };
+};
+
+&i2srx_mst {
+ status = "okay";
+};
diff --git a/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2-amp.dts b/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2-amp.dts
new file mode 100644
index 000000000000..88b26b292541
--- /dev/null
+++ b/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2-amp.dts
@@ -0,0 +1,59 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+/*
+ * Copyright (C) 2022 StarFive Technology Co., Ltd.
+ */
+
+/dts-v1/;
+#include "jh7110-starfive-visionfive-2-v1.3b.dts"
+
+/ {
+ reserved-memory {
+ rpmsg_share_mem: rpmsg-shmem {
+ reg = <0x0 0x6e400000 0x0 0x400000>;
+ };
+
+ rtthread_reserved: rt@6e800000 {
+ reg = <0x0 0x6e800000 0x0 0x1800000>;
+ };
+ };
+
+ ipimb: ipimb@0 {
+ compatible = "starfive,ipi-amp-mailbox";
+ reg = <0x0 0x6e402000 0x0 0x2000>;
+ #mbox-cells = <2>;
+ rtos-hart-id = <4>;
+ };
+
+ rpmsg0: rpmsg@0 {
+ compatible = "starfive,amp-virtio-rpmsg";
+ reg = <0x0 0x6e404000 0x0 0x20000>,
+ <0x0 0x6e500000 0x0 0x200000>;
+ mbox-names = "rx", "tx";
+ mboxes = <&ipimb 0 0>, <&ipimb 1 0>;
+ vdev-nums = <8>;
+ };
+};
+
+&cpu1_intc {
+ enable-ipi-amp;
+};
+
+&cpu2_intc {
+ enable-ipi-amp;
+};
+
+&cpu3_intc {
+ enable-ipi-amp;
+};
+
+&gmac1 {
+ status = "disabled";
+};
+
+&pcie1 {
+ status = "disabled";
+};
+
+&U74_4 {
+ status = "disabled";
+};
diff --git a/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2-tdm.dts b/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2-tdm.dts
new file mode 100644
index 000000000000..b04396836dcc
--- /dev/null
+++ b/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2-tdm.dts
@@ -0,0 +1,59 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+/*
+ * Copyright (C) 2022 StarFive Technology Co., Ltd.
+ * Copyright (C) 2022 Hal Feng <hal.feng@starfivetech.com>
+ */
+
+/dts-v1/;
+#include "jh7110-starfive-visionfive-2-v1.3b.dts"
+
+/ {
+ sound5: snd-card5 {
+ compatible = "simple-audio-card";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ simple-audio-card,name = "Starfive-TDM-Sound-Card";
+ simple-audio-card,widgets = "Microphone", "Mic Jack",
+ "Line", "Line In",
+ "Line", "Line Out",
+ "Speaker", "Speaker",
+ "Headphone", "Headphone Jack";
+ simple-audio-card,routing = "Headphone Jack", "HP_L",
+ "Headphone Jack", "HP_R",
+ "Speaker", "SPK_LP",
+ "Speaker", "SPK_LN",
+ "LINPUT1", "Mic Jack",
+ "LINPUT3", "Mic Jack",
+ "RINPUT1", "Mic Jack",
+ "RINPUT2", "Mic Jack";
+
+ simple-audio-card,dai-link@0 {
+ reg = <0>;
+ format = "dsp_a";
+ bitclock-master = <&dailink_master>;
+ frame-master = <&dailink_master>;
+
+ cpu {
+ sound-dai = <&tdm>;
+ };
+ dailink_master: codec {
+ sound-dai = <&wm8960>;
+ clocks = <&wm8960_mclk>;
+ };
+ };
+ };
+};
+
+&i2c0 {
+ wm8960: codec@1a {
+ compatible = "wlf,wm8960";
+ reg = <0x1a>;
+ wlf,shared-lrclk;
+ #sound-dai-cells = <0>;
+ };
+};
+
+&tdm {
+ status = "okay";
+};
diff --git a/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2-wm8960.dts b/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2-wm8960.dts
new file mode 100644
index 000000000000..ef3c7fd5171d
--- /dev/null
+++ b/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2-wm8960.dts
@@ -0,0 +1,71 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+/*
+ * Copyright (C) 2022 StarFive Technology Co., Ltd.
+ * Copyright (C) 2022 Hal Feng <hal.feng@starfivetech.com>
+ */
+
+/dts-v1/;
+#include "jh7110-starfive-visionfive-2-v1.3b.dts"
+
+/ {
+ /* i2s + wm8960 */
+ sound6: snd-card6 {
+ compatible = "simple-audio-card";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ simple-audio-card,name = "Starfive-WM8960-Sound-Card";
+ simple-audio-card,dai-link@0 {
+ reg = <0>;
+ status = "okay";
+ format = "i2s";
+ bitclock-master = <&sndcodec1>;
+ frame-master = <&sndcodec1>;
+
+ widgets =
+ "Microphone", "Mic Jack",
+ "Line", "Line In",
+ "Line", "Line Out",
+ "Speaker", "Speaker",
+ "Headphone", "Headphone Jack";
+ routing =
+ "Headphone Jack", "HP_L",
+ "Headphone Jack", "HP_R",
+ "Speaker", "SPK_LP",
+ "Speaker", "SPK_LN",
+ "LINPUT1", "Mic Jack",
+ "LINPUT3", "Mic Jack",
+ "RINPUT1", "Mic Jack",
+ "RINPUT2", "Mic Jack";
+ cpu0 {
+ sound-dai = <&i2srx>;
+ };
+ cpu1 {
+ sound-dai = <&i2stx1>;
+ };
+
+ sndcodec1:codec {
+ sound-dai = <&wm8960>;
+ clocks = <&wm8960_mclk>;
+ clock-names = "mclk";
+ };
+ };
+ };
+};
+
+&i2c0 {
+ wm8960: codec@1a {
+ compatible = "wlf,wm8960";
+ reg = <0x1a>;
+ wlf,shared-lrclk;
+ #sound-dai-cells = <0>;
+ };
+};
+
+&i2srx {
+ status = "okay";
+};
+
+&i2stx1 {
+ status = "okay";
+};
diff --git a/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi b/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi
index a3a8b63f90cd..fd674ed1a973 100644
--- a/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi
+++ b/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi
@@ -7,6 +7,7 @@
/dts-v1/;
#include "jh7110.dtsi"
#include "jh7110-pinfunc.h"
+#include <dt-bindings/leds/common.h>
#include <dt-bindings/gpio/gpio.h>
/ {
@@ -14,7 +15,10 @@
ethernet0 = &gmac0;
ethernet1 = &gmac1;
i2c0 = &i2c0;
+ i2c1 = &i2c1;
i2c2 = &i2c2;
+ i2c3 = &i2c3;
+ i2c4 = &i2c4;
i2c5 = &i2c5;
i2c6 = &i2c6;
mmc0 = &mmc0;
@@ -22,6 +26,7 @@
pcie0 = &pcie0;
pcie1 = &pcie1;
serial0 = &uart0;
+ serial3 = &uart3;
};
chosen {
@@ -37,6 +42,44 @@
reg = <0x0 0x40000000 0x1 0x0>;
};
+ reserved-memory {
+ #address-cells = <2>;
+ #size-cells = <2>;
+ ranges;
+
+ linux,cma {
+ compatible = "shared-dma-pool";
+ reusable;
+ size = <0x0 0x20000000>;
+ alignment = <0x0 0x1000>;
+ alloc-ranges = <0x0 0x70000000 0x0 0x20000000>;
+ linux,cma-default;
+ };
+
+ e24_mem: e24@c0000000 {
+ reg = <0x0 0x6ce00000 0x0 0x1600000>;
+ };
+
+ xrp_reserved: xrpbuffer@f0000000 {
+ reg = <0x0 0x69c00000 0x0 0x01ffffff
+ 0x0 0x6bc00000 0x0 0x00001000
+ 0x0 0x6bc01000 0x0 0x00fff000
+ 0x0 0x6cc00000 0x0 0x00001000>;
+ };
+ };
+
+ leds {
+ compatible = "gpio-leds";
+
+ led-ack {
+ gpios = <&aongpio 3 GPIO_ACTIVE_HIGH>;
+ color = <LED_COLOR_ID_GREEN>;
+ function = LED_FUNCTION_HEARTBEAT;
+ linux,default-trigger = "heartbeat";
+ label = "ack";
+ };
+ };
+
gpio-restart {
compatible = "gpio-restart";
gpios = <&sysgpio 35 GPIO_ACTIVE_HIGH>;
@@ -48,9 +91,9 @@
#sound-dai-cells = <0>;
};
- sound-pwmdac {
+ sound3: snd-card3 {
compatible = "simple-audio-card";
- simple-audio-card,name = "StarFive-PWMDAC-Sound-Card";
+ simple-audio-card,name = "Starfive-PWMDAC-Sound-Card";
#address-cells = <1>;
#size-cells = <0>;
@@ -69,6 +112,48 @@
};
};
};
+
+ sound1: snd-card1 {
+ compatible = "simple-audio-card";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ simple-audio-card,name = "Starfive-HDMI-Sound-Card";
+ simple-audio-card,dai-link@0 {
+ reg = <0>;
+ format = "i2s";
+ bitclock-master = <&sndi2s0>;
+ frame-master = <&sndi2s0>;
+ mclk-fs = <256>;
+ status = "okay";
+
+ sndi2s0: cpu {
+ sound-dai = <&i2stx0>;
+ };
+
+ codec {
+ sound-dai = <&hdmi>;
+ };
+ };
+ };
+
+ ac108_mclk: ac108_mclk {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <24000000>;
+ };
+
+ clk_ext_camera: clk-ext-camera {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <24000000>;
+ };
+
+ wm8960_mclk: wm8960_mclk {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <24576000>;
+ };
};
&dvp_clk {
@@ -177,6 +262,42 @@
pinctrl-names = "default";
pinctrl-0 = <&i2c2_pins>;
status = "okay";
+
+ seeed_plane_i2c@45 {
+ compatible = "seeed_panel";
+ reg = <0x45>;
+
+ port {
+ panel_out0: endpoint {
+ remote-endpoint = <&dsi0_output>;
+ };
+ };
+ };
+
+ tinker_ft5406: tinker_ft5406@38 {
+ compatible = "tinker_ft5406";
+ reg = <0x38>;
+ };
+
+ touchscreen@14 {
+ compatible = "goodix,gt911";
+ reg = <0x14>;
+ irq-gpios = <&sysgpio 30 GPIO_ACTIVE_HIGH>;
+ reset-gpios = <&sysgpio 31 GPIO_ACTIVE_HIGH>;
+ };
+
+ panel_radxa@19 {
+ compatible ="starfive_jadard";
+ reg = <0x19>;
+ reset-gpio = <&sysgpio 23 0>;
+ enable-gpio = <&sysgpio 22 0>;
+
+ port {
+ panel_out1: endpoint {
+ remote-endpoint = <&dsi1_output>;
+ };
+ };
+ };
};
&i2c5 {
@@ -196,11 +317,36 @@
#interrupt-cells = <1>;
regulators {
+ mipi_0p9: ALDO1 {
+ regulator-boot-on;
+ regulator-compatible = "aldo1";
+ regulator-name = "mipi_0p9";
+ regulator-min-microvolt = <900000>;
+ regulator-max-microvolt = <900000>;
+ };
+
+ hdmi_0p9: ALDO5 {
+ regulator-boot-on;
+ regulator-compatible = "aldo5";
+ regulator-name = "hdmi_0p9";
+ regulator-min-microvolt = <900000>;
+ regulator-max-microvolt = <900000>;
+ };
+
+ hdmi_1p8: ALDO3 {
+ regulator-boot-on;
+ regulator-compatible = "aldo3";
+ regulator-name = "hdmi_1p8";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ };
+
vcc_3v3: dcdc1 {
regulator-boot-on;
regulator-always-on;
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3300000>;
+ regulator-compatible = "dcdc1";
regulator-name = "vcc_3v3";
};
@@ -208,6 +354,7 @@
regulator-always-on;
regulator-min-microvolt = <500000>;
regulator-max-microvolt = <1540000>;
+ regulator-compatible = "dcdc2";
regulator-name = "vdd-cpu";
};
@@ -216,6 +363,7 @@
regulator-always-on;
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <1800000>;
+ regulator-compatible = "aldo4";
regulator-name = "emmc_vdd";
};
};
@@ -230,12 +378,68 @@
pinctrl-names = "default";
pinctrl-0 = <&i2c6_pins>;
status = "okay";
+
+ imx219: imx219@10 {
+ compatible = "sony,imx219";
+ reg = <0x10>;
+ clocks = <&clk_ext_camera>;
+ clock-names = "xclk";
+ reset-gpio = <&sysgpio 18 0>;
+ rotation = <0>;
+ orientation = <1>; //CAMERA_ORIENTATION_BACK
+
+ port {
+ /* CSI2 bus endpoint */
+ imx219_to_csi2rx0: endpoint {
+ remote-endpoint = <&csi2rx0_from_imx219>;
+ bus-type = <4>; /* MIPI CSI-2 D-PHY */
+ clock-lanes = <4>;
+ data-lanes = <0 1>;
+ lane-polarities = <0 0 0>;
+ link-frequencies = /bits/ 64 <456000000>;
+ };
+ };
+ };
+
+ imx708: imx708@1a {
+ compatible = "sony,imx708";
+ reg = <0x1a>;
+ clocks = <&clk_ext_camera>;
+ reset-gpio = <&sysgpio 18 0>;
+
+ port {
+ imx708_to_csi2rx0: endpoint {
+ remote-endpoint = <&csi2rx0_from_imx708>;
+ data-lanes = <1 2>;
+ clock-noncontinuous;
+ link-frequencies = /bits/ 64 <450000000>;
+ };
+ };
+ };
+
+ ov4689: ov4689@36 {
+ compatible = "ovti,ov4689";
+ reg = <0x36>;
+ clocks = <&clk_ext_camera>;
+ clock-names = "xclk";
+ rotation = <180>;
+
+ port {
+ /* Parallel bus endpoint */
+ ov4689_to_csi2rx0: endpoint {
+ remote-endpoint = <&csi2rx0_from_ov4689>;
+ bus-type = <4>; /* MIPI CSI-2 D-PHY */
+ clock-lanes = <0>;
+ data-lanes = <1 2>;
+ };
+ };
+ };
};
&i2srx {
pinctrl-names = "default";
pinctrl-0 = <&i2srx_pins>;
- status = "okay";
+ status = "disabled";
};
&i2stx0 {
@@ -247,7 +451,7 @@
&i2stx1 {
pinctrl-names = "default";
pinctrl-0 = <&i2stx1_pins>;
- status = "okay";
+ status = "disabled";
};
&mmc0 {
@@ -696,12 +900,46 @@
slew-rate = <0>;
};
};
+
+ hdmi_pins: hdmi-0 {
+ scl-pins {
+ pinmux = <GPIOMUX(0, GPOUT_SYS_HDMI_DDC_SCL,
+ GPOEN_SYS_HDMI_DDC_SCL,
+ GPI_SYS_HDMI_DDC_SCL)>;
+ bias-pull-up;
+ input-enable;
+ };
+
+ sda-pins {
+ pinmux = <GPIOMUX(1, GPOUT_SYS_HDMI_DDC_SDA,
+ GPOEN_SYS_HDMI_DDC_SDA,
+ GPI_SYS_HDMI_DDC_SDA)>;
+ bias-pull-up;
+ input-enable;
+ };
+
+ cec-pins {
+ pinmux = <GPIOMUX(14, GPOUT_SYS_HDMI_CEC_SDA,
+ GPOEN_SYS_HDMI_CEC_SDA,
+ GPI_SYS_HDMI_CEC_SDA)>;
+ bias-pull-up;
+ input-enable;
+ };
+
+ hpd-pins {
+ pinmux = <GPIOMUX(15, GPOUT_LOW,
+ GPOEN_DISABLE,
+ GPI_SYS_HDMI_HPD)>;
+ bias-disable; /* external pull-up */
+ input-enable;
+ };
+ };
};
&tdm {
pinctrl-names = "default";
pinctrl-0 = <&tdm_pins>;
- status = "okay";
+ status = "disabled";
};
&uart0 {
@@ -730,3 +968,205 @@
&U74_4 {
cpu-supply = <&vdd_cpu>;
};
+
+
+&display {
+ ports = <&dc_out_dpi0>;
+ status = "okay";
+};
+
+&dc8200 {
+ status = "okay";
+
+ dc_out: port {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ dc_out_dpi0: endpoint@0 {
+ reg = <0>;
+ remote-endpoint = <&hdmi_input0>;
+ };
+ dc_out_dpi1: endpoint@1 {
+ reg = <1>;
+ remote-endpoint = <&hdmi_in_lcdc>;
+ };
+
+ dc_out_dpi2: endpoint@2 {
+ reg = <2>;
+ remote-endpoint = <&mipi_in>;
+ };
+ };
+};
+
+&hdmi {
+ status = "okay";
+ pinctrl-names = "default";
+ pinctrl-0 = <&hdmi_pins>;
+ hpd-gpio = <&sysgpio 15 GPIO_ACTIVE_HIGH>;
+ hdmi_0p9-supply = <&hdmi_0p9>;
+ hdmi_1p8-supply = <&hdmi_1p8>;
+
+ hdmi_in: port {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ hdmi_in_lcdc: endpoint@0 {
+ reg = <0>;
+ remote-endpoint = <&dc_out_dpi1>;
+ };
+ };
+};
+
+&rgb_output {
+ status = "disabled";
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ port@0 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0>;
+ hdmi_input0:endpoint@0 {
+ reg = <0>;
+ remote-endpoint = <&dc_out_dpi0>;
+ };
+ };
+ };
+};
+
+&dsi_output {
+ status = "okay";
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+ mipi_in: endpoint {
+ remote-endpoint = <&dc_out_dpi2>;
+ };
+ };
+
+ port@1 {
+ reg = <1>;
+ sf_dpi_output: endpoint {
+ remote-endpoint = <&dsi_in_port>;
+ };
+ };
+ };
+};
+
+&mipi_dsi {
+ status = "okay";
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ dsi0_output: endpoint@0 {
+ reg = <0>;
+ remote-endpoint = <&panel_out0>;
+ };
+
+ dsi1_output: endpoint@1 {
+ reg = <1>;
+ remote-endpoint = <&panel_out1>;
+ };
+
+ };
+
+ port@1{
+ reg = <1>;
+ dsi_in_port: endpoint {
+ remote-endpoint = <&sf_dpi_output>;
+ };
+ };
+
+ };
+};
+
+&mipi_dphy {
+ status = "okay";
+};
+
+&co_process {
+ memory-region = <&e24_mem>;
+ status = "okay";
+};
+
+
+&mailbox_contrl0 {
+ status = "okay";
+};
+
+&mailbox_client0 {
+ status = "okay";
+};
+
+&vpu_dec {
+ status = "okay";
+};
+
+&vpu_enc {
+ status = "okay";
+};
+
+&jpu {
+ status = "okay";
+};
+
+&gpu {
+ status = "okay";
+};
+
+&vin_sysctl {
+ /* when use dvp open this pinctrl*/
+ status = "okay";
+ mipi_0p9-supply = <&mipi_0p9>;
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@1 {
+ reg = <1>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ /* CSI2 bus endpoint */
+ csi2rx0_from_imx219: endpoint@0 {
+ reg = <0>;
+ remote-endpoint = <&imx219_to_csi2rx0>;
+ bus-type = <4>; /* MIPI CSI-2 D-PHY */
+ clock-lanes = <4>;
+ data-lanes = <0 1>;
+ lane-polarities = <0 0 0>;
+ status = "okay";
+ };
+
+ csi2rx0_from_imx708: endpoint@1 {
+ reg = <1>;
+ remote-endpoint = <&imx708_to_csi2rx0>;
+ bus-type = <4>; /* MIPI CSI-2 D-PHY */
+ clock-lanes = <4>;
+ data-lanes = <0 1>;
+ lane-polarities = <0 0 0>;
+ status = "okay";
+ };
+
+ csi2rx0_from_ov4689: endpoint@2 {
+ reg = <2>;
+ remote-endpoint = <&ov4689_to_csi2rx0>;
+ bus-type = <4>; /* MIPI CSI-2 D-PHY */
+ clock-lanes = <4>;
+ data-lanes = <0 1>;
+ status = "okay";
+ };
+ };
+ };
+}; \ No newline at end of file
diff --git a/arch/riscv/boot/dts/starfive/jh7110.dtsi b/arch/riscv/boot/dts/starfive/jh7110.dtsi
index a928096f1b14..93752f891c53 100644
--- a/arch/riscv/boot/dts/starfive/jh7110.dtsi
+++ b/arch/riscv/boot/dts/starfive/jh7110.dtsi
@@ -8,6 +8,7 @@
#include <dt-bindings/clock/starfive,jh7110-crg.h>
#include <dt-bindings/power/starfive,jh7110-pmu.h>
#include <dt-bindings/reset/starfive,jh7110-crg.h>
+#include <dt-bindings/interrupt-controller/irq.h>
#include <dt-bindings/thermal/thermal.h>
/ {
diff --git a/arch/riscv/boot/dts/starfive/vf2-overlay/Makefile b/arch/riscv/boot/dts/starfive/vf2-overlay/Makefile
new file mode 100644
index 000000000000..69bd8e3bc56e
--- /dev/null
+++ b/arch/riscv/boot/dts/starfive/vf2-overlay/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+dtb-$(CONFIG_ARCH_STARFIVE) += vf2-overlay-uart3-i2c.dtbo \
+ vf2-overlay-can.dtbo
diff --git a/arch/riscv/boot/dts/starfive/vf2-overlay/vf2-overlay-can.dtso b/arch/riscv/boot/dts/starfive/vf2-overlay/vf2-overlay-can.dtso
new file mode 100644
index 000000000000..bbbdfcdf0f1a
--- /dev/null
+++ b/arch/riscv/boot/dts/starfive/vf2-overlay/vf2-overlay-can.dtso
@@ -0,0 +1,23 @@
+/dts-v1/;
+/plugin/;
+#include <dt-bindings/gpio/gpio.h>
+#include "../jh7110-pinfunc.h"
+/ {
+ compatible = "starfive,jh7110";
+
+ //can0
+ fragment@0 {
+ target-path = "/soc/can@130d0000";
+ __overlay__ {
+ status = "okay";
+ };
+ };
+
+ //can1
+ fragment@1 {
+ target-path = "/soc/can@130e0000";
+ __overlay__ {
+ status = "okay";
+ };
+ };
+};
diff --git a/arch/riscv/boot/dts/starfive/vf2-overlay/vf2-overlay-uart3-i2c.dtso b/arch/riscv/boot/dts/starfive/vf2-overlay/vf2-overlay-uart3-i2c.dtso
new file mode 100644
index 000000000000..f51fc8f0a55f
--- /dev/null
+++ b/arch/riscv/boot/dts/starfive/vf2-overlay/vf2-overlay-uart3-i2c.dtso
@@ -0,0 +1,75 @@
+/dts-v1/;
+/plugin/;
+#include <dt-bindings/gpio/gpio.h>
+#include "../jh7110-pinfunc.h"
+/ {
+ compatible = "starfive,jh7110";
+
+ //sysgpio
+ fragment@0 {
+ target-path = "/soc/pinctrl@13040000";
+ __overlay__ {
+ dt_uart3_pins: dt-uart3-0 {
+ tx-pins {
+ pinmux = <GPIOMUX(60, GPOUT_SYS_UART3_TX,
+ GPOEN_ENABLE,
+ GPI_NONE)>;
+ bias-disable;
+ drive-strength = <12>;
+ input-disable;
+ input-schmitt-disable;
+ slew-rate = <0>;
+ };
+
+ rx-pins {
+ pinmux = <GPIOMUX(63, GPOUT_LOW,
+ GPOEN_DISABLE,
+ GPI_SYS_UART3_RX)>;
+ bias-pull-up;
+ drive-strength = <2>;
+ input-enable;
+ input-schmitt-enable;
+ slew-rate = <0>;
+ };
+ };
+
+ dt_i2c1_pins: dt-i2c1-0 {
+ i2c-pins {
+ pinmux = <GPIOMUX(42, GPOUT_LOW,
+ GPOEN_SYS_I2C1_CLK,
+ GPI_SYS_I2C1_CLK)>,
+ <GPIOMUX(43, GPOUT_LOW,
+ GPOEN_SYS_I2C1_DATA,
+ GPI_SYS_I2C1_DATA)>;
+ bias-pull-up;
+ input-enable;
+ input-schmitt-enable;
+ };
+ };
+ };
+ };
+
+ //uart3
+ fragment@1 {
+ target-path = "/soc/serial@12000000";
+ __overlay__ {
+ pinctrl-names = "default";
+ pinctrl-0 = <&dt_uart3_pins>;
+ status = "okay";
+ };
+ };
+
+ //i2c1
+ fragment@2 {
+ target-path = "/soc/i2c@10040000";
+ __overlay__ {
+ clock-frequency = <100000>;
+ i2c-sda-hold-time-ns = <300>;
+ i2c-sda-falling-time-ns = <510>;
+ i2c-scl-falling-time-ns = <510>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&dt_i2c1_pins>;
+ status = "okay";
+ };
+ };
+};
diff --git a/arch/riscv/configs/starfive_visionfive2_defconfig b/arch/riscv/configs/starfive_visionfive2_defconfig
new file mode 100644
index 000000000000..5a79a844f0fb
--- /dev/null
+++ b/arch/riscv/configs/starfive_visionfive2_defconfig
@@ -0,0 +1,574 @@
+CONFIG_COMPILE_TEST=y
+# CONFIG_WERROR is not set
+CONFIG_DEFAULT_HOSTNAME="StarFive"
+CONFIG_SYSVIPC=y
+CONFIG_POSIX_MQUEUE=y
+CONFIG_USELIB=y
+CONFIG_NO_HZ_IDLE=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_BPF_SYSCALL=y
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_CGROUPS=y
+CONFIG_CGROUP_SCHED=y
+CONFIG_CFS_BANDWIDTH=y
+CONFIG_CGROUP_BPF=y
+CONFIG_NAMESPACES=y
+CONFIG_USER_NS=y
+CONFIG_CHECKPOINT_RESTORE=y
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_EXPERT=y
+CONFIG_PERF_EVENTS=y
+CONFIG_SOC_STARFIVE=y
+CONFIG_NONPORTABLE=y
+CONFIG_SMP=y
+CONFIG_RISCV_AMP=y
+CONFIG_HZ_100=y
+CONFIG_HIBERNATION=y
+CONFIG_PM_STD_PARTITION="PARTLABEL=hibernation"
+CONFIG_PM_DEBUG=y
+CONFIG_PM_ADVANCED_DEBUG=y
+CONFIG_PM_TEST_SUSPEND=y
+CONFIG_CPU_IDLE=y
+CONFIG_RISCV_SBI_CPUIDLE=y
+CONFIG_CPU_FREQ=y
+CONFIG_CPU_FREQ_STAT=y
+CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y
+CONFIG_CPU_FREQ_GOV_POWERSAVE=y
+CONFIG_CPU_FREQ_GOV_USERSPACE=y
+CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y
+CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y
+CONFIG_CPUFREQ_DT=y
+# CONFIG_SECCOMP is not set
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_BINFMT_MISC=y
+CONFIG_PAGE_REPORTING=y
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_IP_MULTICAST=y
+CONFIG_IP_ADVANCED_ROUTER=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_IP_PNP_BOOTP=y
+CONFIG_IP_PNP_RARP=y
+CONFIG_NETFILTER=y
+CONFIG_NETFILTER_NETLINK_ACCT=y
+CONFIG_NETFILTER_NETLINK_QUEUE=y
+CONFIG_NF_CONNTRACK=y
+CONFIG_NF_TABLES=y
+CONFIG_NFT_CT=y
+CONFIG_NFT_COMPAT=y
+CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y
+CONFIG_NETFILTER_XT_MATCH_IPCOMP=y
+CONFIG_NETFILTER_XT_MATCH_IPRANGE=y
+CONFIG_NETFILTER_XT_MATCH_MAC=y
+CONFIG_NETFILTER_XT_MATCH_MARK=y
+CONFIG_NETFILTER_XT_MATCH_MULTIPORT=y
+CONFIG_NETFILTER_XT_MATCH_SOCKET=y
+CONFIG_NETFILTER_XT_MATCH_STATE=y
+CONFIG_NETFILTER_XT_MATCH_STRING=y
+CONFIG_NETFILTER_XT_MATCH_U32=y
+CONFIG_NF_TABLES_IPV4=y
+CONFIG_NFT_DUP_IPV4=y
+CONFIG_NFT_FIB_IPV4=y
+CONFIG_IP_NF_IPTABLES=y
+CONFIG_IP_NF_FILTER=y
+CONFIG_IP_NF_TARGET_REJECT=y
+CONFIG_IP_NF_NAT=y
+CONFIG_IP_NF_TARGET_MASQUERADE=y
+CONFIG_IP_NF_TARGET_NETMAP=y
+CONFIG_IP_NF_TARGET_REDIRECT=y
+CONFIG_NETLINK_DIAG=y
+CONFIG_CAN=y
+CONFIG_BT=y
+CONFIG_BT_RFCOMM=y
+CONFIG_BT_RFCOMM_TTY=y
+CONFIG_BT_BNEP=y
+CONFIG_BT_BNEP_MC_FILTER=y
+CONFIG_BT_BNEP_PROTO_FILTER=y
+CONFIG_BT_AICUSB=y
+CONFIG_CFG80211=y
+CONFIG_MAC80211=y
+CONFIG_RFKILL=y
+CONFIG_NET_9P=y
+CONFIG_NET_9P_VIRTIO=y
+CONFIG_PCI=y
+# CONFIG_PCIEASPM is not set
+CONFIG_PCIE_STARFIVE_HOST=y
+CONFIG_DEVTMPFS=y
+CONFIG_DEVTMPFS_MOUNT=y
+CONFIG_MTD=y
+CONFIG_MTD_BLOCK=y
+CONFIG_MTD_CFI=y
+CONFIG_MTD_CFI_ADV_OPTIONS=y
+CONFIG_MTD_SPI_NOR=y
+CONFIG_OF_CONFIGFS=y
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_VIRTIO_BLK=y
+CONFIG_BLK_DEV_NVME=y
+CONFIG_EEPROM_AT24=y
+CONFIG_BLK_DEV_SD=y
+CONFIG_BLK_DEV_SR=y
+CONFIG_SCSI_VIRTIO=y
+CONFIG_ATA=y
+CONFIG_SATA_AHCI=y
+CONFIG_MD=y
+CONFIG_BLK_DEV_DM=m
+CONFIG_NETDEVICES=y
+CONFIG_VIRTIO_NET=y
+# CONFIG_NET_VENDOR_ALACRITECH is not set
+# CONFIG_NET_VENDOR_AMAZON is not set
+# CONFIG_NET_VENDOR_AQUANTIA is not set
+# CONFIG_NET_VENDOR_ARC is not set
+# CONFIG_NET_VENDOR_BROADCOM is not set
+# CONFIG_NET_VENDOR_CADENCE is not set
+# CONFIG_NET_VENDOR_CAVIUM is not set
+# CONFIG_NET_VENDOR_CORTINA is not set
+# CONFIG_NET_VENDOR_EZCHIP is not set
+# CONFIG_NET_VENDOR_GOOGLE is not set
+# CONFIG_NET_VENDOR_HUAWEI is not set
+# CONFIG_NET_VENDOR_INTEL is not set
+# CONFIG_NET_VENDOR_MARVELL is not set
+# CONFIG_NET_VENDOR_MELLANOX is not set
+# CONFIG_NET_VENDOR_MICREL is not set
+# CONFIG_NET_VENDOR_MICROCHIP is not set
+# CONFIG_NET_VENDOR_MICROSEMI is not set
+# CONFIG_NET_VENDOR_NI is not set
+# CONFIG_NET_VENDOR_NATSEMI is not set
+# CONFIG_NET_VENDOR_NETRONOME is not set
+# CONFIG_NET_VENDOR_PENSANDO is not set
+# CONFIG_NET_VENDOR_QUALCOMM is not set
+CONFIG_R8169=y
+# CONFIG_NET_VENDOR_RENESAS is not set
+# CONFIG_NET_VENDOR_ROCKER is not set
+# CONFIG_NET_VENDOR_SAMSUNG is not set
+# CONFIG_NET_VENDOR_SEEQ is not set
+# CONFIG_NET_VENDOR_SOLARFLARE is not set
+# CONFIG_NET_VENDOR_SOCIONEXT is not set
+CONFIG_STMMAC_ETH=y
+CONFIG_STMMAC_SELFTESTS=y
+CONFIG_DWMAC_DWC_QOS_ETH=y
+CONFIG_DWMAC_STARFIVE=y
+# CONFIG_NET_VENDOR_SYNOPSYS is not set
+# CONFIG_NET_VENDOR_VIA is not set
+# CONFIG_NET_VENDOR_WIZNET is not set
+# CONFIG_NET_VENDOR_XILINX is not set
+CONFIG_MARVELL_PHY=y
+CONFIG_MICREL_PHY=y
+CONFIG_MICROCHIP_PHY=y
+CONFIG_MOTORCOMM_PHY=y
+CONFIG_IPMS_CAN=y
+CONFIG_IWLWIFI=y
+CONFIG_IWLDVM=y
+CONFIG_IWLMVM=y
+CONFIG_HOSTAP=y
+# CONFIG_RTL_CARDS is not set
+CONFIG_USB_WIFI_ECR6600U=y
+CONFIG_AIC_WLAN_SUPPORT=y
+CONFIG_AIC8800_WLAN_SUPPORT=m
+CONFIG_AIC_LOADFW_SUPPORT=m
+CONFIG_INPUT_EVDEV=y
+# CONFIG_INPUT_KEYBOARD is not set
+# CONFIG_INPUT_MOUSE is not set
+CONFIG_INPUT_TOUCHSCREEN=y
+CONFIG_TOUCHSCREEN_GOODIX=y
+CONFIG_TOUCHSCREEN_TINKER_FT5406=y
+CONFIG_SERIO_LIBPS2=y
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_CONSOLE=y
+CONFIG_SERIAL_8250_NR_UARTS=6
+CONFIG_SERIAL_8250_RUNTIME_UARTS=6
+CONFIG_SERIAL_8250_EXTENDED=y
+CONFIG_SERIAL_8250_MANY_PORTS=y
+CONFIG_SERIAL_8250_DW=y
+CONFIG_SERIAL_OF_PLATFORM=y
+CONFIG_TTY_PRINTK=y
+CONFIG_VIRTIO_CONSOLE=y
+CONFIG_HW_RANDOM=y
+CONFIG_HW_RANDOM_JH7110=y
+CONFIG_I2C_CHARDEV=y
+CONFIG_I2C_DESIGNWARE_PLATFORM=y
+CONFIG_SPI=y
+CONFIG_SPI_CADENCE_QUADSPI=y
+CONFIG_SPI_PL022=y
+CONFIG_SPI_SIFIVE=y
+CONFIG_SPI_SPIDEV=y
+# CONFIG_PTP_1588_CLOCK is not set
+CONFIG_GPIO_SYSFS=y
+CONFIG_POWER_RESET=y
+CONFIG_POWER_RESET_GPIO_RESTART=y
+CONFIG_POWER_RESET_SYSCON=y
+CONFIG_POWER_RESET_SYSCON_POWEROFF=y
+CONFIG_SENSORS_SFCTEMP=y
+CONFIG_THERMAL=y
+CONFIG_THERMAL_WRITABLE_TRIPS=y
+CONFIG_CPU_THERMAL=y
+CONFIG_DEVFREQ_THERMAL=y
+CONFIG_THERMAL_EMULATION=y
+# CONFIG_HISI_THERMAL is not set
+CONFIG_WATCHDOG=y
+CONFIG_WATCHDOG_SYSFS=y
+CONFIG_MFD_AXP20X_I2C=y
+CONFIG_REGULATOR=y
+CONFIG_REGULATOR_AXP20X=y
+CONFIG_REGULATOR_GPIO=y
+CONFIG_REGULATOR_RASPBERRYPI_TOUCHSCREEN_ATTINY=y
+# CONFIG_MEDIA_CEC_SUPPORT is not set
+CONFIG_MEDIA_SUPPORT=y
+CONFIG_MEDIA_USB_SUPPORT=y
+CONFIG_USB_VIDEO_CLASS=y
+CONFIG_V4L_PLATFORM_DRIVERS=y
+CONFIG_V4L_MEM2MEM_DRIVERS=y
+CONFIG_VIDEO_CADENCE_CSI2RX=y
+CONFIG_VIDEO_WAVE_VPU=m
+CONFIG_VIN_SENSOR_OV4689=y
+CONFIG_VIN_SENSOR_IMX219=y
+CONFIG_VIDEO_STF_VIN=y
+CONFIG_VIDEO_IMX219=y
+CONFIG_VIDEO_IMX708=y
+# CONFIG_CXD2880_SPI_DRV is not set
+# CONFIG_MEDIA_TUNER_E4000 is not set
+# CONFIG_MEDIA_TUNER_FC0011 is not set
+# CONFIG_MEDIA_TUNER_FC0012 is not set
+# CONFIG_MEDIA_TUNER_FC0013 is not set
+# CONFIG_MEDIA_TUNER_FC2580 is not set
+# CONFIG_MEDIA_TUNER_IT913X is not set
+# CONFIG_MEDIA_TUNER_M88RS6000T is not set
+# CONFIG_MEDIA_TUNER_MAX2165 is not set
+# CONFIG_MEDIA_TUNER_MC44S803 is not set
+# CONFIG_MEDIA_TUNER_MSI001 is not set
+# CONFIG_MEDIA_TUNER_MT2060 is not set
+# CONFIG_MEDIA_TUNER_MT2063 is not set
+# CONFIG_MEDIA_TUNER_MT20XX is not set
+# CONFIG_MEDIA_TUNER_MT2131 is not set
+# CONFIG_MEDIA_TUNER_MT2266 is not set
+# CONFIG_MEDIA_TUNER_MXL301RF is not set
+# CONFIG_MEDIA_TUNER_MXL5005S is not set
+# CONFIG_MEDIA_TUNER_MXL5007T is not set
+# CONFIG_MEDIA_TUNER_QM1D1B0004 is not set
+# CONFIG_MEDIA_TUNER_QM1D1C0042 is not set
+# CONFIG_MEDIA_TUNER_QT1010 is not set
+# CONFIG_MEDIA_TUNER_R820T is not set
+# CONFIG_MEDIA_TUNER_SI2157 is not set
+# CONFIG_MEDIA_TUNER_SIMPLE is not set
+# CONFIG_MEDIA_TUNER_TDA18212 is not set
+# CONFIG_MEDIA_TUNER_TDA18218 is not set
+# CONFIG_MEDIA_TUNER_TDA18250 is not set
+# CONFIG_MEDIA_TUNER_TDA18271 is not set
+# CONFIG_MEDIA_TUNER_TDA827X is not set
+# CONFIG_MEDIA_TUNER_TDA8290 is not set
+# CONFIG_MEDIA_TUNER_TDA9887 is not set
+# CONFIG_MEDIA_TUNER_TEA5761 is not set
+# CONFIG_MEDIA_TUNER_TEA5767 is not set
+# CONFIG_MEDIA_TUNER_TUA9001 is not set
+# CONFIG_MEDIA_TUNER_XC2028 is not set
+# CONFIG_MEDIA_TUNER_XC4000 is not set
+# CONFIG_MEDIA_TUNER_XC5000 is not set
+# CONFIG_DVB_MXL5XX is not set
+# CONFIG_DVB_STB0899 is not set
+# CONFIG_DVB_STB6100 is not set
+# CONFIG_DVB_STV090x is not set
+# CONFIG_DVB_STV0910 is not set
+# CONFIG_DVB_STV6110x is not set
+# CONFIG_DVB_STV6111 is not set
+# CONFIG_DVB_DRXK is not set
+# CONFIG_DVB_MN88472 is not set
+# CONFIG_DVB_MN88473 is not set
+# CONFIG_DVB_SI2165 is not set
+# CONFIG_DVB_TDA18271C2DD is not set
+# CONFIG_DVB_CX24110 is not set
+# CONFIG_DVB_CX24116 is not set
+# CONFIG_DVB_CX24117 is not set
+# CONFIG_DVB_CX24120 is not set
+# CONFIG_DVB_CX24123 is not set
+# CONFIG_DVB_DS3000 is not set
+# CONFIG_DVB_MB86A16 is not set
+# CONFIG_DVB_MT312 is not set
+# CONFIG_DVB_S5H1420 is not set
+# CONFIG_DVB_SI21XX is not set
+# CONFIG_DVB_STB6000 is not set
+# CONFIG_DVB_STV0288 is not set
+# CONFIG_DVB_STV0299 is not set
+# CONFIG_DVB_STV0900 is not set
+# CONFIG_DVB_STV6110 is not set
+# CONFIG_DVB_TDA10071 is not set
+# CONFIG_DVB_TDA10086 is not set
+# CONFIG_DVB_TDA8083 is not set
+# CONFIG_DVB_TDA8261 is not set
+# CONFIG_DVB_TDA826X is not set
+# CONFIG_DVB_TS2020 is not set
+# CONFIG_DVB_TUA6100 is not set
+# CONFIG_DVB_TUNER_CX24113 is not set
+# CONFIG_DVB_TUNER_ITD1000 is not set
+# CONFIG_DVB_VES1X93 is not set
+# CONFIG_DVB_ZL10036 is not set
+# CONFIG_DVB_ZL10039 is not set
+# CONFIG_DVB_CX22700 is not set
+# CONFIG_DVB_CX22702 is not set
+# CONFIG_DVB_CXD2820R is not set
+# CONFIG_DVB_CXD2841ER is not set
+# CONFIG_DVB_DIB3000MB is not set
+# CONFIG_DVB_DIB3000MC is not set
+# CONFIG_DVB_DIB7000M is not set
+# CONFIG_DVB_DIB7000P is not set
+# CONFIG_DVB_DIB9000 is not set
+# CONFIG_DVB_DRXD is not set
+# CONFIG_DVB_EC100 is not set
+# CONFIG_DVB_L64781 is not set
+# CONFIG_DVB_MT352 is not set
+# CONFIG_DVB_NXT6000 is not set
+# CONFIG_DVB_S5H1432 is not set
+# CONFIG_DVB_SP887X is not set
+# CONFIG_DVB_STV0367 is not set
+# CONFIG_DVB_TDA10048 is not set
+# CONFIG_DVB_TDA1004X is not set
+# CONFIG_DVB_ZD1301_DEMOD is not set
+# CONFIG_DVB_ZL10353 is not set
+# CONFIG_DVB_CXD2880 is not set
+# CONFIG_DVB_STV0297 is not set
+# CONFIG_DVB_TDA10021 is not set
+# CONFIG_DVB_TDA10023 is not set
+# CONFIG_DVB_VES1820 is not set
+# CONFIG_DVB_AU8522_DTV is not set
+# CONFIG_DVB_AU8522_V4L is not set
+# CONFIG_DVB_BCM3510 is not set
+# CONFIG_DVB_LG2160 is not set
+# CONFIG_DVB_LGDT3305 is not set
+# CONFIG_DVB_LGDT330X is not set
+# CONFIG_DVB_MXL692 is not set
+# CONFIG_DVB_NXT200X is not set
+# CONFIG_DVB_OR51132 is not set
+# CONFIG_DVB_OR51211 is not set
+# CONFIG_DVB_S5H1409 is not set
+# CONFIG_DVB_S5H1411 is not set
+# CONFIG_DVB_DIB8000 is not set
+# CONFIG_DVB_MB86A20S is not set
+# CONFIG_DVB_S921 is not set
+# CONFIG_DVB_MN88443X is not set
+# CONFIG_DVB_TC90522 is not set
+# CONFIG_DVB_PLL is not set
+# CONFIG_DVB_TUNER_DIB0070 is not set
+# CONFIG_DVB_TUNER_DIB0090 is not set
+# CONFIG_DVB_A8293 is not set
+# CONFIG_DVB_AF9033 is not set
+# CONFIG_DVB_ASCOT2E is not set
+# CONFIG_DVB_ATBM8830 is not set
+# CONFIG_DVB_HELENE is not set
+# CONFIG_DVB_HORUS3A is not set
+# CONFIG_DVB_ISL6405 is not set
+# CONFIG_DVB_ISL6421 is not set
+# CONFIG_DVB_ISL6423 is not set
+# CONFIG_DVB_IX2505V is not set
+# CONFIG_DVB_LGS8GL5 is not set
+# CONFIG_DVB_LGS8GXX is not set
+# CONFIG_DVB_LNBH25 is not set
+# CONFIG_DVB_LNBH29 is not set
+# CONFIG_DVB_LNBP21 is not set
+# CONFIG_DVB_LNBP22 is not set
+# CONFIG_DVB_M88RS2000 is not set
+# CONFIG_DVB_TDA665x is not set
+# CONFIG_DVB_DRX39XYJ is not set
+# CONFIG_DVB_CXD2099 is not set
+# CONFIG_DVB_SP2 is not set
+CONFIG_DRM_PANEL_SIMPLE=y
+CONFIG_DRM_PANEL_STARFIVE_JADARD=y
+CONFIG_DRM_TOSHIBA_TC358762=y
+CONFIG_DRM_VERISILICON=y
+CONFIG_STARFIVE_INNO_HDMI=y
+CONFIG_STARFIVE_DSI=y
+CONFIG_DRM_IMG_ROGUE=y
+CONFIG_DRM_LEGACY=y
+CONFIG_FB=y
+CONFIG_BACKLIGHT_CLASS_DEVICE=y
+CONFIG_SOUND=y
+CONFIG_SND=y
+CONFIG_SND_USB_AUDIO=y
+CONFIG_SND_SOC=y
+CONFIG_SND_DESIGNWARE_I2S=y
+CONFIG_SND_SOC_RZ=m
+CONFIG_SND_SOC_STARFIVE=y
+CONFIG_SND_SOC_JH7110_PWMDAC=y
+CONFIG_SND_SOC_JH7110_TDM=y
+CONFIG_SND_SOC_AC108=y
+CONFIG_SND_SOC_WM8960=y
+CONFIG_SND_SIMPLE_CARD=y
+CONFIG_UHID=y
+CONFIG_USB=y
+CONFIG_USB_OTG=y
+CONFIG_USB_XHCI_HCD=y
+CONFIG_USB_RENESAS_USBHS=y
+CONFIG_USB_STORAGE=y
+CONFIG_USB_UAS=y
+CONFIG_USB_CDNS_SUPPORT=y
+CONFIG_USB_CDNS3=y
+CONFIG_USB_CDNS3_GADGET=y
+CONFIG_USB_CDNS3_HOST=y
+CONFIG_USB_CDNS3_STARFIVE=y
+CONFIG_USB_SERIAL=m
+CONFIG_USB_SERIAL_GENERIC=y
+CONFIG_USB_SERIAL_AIRCABLE=m
+CONFIG_USB_SERIAL_ARK3116=m
+CONFIG_USB_SERIAL_BELKIN=m
+CONFIG_USB_SERIAL_CH341=m
+CONFIG_USB_SERIAL_WHITEHEAT=m
+CONFIG_USB_SERIAL_DIGI_ACCELEPORT=m
+CONFIG_USB_SERIAL_CP210X=m
+CONFIG_USB_SERIAL_CYPRESS_M8=m
+CONFIG_USB_SERIAL_EMPEG=m
+CONFIG_USB_SERIAL_FTDI_SIO=m
+CONFIG_USB_SERIAL_VISOR=m
+CONFIG_USB_SERIAL_IPAQ=m
+CONFIG_USB_SERIAL_IR=m
+CONFIG_USB_SERIAL_EDGEPORT=m
+CONFIG_USB_SERIAL_EDGEPORT_TI=m
+CONFIG_USB_SERIAL_F81232=m
+CONFIG_USB_SERIAL_GARMIN=m
+CONFIG_USB_SERIAL_IPW=m
+CONFIG_USB_SERIAL_IUU=m
+CONFIG_USB_SERIAL_KEYSPAN_PDA=m
+CONFIG_USB_SERIAL_KEYSPAN=m
+CONFIG_USB_SERIAL_KLSI=m
+CONFIG_USB_SERIAL_KOBIL_SCT=m
+CONFIG_USB_SERIAL_MCT_U232=m
+CONFIG_USB_SERIAL_METRO=m
+CONFIG_USB_SERIAL_MOS7720=m
+CONFIG_USB_SERIAL_MOS7840=m
+CONFIG_USB_SERIAL_NAVMAN=m
+CONFIG_USB_SERIAL_PL2303=m
+CONFIG_USB_SERIAL_OTI6858=m
+CONFIG_USB_SERIAL_QCAUX=m
+CONFIG_USB_SERIAL_QUALCOMM=m
+CONFIG_USB_SERIAL_SPCP8X5=m
+CONFIG_USB_SERIAL_SAFE=m
+CONFIG_USB_SERIAL_SIERRAWIRELESS=m
+CONFIG_USB_SERIAL_SYMBOL=m
+CONFIG_USB_SERIAL_TI=m
+CONFIG_USB_SERIAL_CYBERJACK=m
+CONFIG_USB_SERIAL_OMNINET=m
+CONFIG_USB_SERIAL_OPTICON=m
+CONFIG_USB_SERIAL_XSENS_MT=m
+CONFIG_USB_SERIAL_WISHBONE=m
+CONFIG_USB_SERIAL_SSU100=m
+CONFIG_USB_SERIAL_DEBUG=m
+CONFIG_USB_GADGET=y
+CONFIG_USB_RENESAS_USBHS_UDC=m
+CONFIG_USB_CONFIGFS=y
+CONFIG_USB_CONFIGFS_SERIAL=y
+CONFIG_USB_CONFIGFS_ACM=y
+CONFIG_USB_CONFIGFS_OBEX=y
+CONFIG_USB_CONFIGFS_NCM=y
+CONFIG_USB_CONFIGFS_ECM=y
+CONFIG_USB_CONFIGFS_ECM_SUBSET=y
+CONFIG_USB_CONFIGFS_RNDIS=y
+CONFIG_USB_CONFIGFS_EEM=y
+CONFIG_USB_CONFIGFS_MASS_STORAGE=y
+CONFIG_USB_CONFIGFS_F_FS=y
+CONFIG_MMC=y
+CONFIG_MMC_DEBUG=y
+CONFIG_MMC_SDHCI=y
+CONFIG_MMC_SDHCI_PLTFM=y
+CONFIG_MMC_SDHCI_OF_DWCMSHC=y
+CONFIG_MMC_SPI=y
+CONFIG_MMC_SDHI=y
+CONFIG_MMC_DW=y
+CONFIG_MMC_DW_STARFIVE=y
+CONFIG_NEW_LEDS=y
+CONFIG_LEDS_CLASS=y
+CONFIG_LEDS_GPIO=y
+CONFIG_LEDS_TRIGGER_HEARTBEAT=y
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_DRV_STARFIVE=y
+CONFIG_RTC_DRV_GOLDFISH=y
+CONFIG_DMADEVICES=y
+CONFIG_AMBA_PL08X=y
+CONFIG_DW_AXI_DMAC=y
+CONFIG_DMATEST=y
+# CONFIG_VIRTIO_MENU is not set
+# CONFIG_VHOST_MENU is not set
+CONFIG_GOLDFISH=y
+# CONFIG_CLK_STARFIVE_JH7100_AUDIO is not set
+CONFIG_CLK_STARFIVE_JH7110_AON=y
+CONFIG_CLK_STARFIVE_JH7110_STG=y
+CONFIG_CLK_STARFIVE_JH7110_ISP=y
+CONFIG_CLK_STARFIVE_JH7110_VOUT=y
+CONFIG_MAILBOX=y
+CONFIG_STARFIVE_IPI_MBOX=y
+CONFIG_STARFIVE_MBOX=m
+CONFIG_STARFIVE_MBOX_TEST=m
+# CONFIG_IOMMU_SUPPORT is not set
+CONFIG_RPMSG_CHAR=y
+CONFIG_RPMSG_CTRL=y
+CONFIG_RPMSG_STARFIVE=m
+CONFIG_RPMSG_VIRTIO=y
+CONFIG_SIFIVE_CCACHE=y
+CONFIG_PM_DEVFREQ=y
+CONFIG_IIO=y
+CONFIG_IIO_ST_ACCEL_3AXIS=y
+CONFIG_PWM=y
+CONFIG_PWM_OCORES=y
+CONFIG_PHY_STARFIVE_JH7110_PCIE=y
+CONFIG_PHY_STARFIVE_JH7110_USB=y
+CONFIG_PHY_M31_DPHY_RX0=y
+CONFIG_RAS=y
+CONFIG_EXT4_FS=y
+CONFIG_EXT4_FS_POSIX_ACL=y
+CONFIG_BTRFS_FS=m
+CONFIG_BTRFS_FS_POSIX_ACL=y
+CONFIG_AUTOFS_FS=y
+CONFIG_FUSE_FS=y
+CONFIG_CUSE=y
+CONFIG_VIRTIO_FS=y
+CONFIG_FSCACHE=y
+CONFIG_FSCACHE_STATS=y
+CONFIG_MSDOS_FS=y
+CONFIG_VFAT_FS=y
+CONFIG_FAT_DEFAULT_UTF8=y
+CONFIG_EXFAT_FS=y
+CONFIG_NTFS_FS=y
+CONFIG_NTFS_RW=y
+CONFIG_TMPFS=y
+CONFIG_TMPFS_POSIX_ACL=y
+CONFIG_HUGETLBFS=y
+CONFIG_JFFS2_FS=y
+CONFIG_NFS_FS=y
+CONFIG_NFS_V4=y
+CONFIG_NFS_V4_1=y
+CONFIG_NFS_V4_2=y
+CONFIG_ROOT_NFS=y
+CONFIG_CIFS=m
+# CONFIG_CIFS_STATS2 is not set
+CONFIG_CIFS_UPCALL=y
+CONFIG_CIFS_XATTR=y
+CONFIG_CIFS_POSIX=y
+# CONFIG_CIFS_DEBUG is not set
+CONFIG_CIFS_DFS_UPCALL=y
+CONFIG_CIFS_FSCACHE=y
+CONFIG_SMB_SERVER=m
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_ISO8859_1=y
+CONFIG_INIT_STACK_NONE=y
+CONFIG_CRYPTO_USER=y
+CONFIG_CRYPTO_TEST=m
+CONFIG_CRYPTO_USER_API_HASH=y
+CONFIG_CRYPTO_USER_API_SKCIPHER=y
+CONFIG_CRYPTO_USER_API_RNG=y
+CONFIG_CRYPTO_USER_API_AEAD=y
+CONFIG_CRYPTO_DEV_VIRTIO=y
+CONFIG_CRYPTO_DEV_JH7110=y
+CONFIG_PRINTK_TIME=y
+CONFIG_DEBUG_FS=y
+CONFIG_SOFTLOCKUP_DETECTOR=y
+CONFIG_WQ_WATCHDOG=y
+CONFIG_DEBUG_TIMEKEEPING=y
+CONFIG_DEBUG_RT_MUTEXES=y
+CONFIG_DEBUG_SPINLOCK=y
+CONFIG_DEBUG_RWSEMS=y
+CONFIG_DEBUG_LIST=y
+CONFIG_DEBUG_PLIST=y
+CONFIG_DEBUG_SG=y
+# CONFIG_RCU_TRACE is not set
+CONFIG_RCU_EQS_DEBUG=y
+# CONFIG_FTRACE is not set
+# CONFIG_RUNTIME_TESTING_MENU is not set
+CONFIG_MEMTEST=y
diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig
index bc211c324206..85b600f74964 100644
--- a/drivers/bluetooth/Kconfig
+++ b/drivers/bluetooth/Kconfig
@@ -478,5 +478,9 @@ config BT_NXPUART
Say Y here to compile support for NXP Bluetooth UART device into
the kernel, or say M here to compile as a module (btnxpuart).
+config BT_AICUSB
+ tristate "AIC8800 btusb driver"
+ help
+ AIC8800 usb dongle bluetooth support driver
endmenu
diff --git a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile
index 7a5967e9ac48..06b0d837ed6d 100644
--- a/drivers/bluetooth/Makefile
+++ b/drivers/bluetooth/Makefile
@@ -51,3 +51,4 @@ hci_uart-$(CONFIG_BT_HCIUART_QCA) += hci_qca.o
hci_uart-$(CONFIG_BT_HCIUART_AG6XX) += hci_ag6xx.o
hci_uart-$(CONFIG_BT_HCIUART_MRVL) += hci_mrvl.o
hci_uart-objs := $(hci_uart-y)
+obj-$(CONFIG_BT_AICUSB) += aic_btusb/
diff --git a/drivers/bluetooth/aic_btusb/Makefile b/drivers/bluetooth/aic_btusb/Makefile
new file mode 100755
index 000000000000..55477fab6f5c
--- /dev/null
+++ b/drivers/bluetooth/aic_btusb/Makefile
@@ -0,0 +1,80 @@
+MODULE_NAME = aic_btusb
+
+CONFIG_AIC8800_BTUSB_SUPPORT = m
+CONFIG_SUPPORT_VENDOR_APCF = n
+# Need to set fw path in BOARD_KERNEL_CMDLINE
+CONFIG_USE_FW_REQUEST = n
+
+ifeq ($(CONFIG_SUPPORT_VENDOR_APCF), y)
+obj-$(CONFIG_AIC8800_BTUSB_SUPPORT) := $(MODULE_NAME).o aic_btusb_external_featrue.o
+else
+obj-$(CONFIG_AIC8800_BTUSB_SUPPORT) := $(MODULE_NAME).o
+endif
+
+ccflags-$(CONFIG_SUPPORT_VENDOR_APCF) += -DCONFIG_SUPPORT_VENDOR_APCF
+#$(MODULE_NAME)-y := aic_btusb_ioctl.o\
+# aic_btusb.o \
+
+ccflags-$(CONFIG_USE_FW_REQUEST) += -DCONFIG_USE_FW_REQUEST
+
+
+# Platform support list
+CONFIG_PLATFORM_ROCKCHIP ?= n
+CONFIG_PLATFORM_ALLWINNER ?= n
+CONFIG_PLATFORM_AMLOGIC ?= n
+CONFIG_PLATFORM_UBUNTU ?= y
+CONFIG_PLATFORM_ING ?= n
+
+ifeq ($(CONFIG_PLATFORM_ING), y)
+ARCH := mips
+KDIR ?= /home/yaya/E/Ingenic/T31/Ingenic-SDK-T31-1.1.5-20220428/opensource/kernel
+CROSS_COMPILE := /home/yaya/E/Ingenic/T31/Ingenic-SDK-T31-1.1.5-20220428/resource/toolchain/gcc_472/mips-gcc472-glibc216-32bit/bin/mips-linux-gnu-
+endif
+
+ifeq ($(CONFIG_PLATFORM_ROCKCHIP), y)
+ccflags-$(CONFIG_PLATFORM_ROCKCHIP) += -DCONFIG_PLATFORM_ROCKCHIP
+#KDIR := /home/yaya/E/Rockchip/3229/Android9/rk3229_android9.0_box/kernel
+#ARCH ?= arm
+#CROSS_COMPILE ?= /home/yaya/E/Rockchip/3229/Android9/rk3229_android9.0_box/prebuilts/gcc/linux-x86/arm/gcc-linaro-6.3.1-2017.05-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf-
+#KDIR := /home/yaya/E/Rockchip/3229/Android7/RK3229_ANDROID7.1_v1.01_20170914/rk3229_Android7.1_v1.01_xml0914/kernel
+#ARCH ?= arm
+#CROSS_COMPILE ?= /home/yaya/E/Rockchip/3229/Android7/RK3229_ANDROID7.1_v1.01_20170914/rk3229_Android7.1_v1.01_xml0914/prebuilts/gcc/linux-x86/arm/arm-eabi-4.6/bin/arm-eabi-
+ARCH := arm64
+KDIR ?= /home/yaya/E/Rockchip/3566/firefly/Android11.0/Firefly-RK356X_Android11.0_git_20210824/RK356X_Android11.0/kernel
+CROSS_COMPILE := /home/yaya/E/Rockchip/3566/firefly/Android11.0/Firefly-RK356X_Android11.0_git_20210824/RK356X_Android11.0/prebuilts/gcc/linux-x86/aarch64/gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-
+
+endif
+
+ifeq ($(CONFIG_PLATFORM_ALLWINNER), y)
+ccflags-$(CONFIG_PLATFORM_ALLWINNER) += -DCONFIG_PLATFORM_ALLWINNER
+KDIR := /home/yaya/E/Allwinner/R818/R818/AndroidQ/lichee/kernel/linux-4.9
+ARCH ?= arm64
+CROSS_COMPILE ?= /home/yaya/E/Allwinner/R818/R818/AndroidQ/lichee/out/gcc-linaro-5.3.1-2016.05-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-
+endif
+
+ifeq ($(CONFIG_PLATFORM_AMLOGIC), y)
+ ccflags-$(CONFIG_PLATFORM_AMLOGIC) += -DCONFIG_PLATFORM_AMLOGIC
+endif
+
+ifeq ($(CONFIG_PLATFORM_UBUNTU), y)
+ccflags-$(CONFIG_PLATFORM_UBUNTU) += -DCONFIG_PLATFORM_UBUNTU
+endif
+
+all: modules
+modules:
+ make -C $(KDIR) M=$(PWD) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) modules
+
+install:
+ mkdir -p $(MODDESTDIR)
+ install -p -m 644 $(MODULE_NAME).ko $(MODDESTDIR)/
+ /sbin/depmod -a ${KVER}
+ echo $(MODULE_NAME) >> /etc/modules-load.d/aic_bt.conf
+
+uninstall:
+ rm -rfv $(MODDESTDIR)/$(MODULE_NAME).ko
+ /sbin/depmod -a ${KVER}
+ rm -f /etc/modules-load.d/aic_bt.conf
+
+clean:
+ rm -rf *.o *.ko *.o.* *.mod.* modules.* Module.* .a* .o* .*.o.* *.mod .tmp* .cache.mk .Module.symvers.cmd .modules.order.cmd
+
diff --git a/drivers/bluetooth/aic_btusb/aic_btusb.c b/drivers/bluetooth/aic_btusb/aic_btusb.c
new file mode 100644
index 000000000000..7e6dcb408a16
--- /dev/null
+++ b/drivers/bluetooth/aic_btusb/aic_btusb.c
@@ -0,0 +1,5031 @@
+/*
+ *
+ * AicSemi Bluetooth USB driver
+ *
+ *
+ *
+ * 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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/skbuff.h>
+#include <linux/usb.h>
+
+#include <linux/ioctl.h>
+#include <linux/io.h>
+#include <linux/firmware.h>
+#include <linux/vmalloc.h>
+#include <linux/fs.h>
+#include <linux/uaccess.h>
+#include <linux/reboot.h>
+
+#include "aic_btusb.h"
+
+#ifdef CONFIG_USE_FW_REQUEST
+#include <linux/firmware.h>
+#endif
+
+#define AICBT_RELEASE_NAME "202012_ANDROID"
+#define VERSION "2.1.0"
+
+#define SUSPNED_DW_FW 0
+
+
+static spinlock_t queue_lock;
+static spinlock_t dlfw_lock;
+static volatile uint16_t dlfw_dis_state = 0;
+
+/* USB Device ID */
+#define USB_VENDOR_ID_AIC 0xA69C
+#define USB_PRODUCT_ID_AIC8801 0x8801
+#define USB_PRODUCT_ID_AIC8800DC 0x88dc
+#define USB_PRODUCT_ID_AIC8800D80 0x8d81
+
+enum AICWF_IC{
+ PRODUCT_ID_AIC8801 = 0,
+ PRODUCT_ID_AIC8800DC,
+ PRODUCT_ID_AIC8800DW,
+ PRODUCT_ID_AIC8800D80
+};
+
+u16 g_chipid = PRODUCT_ID_AIC8801;
+u8 chip_id = 0;
+u8 sub_chip_id = 0;
+
+struct btusb_data {
+ struct hci_dev *hdev;
+ struct usb_device *udev;
+ struct usb_interface *intf;
+ struct usb_interface *isoc;
+
+ spinlock_t lock;
+
+ unsigned long flags;
+
+ struct work_struct work;
+ struct work_struct waker;
+
+ struct usb_anchor tx_anchor;
+ struct usb_anchor intr_anchor;
+ struct usb_anchor bulk_anchor;
+ struct usb_anchor isoc_anchor;
+ struct usb_anchor deferred;
+ int tx_in_flight;
+ spinlock_t txlock;
+
+#if (CONFIG_BLUEDROID == 0)
+#if HCI_VERSION_CODE >= KERNEL_VERSION(3, 18, 0)
+ spinlock_t rxlock;
+ struct sk_buff *evt_skb;
+ struct sk_buff *acl_skb;
+ struct sk_buff *sco_skb;
+#endif
+#endif
+
+ struct usb_endpoint_descriptor *intr_ep;
+ struct usb_endpoint_descriptor *bulk_tx_ep;
+ struct usb_endpoint_descriptor *bulk_rx_ep;
+ struct usb_endpoint_descriptor *isoc_tx_ep;
+ struct usb_endpoint_descriptor *isoc_rx_ep;
+
+ __u8 cmdreq_type;
+
+ unsigned int sco_num;
+ int isoc_altsetting;
+ int suspend_count;
+ uint16_t sco_handle;
+
+#if (CONFIG_BLUEDROID == 0)
+#if HCI_VERSION_CODE >= KERNEL_VERSION(3, 18, 0)
+ int (*recv_bulk) (struct btusb_data * data, void *buffer, int count);
+#endif
+#endif
+
+//#ifdef CONFIG_HAS_EARLYSUSPEND
+#if 0
+ struct early_suspend early_suspend;
+#else
+ struct notifier_block pm_notifier;
+ struct notifier_block reboot_notifier;
+#endif
+ firmware_info *fw_info;
+
+#ifdef CONFIG_SCO_OVER_HCI
+ AIC_sco_card_t *pSCOSnd;
+#endif
+};
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 7, 1)
+static bool reset_on_close = 0;
+#endif
+
+#ifdef CONFIG_SCO_OVER_HCI
+struct snd_sco_cap_timer {
+ struct timer_list cap_timer;
+ struct timer_list play_timer;
+ struct btusb_data snd_usb_data;
+ int snd_sco_length;
+};
+static struct snd_sco_cap_timer snd_cap_timer;
+#endif
+
+
+int bt_support = 0;
+module_param(bt_support, int, 0660);
+
+#ifdef CONFIG_SUPPORT_VENDOR_APCF
+int vendor_apcf_sent_done = 0;
+#endif
+
+static inline int check_set_dlfw_state_value(uint16_t change_value)
+{
+ spin_lock(&dlfw_lock);
+ if(!dlfw_dis_state) {
+ dlfw_dis_state = change_value;
+ }
+ spin_unlock(&dlfw_lock);
+ return dlfw_dis_state;
+}
+
+static inline void set_dlfw_state_value(uint16_t change_value)
+{
+ spin_lock(&dlfw_lock);
+ dlfw_dis_state = change_value;
+ spin_unlock(&dlfw_lock);
+}
+
+
+
+
+static void aic_free( struct btusb_data *data)
+{
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 7, 1)
+ kfree(data);
+#endif
+ return;
+}
+
+static struct btusb_data *aic_alloc(struct usb_interface *intf)
+{
+ struct btusb_data *data;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 7, 1)
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+#else
+ data = devm_kzalloc(&intf->dev, sizeof(*data), GFP_KERNEL);
+#endif
+ return data;
+}
+
+static void print_acl(struct sk_buff *skb, int direction)
+{
+#if PRINT_ACL_DATA
+ //uint wlength = skb->len;
+ u16 *handle = (u16 *)(skb->data);
+ u16 len = *(handle+1);
+ //u8 *acl_data = (u8 *)(skb->data);
+
+ AICBT_INFO("aic %s: direction %d, handle %04x, len %d",
+ __func__, direction, *handle, len);
+#endif
+}
+
+static void print_sco(struct sk_buff *skb, int direction)
+{
+#if PRINT_SCO_DATA
+ uint wlength = skb->len;
+ u16 *handle = (u16 *)(skb->data);
+ u8 len = *(u8 *)(handle+1);
+ //u8 *sco_data =(u8 *)(skb->data);
+
+ AICBT_INFO("aic %s: direction %d, handle %04x, len %d,wlength %d",
+ __func__, direction, *handle, len,wlength);
+#endif
+}
+
+static void print_error_command(struct sk_buff *skb)
+{
+ u16 *opcode = (u16*)(skb->data);
+ u8 *cmd_data = (u8*)(skb->data);
+ u8 len = *(cmd_data+2);
+
+ printk(" 0x%04x,len:%d,", *opcode, len);
+#if CONFIG_BLUEDROID
+ switch (*opcode) {
+ case HCI_OP_INQUIRY:
+ printk("HCI_OP_INQUIRY");
+ break;
+ case HCI_OP_INQUIRY_CANCEL:
+ printk("HCI_OP_INQUIRY_CANCEL");
+ break;
+ case HCI_OP_EXIT_PERIODIC_INQ:
+ printk("HCI_OP_EXIT_PERIODIC_INQ");
+ break;
+ case HCI_OP_CREATE_CONN:
+ printk("HCI_OP_CREATE_CONN");
+ break;
+ case HCI_OP_DISCONNECT:
+ printk("HCI_OP_DISCONNECT");
+ break;
+ case HCI_OP_CREATE_CONN_CANCEL:
+ printk("HCI_OP_CREATE_CONN_CANCEL");
+ break;
+ case HCI_OP_ACCEPT_CONN_REQ:
+ printk("HCI_OP_ACCEPT_CONN_REQ");
+ break;
+ case HCI_OP_REJECT_CONN_REQ:
+ printk("HCI_OP_REJECT_CONN_REQ");
+ break;
+ case HCI_OP_AUTH_REQUESTED:
+ printk("HCI_OP_AUTH_REQUESTED");
+ break;
+ case HCI_OP_SET_CONN_ENCRYPT:
+ printk("HCI_OP_SET_CONN_ENCRYPT");
+ break;
+ case HCI_OP_REMOTE_NAME_REQ:
+ printk("HCI_OP_REMOTE_NAME_REQ");
+ break;
+ case HCI_OP_READ_REMOTE_FEATURES:
+ printk("HCI_OP_READ_REMOTE_FEATURES");
+ break;
+ case HCI_OP_SNIFF_MODE:
+ printk("HCI_OP_SNIFF_MODE");
+ break;
+ case HCI_OP_EXIT_SNIFF_MODE:
+ printk("HCI_OP_EXIT_SNIFF_MODE");
+ break;
+ case HCI_OP_SWITCH_ROLE:
+ printk("HCI_OP_SWITCH_ROLE");
+ break;
+ case HCI_OP_SNIFF_SUBRATE:
+ printk("HCI_OP_SNIFF_SUBRATE");
+ break;
+ case HCI_OP_RESET:
+ printk("HCI_OP_RESET");
+ break;
+ case HCI_OP_Write_Extended_Inquiry_Response:
+ printk("HCI_Write_Extended_Inquiry_Response");
+ break;
+ case HCI_OP_Write_Simple_Pairing_Mode:
+ printk("HCI_OP_Write_Simple_Pairing_Mode");
+ break;
+ case HCI_OP_Read_Buffer_Size:
+ printk("HCI_OP_Read_Buffer_Size");
+ break;
+ case HCI_OP_Host_Buffer_Size:
+ printk("HCI_OP_Host_Buffer_Size");
+ break;
+ case HCI_OP_Read_Local_Version_Information:
+ printk("HCI_OP_Read_Local_Version_Information");
+ break;
+ case HCI_OP_Read_BD_ADDR:
+ printk("HCI_OP_Read_BD_ADDR");
+ break;
+ case HCI_OP_Read_Local_Supported_Commands:
+ printk("HCI_OP_Read_Local_Supported_Commands");
+ break;
+ case HCI_OP_Write_Scan_Enable:
+ printk("HCI_OP_Write_Scan_Enable");
+ break;
+ case HCI_OP_Write_Current_IAC_LAP:
+ printk("HCI_OP_Write_Current_IAC_LAP");
+ break;
+ case HCI_OP_Write_Inquiry_Scan_Activity:
+ printk("HCI_OP_Write_Inquiry_Scan_Activity");
+ break;
+ case HCI_OP_Write_Class_of_Device:
+ printk("HCI_OP_Write_Class_of_Device");
+ break;
+ case HCI_OP_LE_Rand:
+ printk("HCI_OP_LE_Rand");
+ break;
+ case HCI_OP_LE_Set_Random_Address:
+ printk("HCI_OP_LE_Set_Random_Address");
+ break;
+ case HCI_OP_LE_Set_Extended_Scan_Enable:
+ printk("HCI_OP_LE_Set_Extended_Scan_Enable");
+ break;
+ case HCI_OP_LE_Set_Extended_Scan_Parameters:
+ printk("HCI_OP_LE_Set_Extended_Scan_Parameters");
+ break;
+ case HCI_OP_Set_Event_Filter:
+ printk("HCI_OP_Set_Event_Filter");
+ break;
+ case HCI_OP_Write_Voice_Setting:
+ printk("HCI_OP_Write_Voice_Setting");
+ break;
+ case HCI_OP_Change_Local_Name:
+ printk("HCI_OP_Change_Local_Name");
+ break;
+ case HCI_OP_Read_Local_Name:
+ printk("HCI_OP_Read_Local_Name");
+ break;
+ case HCI_OP_Wirte_Page_Timeout:
+ printk("HCI_OP_Wirte_Page_Timeout");
+ break;
+ case HCI_OP_LE_Clear_Resolving_List:
+ printk("HCI_OP_LE_Clear_Resolving_List");
+ break;
+ case HCI_OP_LE_Set_Addres_Resolution_Enable_Command:
+ printk("HCI_OP_LE_Set_Addres_Resolution_Enable_Command");
+ break;
+ case HCI_OP_Write_Inquiry_mode:
+ printk("HCI_OP_Write_Inquiry_mode");
+ break;
+ case HCI_OP_Write_Page_Scan_Type:
+ printk("HCI_OP_Write_Page_Scan_Type");
+ break;
+ case HCI_OP_Write_Inquiry_Scan_Type:
+ printk("HCI_OP_Write_Inquiry_Scan_Type");
+ break;
+ case HCI_OP_Delete_Stored_Link_Key:
+ printk("HCI_OP_Delete_Stored_Link_Key");
+ break;
+ case HCI_OP_LE_Read_Local_Resolvable_Address:
+ printk("HCI_OP_LE_Read_Local_Resolvable_Address");
+ break;
+ case HCI_OP_LE_Extended_Create_Connection:
+ printk("HCI_OP_LE_Extended_Create_Connection");
+ break;
+ case HCI_OP_Read_Remote_Version_Information:
+ printk("HCI_OP_Read_Remote_Version_Information");
+ break;
+ case HCI_OP_LE_Start_Encryption:
+ printk("HCI_OP_LE_Start_Encryption");
+ break;
+ case HCI_OP_LE_Add_Device_to_Resolving_List:
+ printk("HCI_OP_LE_Add_Device_to_Resolving_List");
+ break;
+ case HCI_OP_LE_Set_Privacy_Mode:
+ printk("HCI_OP_LE_Set_Privacy_Mode");
+ break;
+ case HCI_OP_LE_Connection_Update:
+ printk("HCI_OP_LE_Connection_Update");
+ break;
+ default:
+ printk("UNKNOW_HCI_COMMAND");
+ break;
+ }
+#endif //CONFIG_BLUEDROID
+}
+
+static void print_command(struct sk_buff *skb)
+{
+#if PRINT_CMD_EVENT
+ print_error_command(skb);
+#endif
+}
+
+
+enum CODEC_TYPE{
+ CODEC_CVSD,
+ CODEC_MSBC,
+};
+
+static enum CODEC_TYPE codec_type = CODEC_CVSD;
+static void set_select_msbc(enum CODEC_TYPE type);
+static enum CODEC_TYPE check_select_msbc(void);
+
+
+#if CONFIG_BLUEDROID
+
+/* Global parameters for bt usb char driver */
+#define BT_CHAR_DEVICE_NAME "aicbt_dev"
+struct mutex btchr_mutex;
+static struct sk_buff_head btchr_readq;
+static wait_queue_head_t btchr_read_wait;
+static wait_queue_head_t bt_dlfw_wait;
+static int bt_char_dev_registered;
+static dev_t bt_devid; /* bt char device number */
+static struct cdev bt_char_dev; /* bt character device structure */
+static struct class *bt_char_class; /* device class for usb char driver */
+static int bt_reset = 0;
+
+/* HCI device & lock */
+DEFINE_RWLOCK(hci_dev_lock);
+struct hci_dev *ghdev = NULL;
+
+#ifdef CONFIG_SUPPORT_VENDOR_APCF
+static int bypass_event(struct sk_buff *skb)
+{
+ int ret = 0;
+ u8 *opcode = (u8*)(skb->data);
+ //u8 len = *(opcode+1);
+ u16 sub_opcpde;
+
+ switch(*opcode) {
+ case HCI_EV_CMD_COMPLETE:
+ sub_opcpde = ((u16)opcode[3]|(u16)(opcode[4])<<8);
+ if(sub_opcpde == 0xfd57){
+ if(vendor_apcf_sent_done){
+ vendor_apcf_sent_done--;
+ printk("apcf bypass\r\n");
+ ret = 1;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ return ret;
+}
+#endif//CONFIG_SUPPORT_VENDOR_APCF
+static void print_event(struct sk_buff *skb)
+{
+#if PRINT_CMD_EVENT
+ //uint wlength = skb->len;
+ //uint icount = 0;
+ u8 *opcode = (u8*)(skb->data);
+ //u8 len = *(opcode+1);
+
+ printk("aic %s ", __func__);
+ switch (*opcode) {
+ case HCI_EV_INQUIRY_COMPLETE:
+ printk("HCI_EV_INQUIRY_COMPLETE");
+ break;
+ case HCI_EV_INQUIRY_RESULT:
+ printk("HCI_EV_INQUIRY_RESULT");
+ break;
+ case HCI_EV_CONN_COMPLETE:
+ printk("HCI_EV_CONN_COMPLETE");
+ break;
+ case HCI_EV_CONN_REQUEST:
+ printk("HCI_EV_CONN_REQUEST");
+ break;
+ case HCI_EV_DISCONN_COMPLETE:
+ printk("HCI_EV_DISCONN_COMPLETE");
+ break;
+ case HCI_EV_AUTH_COMPLETE:
+ printk("HCI_EV_AUTH_COMPLETE");
+ break;
+ case HCI_EV_REMOTE_NAME:
+ printk("HCI_EV_REMOTE_NAME");
+ break;
+ case HCI_EV_ENCRYPT_CHANGE:
+ printk("HCI_EV_ENCRYPT_CHANGE");
+ break;
+ case HCI_EV_CHANGE_LINK_KEY_COMPLETE:
+ printk("HCI_EV_CHANGE_LINK_KEY_COMPLETE");
+ break;
+ case HCI_EV_REMOTE_FEATURES:
+ printk("HCI_EV_REMOTE_FEATURES");
+ break;
+ case HCI_EV_REMOTE_VERSION:
+ printk("HCI_EV_REMOTE_VERSION");
+ break;
+ case HCI_EV_QOS_SETUP_COMPLETE:
+ printk("HCI_EV_QOS_SETUP_COMPLETE");
+ break;
+ case HCI_EV_CMD_COMPLETE:
+ printk("HCI_EV_CMD_COMPLETE");
+ break;
+ case HCI_EV_CMD_STATUS:
+ printk("HCI_EV_CMD_STATUS");
+ break;
+ case HCI_EV_ROLE_CHANGE:
+ printk("HCI_EV_ROLE_CHANGE");
+ break;
+ case HCI_EV_NUM_COMP_PKTS:
+ printk("HCI_EV_NUM_COMP_PKTS");
+ break;
+ case HCI_EV_MODE_CHANGE:
+ printk("HCI_EV_MODE_CHANGE");
+ break;
+ case HCI_EV_PIN_CODE_REQ:
+ printk("HCI_EV_PIN_CODE_REQ");
+ break;
+ case HCI_EV_LINK_KEY_REQ:
+ printk("HCI_EV_LINK_KEY_REQ");
+ break;
+ case HCI_EV_LINK_KEY_NOTIFY:
+ printk("HCI_EV_LINK_KEY_NOTIFY");
+ break;
+ case HCI_EV_CLOCK_OFFSET:
+ printk("HCI_EV_CLOCK_OFFSET");
+ break;
+ case HCI_EV_PKT_TYPE_CHANGE:
+ printk("HCI_EV_PKT_TYPE_CHANGE");
+ break;
+ case HCI_EV_PSCAN_REP_MODE:
+ printk("HCI_EV_PSCAN_REP_MODE");
+ break;
+ case HCI_EV_INQUIRY_RESULT_WITH_RSSI:
+ printk("HCI_EV_INQUIRY_RESULT_WITH_RSSI");
+ break;
+ case HCI_EV_REMOTE_EXT_FEATURES:
+ printk("HCI_EV_REMOTE_EXT_FEATURES");
+ break;
+ case HCI_EV_SYNC_CONN_COMPLETE:
+ printk("HCI_EV_SYNC_CONN_COMPLETE");
+ break;
+ case HCI_EV_SYNC_CONN_CHANGED:
+ printk("HCI_EV_SYNC_CONN_CHANGED");
+ break;
+ case HCI_EV_SNIFF_SUBRATE:
+ printk("HCI_EV_SNIFF_SUBRATE");
+ break;
+ case HCI_EV_EXTENDED_INQUIRY_RESULT:
+ printk("HCI_EV_EXTENDED_INQUIRY_RESULT");
+ break;
+ case HCI_EV_IO_CAPA_REQUEST:
+ printk("HCI_EV_IO_CAPA_REQUEST");
+ break;
+ case HCI_EV_SIMPLE_PAIR_COMPLETE:
+ printk("HCI_EV_SIMPLE_PAIR_COMPLETE");
+ break;
+ case HCI_EV_REMOTE_HOST_FEATURES:
+ printk("HCI_EV_REMOTE_HOST_FEATURES");
+ break;
+ default:
+ printk("unknow event");
+ break;
+ }
+ printk("\n");
+#if 0
+ printk("%02x,len:%d,", *opcode,len);
+ for (icount = 2; (icount < wlength) && (icount < 24); icount++)
+ printk("%02x ", *(opcode+icount));
+ printk("\n");
+#endif
+#endif
+}
+
+static inline ssize_t usb_put_user(struct sk_buff *skb,
+ char __user *buf, int count)
+{
+ char __user *ptr = buf;
+ int len = min_t(unsigned int, skb->len, count);
+
+ if (copy_to_user(ptr, skb->data, len))
+ return -EFAULT;
+
+ return len;
+}
+
+static struct sk_buff *aic_skb_queue[QUEUE_SIZE];
+static int aic_skb_queue_front = 0;
+static int aic_skb_queue_rear = 0;
+
+static void aic_enqueue(struct sk_buff *skb)
+{
+ spin_lock(&queue_lock);
+ if (aic_skb_queue_front == (aic_skb_queue_rear + 1) % QUEUE_SIZE) {
+ /*
+ * If queue is full, current solution is to drop
+ * the following entries.
+ */
+ AICBT_WARN("%s: Queue is full, entry will be dropped", __func__);
+ } else {
+ aic_skb_queue[aic_skb_queue_rear] = skb;
+
+ aic_skb_queue_rear++;
+ aic_skb_queue_rear %= QUEUE_SIZE;
+
+ }
+ spin_unlock(&queue_lock);
+}
+
+static struct sk_buff *aic_dequeue_try(unsigned int deq_len)
+{
+ struct sk_buff *skb;
+ struct sk_buff *skb_copy;
+
+ if (aic_skb_queue_front == aic_skb_queue_rear) {
+ AICBT_WARN("%s: Queue is empty", __func__);
+ return NULL;
+ }
+
+ skb = aic_skb_queue[aic_skb_queue_front];
+ if (deq_len >= skb->len) {
+
+ aic_skb_queue_front++;
+ aic_skb_queue_front %= QUEUE_SIZE;
+
+ /*
+ * Return skb addr to be dequeued, and the caller
+ * should free the skb eventually.
+ */
+ return skb;
+ } else {
+ skb_copy = pskb_copy(skb, GFP_ATOMIC);
+ skb_pull(skb, deq_len);
+ /* Return its copy to be freed */
+ return skb_copy;
+ }
+}
+
+static inline int is_queue_empty(void)
+{
+ return (aic_skb_queue_front == aic_skb_queue_rear) ? 1 : 0;
+}
+
+static void aic_clear_queue(void)
+{
+ struct sk_buff *skb;
+ spin_lock(&queue_lock);
+ while(!is_queue_empty()) {
+ skb = aic_skb_queue[aic_skb_queue_front];
+ aic_skb_queue[aic_skb_queue_front] = NULL;
+ aic_skb_queue_front++;
+ aic_skb_queue_front %= QUEUE_SIZE;
+ if (skb) {
+ kfree_skb(skb);
+ }
+ }
+ spin_unlock(&queue_lock);
+}
+
+/*
+ * AicSemi - Integrate from hci_core.c
+ */
+
+/* Get HCI device by index.
+ * Device is held on return. */
+static struct hci_dev *hci_dev_get(int index)
+{
+ if (index != 0)
+ return NULL;
+
+ return ghdev;
+}
+
+/* ---- HCI ioctl helpers ---- */
+static int hci_dev_open(__u16 dev)
+{
+ struct hci_dev *hdev;
+ int ret = 0;
+
+ AICBT_DBG("%s: dev %d", __func__, dev);
+
+ hdev = hci_dev_get(dev);
+ if (!hdev) {
+ AICBT_ERR("%s: Failed to get hci dev[Null]", __func__);
+ return -ENODEV;
+ }
+
+ if (test_bit(HCI_UNREGISTER, &hdev->dev_flags)) {
+ ret = -ENODEV;
+ goto done;
+ }
+
+ if (test_bit(HCI_UP, &hdev->flags)) {
+ ret = -EALREADY;
+ goto done;
+ }
+
+done:
+ return ret;
+}
+
+static int hci_dev_do_close(struct hci_dev *hdev)
+{
+ if (hdev->flush)
+ hdev->flush(hdev);
+ /* After this point our queues are empty
+ * and no tasks are scheduled. */
+ hdev->close(hdev);
+ /* Clear flags */
+ hdev->flags = 0;
+ return 0;
+}
+
+static int hci_dev_close(__u16 dev)
+{
+ struct hci_dev *hdev;
+ int err;
+ hdev = hci_dev_get(dev);
+ if (!hdev) {
+ AICBT_ERR("%s: failed to get hci dev[Null]", __func__);
+ return -ENODEV;
+ }
+
+ err = hci_dev_do_close(hdev);
+
+ return err;
+}
+
+#ifdef CONFIG_SCO_OVER_HCI
+/* copy data from the URB buffer into the ALSA ring buffer */
+static bool aic_copy_capture_data_to_alsa(struct btusb_data *data, uint8_t* p_data, unsigned int frames)
+{
+ struct snd_pcm_runtime *runtime;
+ unsigned int frame_bytes, frames1;
+ u8 *dest;
+ AIC_sco_card_t *pSCOSnd = data->pSCOSnd;
+
+ runtime = pSCOSnd->capture.substream->runtime;
+ frame_bytes = 2;
+
+ dest = runtime->dma_area + pSCOSnd->capture.buffer_pos * frame_bytes;
+ if (pSCOSnd->capture.buffer_pos + frames <= runtime->buffer_size) {
+ memcpy(dest, p_data, frames * frame_bytes);
+ } else {
+ /* wrap around at end of ring buffer */
+ frames1 = runtime->buffer_size - pSCOSnd->capture.buffer_pos;
+ memcpy(dest, p_data, frames1 * frame_bytes);
+ memcpy(runtime->dma_area,
+ p_data + frames1 * frame_bytes,
+ (frames - frames1) * frame_bytes);
+ }
+
+ pSCOSnd->capture.buffer_pos += frames;
+ if (pSCOSnd->capture.buffer_pos >= runtime->buffer_size) {
+ pSCOSnd->capture.buffer_pos -= runtime->buffer_size;
+ }
+
+ if((pSCOSnd->capture.buffer_pos%runtime->period_size) == 0) {
+ snd_pcm_period_elapsed(pSCOSnd->capture.substream);
+ }
+
+ return false;
+}
+
+
+static void hci_send_to_alsa_ringbuffer(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ struct btusb_data *data = GET_DRV_DATA(hdev);
+ AIC_sco_card_t *pSCOSnd = data->pSCOSnd;
+ uint8_t* p_data;
+ int sco_length = skb->len - HCI_SCO_HDR_SIZE;
+ u16 *handle = (u16 *) (skb->data);
+ //u8 errflg = (u8)((*handle & 0x3000) >> 12);
+
+ pSCOSnd->usb_data->sco_handle = (*handle & 0x0fff);
+
+ AICBT_DBG("%s, %x, %x %x\n", __func__,pSCOSnd->usb_data->sco_handle, *handle, errflg);
+
+ if (!hdev) {
+ AICBT_INFO("%s: Frame for unknown HCI device", __func__);
+ return;
+ }
+
+ if (!test_bit(ALSA_CAPTURE_RUNNING, &pSCOSnd->states)) {
+ AICBT_INFO("%s: ALSA is not running", __func__);
+ return;
+ }
+ snd_cap_timer.snd_sco_length = sco_length;
+ p_data = (uint8_t *)skb->data + HCI_SCO_HDR_SIZE;
+ aic_copy_capture_data_to_alsa(data, p_data, sco_length/2);
+}
+
+#endif
+
+#if CONFIG_BLUEDROID
+static struct hci_dev *hci_alloc_dev(void)
+{
+ struct hci_dev *hdev;
+
+ hdev = kzalloc(sizeof(struct hci_dev), GFP_KERNEL);
+ if (!hdev)
+ return NULL;
+
+ return hdev;
+}
+
+/* Free HCI device */
+static void hci_free_dev(struct hci_dev *hdev)
+{
+ kfree(hdev);
+}
+
+/* Register HCI device */
+static int hci_register_dev(struct hci_dev *hdev)
+{
+ int i, id;
+
+ AICBT_DBG("%s: %p name %s bus %d", __func__, hdev, hdev->name, hdev->bus);
+ /* Do not allow HCI_AMP devices to register at index 0,
+ * so the index can be used as the AMP controller ID.
+ */
+ id = (hdev->dev_type == HCI_BREDR) ? 0 : 1;
+
+ write_lock(&hci_dev_lock);
+
+ sprintf(hdev->name, "hci%d", id);
+ hdev->id = id;
+ hdev->flags = 0;
+ hdev->dev_flags = 0;
+ mutex_init(&hdev->lock);
+
+ AICBT_DBG("%s: id %d, name %s", __func__, hdev->id, hdev->name);
+
+
+ for (i = 0; i < NUM_REASSEMBLY; i++)
+ hdev->reassembly[i] = NULL;
+
+ memset(&hdev->stat, 0, sizeof(struct hci_dev_stats));
+ atomic_set(&hdev->promisc, 0);
+
+ if (ghdev) {
+ write_unlock(&hci_dev_lock);
+ AICBT_ERR("%s: Hci device has been registered already", __func__);
+ return -1;
+ } else
+ ghdev = hdev;
+
+ write_unlock(&hci_dev_lock);
+
+ return id;
+}
+
+/* Unregister HCI device */
+static void hci_unregister_dev(struct hci_dev *hdev)
+{
+ int i;
+
+ AICBT_DBG("%s: hdev %p name %s bus %d", __func__, hdev, hdev->name, hdev->bus);
+ set_bit(HCI_UNREGISTER, &hdev->dev_flags);
+
+ write_lock(&hci_dev_lock);
+ ghdev = NULL;
+ write_unlock(&hci_dev_lock);
+
+ hci_dev_do_close(hdev);
+ for (i = 0; i < NUM_REASSEMBLY; i++)
+ kfree_skb(hdev->reassembly[i]);
+}
+
+static void hci_send_to_stack(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ struct sk_buff *aic_skb_copy = NULL;
+
+ //AICBT_DBG("%s", __func__);
+
+ if (!hdev) {
+ AICBT_ERR("%s: Frame for unknown HCI device", __func__);
+ return;
+ }
+
+ if (!test_bit(HCI_RUNNING, &hdev->flags)) {
+ AICBT_ERR("%s: HCI not running", __func__);
+ return;
+ }
+
+ aic_skb_copy = pskb_copy(skb, GFP_ATOMIC);
+ if (!aic_skb_copy) {
+ AICBT_ERR("%s: Copy skb error", __func__);
+ return;
+ }
+
+ memcpy(skb_push(aic_skb_copy, 1), &bt_cb(skb)->pkt_type, 1);
+ aic_enqueue(aic_skb_copy);
+
+ /* Make sure bt char device existing before wakeup read queue */
+ hdev = hci_dev_get(0);
+ if (hdev) {
+ //AICBT_DBG("%s: Try to wakeup read queue", __func__);
+ AICBT_DBG("%s", __func__);
+ wake_up_interruptible(&btchr_read_wait);
+ }
+
+
+ return;
+}
+
+/* Receive frame from HCI drivers */
+static int hci_recv_frame(struct sk_buff *skb)
+{
+ struct hci_dev *hdev = (struct hci_dev *) skb->dev;
+
+ if (!hdev ||
+ (!test_bit(HCI_UP, &hdev->flags) && !test_bit(HCI_INIT, &hdev->flags))) {
+ kfree_skb(skb);
+ return -ENXIO;
+ }
+
+ /* Incomming skb */
+ bt_cb(skb)->incoming = 1;
+
+ /* Time stamp */
+ __net_timestamp(skb);
+
+ if (atomic_read(&hdev->promisc)) {
+#ifdef CONFIG_SCO_OVER_HCI
+ if(bt_cb(skb)->pkt_type == HCI_SCODATA_PKT){
+ hci_send_to_alsa_ringbuffer(hdev, skb);
+ }else{
+#ifdef CONFIG_SUPPORT_VENDOR_APCF
+ if(bt_cb(skb)->pkt_type == HCI_EVENT_PKT){
+ if(bypass_event(skb)){
+ kfree_skb(skb);
+ return 0;
+ }
+ }
+#endif //CONFIG_SUPPORT_VENDOR_APCF
+ hci_send_to_stack(hdev, skb);
+ }
+#else
+#ifdef CONFIG_SUPPORT_VENDOR_APCF
+ if(bt_cb(skb)->pkt_type == HCI_EVENT_PKT){
+ if(bypass_event(skb)){
+ kfree_skb(skb);
+ return 0;
+ }
+ }
+#endif //CONFIG_SUPPORT_VENDOR_APCF
+ /* Send copy to the sockets */
+ hci_send_to_stack(hdev, skb);
+#endif
+
+ }
+
+ kfree_skb(skb);
+ return 0;
+}
+
+
+
+static int hci_reassembly(struct hci_dev *hdev, int type, void *data,
+ int count, __u8 index)
+{
+ int len = 0;
+ int hlen = 0;
+ int remain = count;
+ struct sk_buff *skb;
+ struct bt_skb_cb *scb;
+
+ //AICBT_DBG("%s", __func__);
+
+ if ((type < HCI_ACLDATA_PKT || type > HCI_EVENT_PKT) ||
+ index >= NUM_REASSEMBLY)
+ return -EILSEQ;
+
+ skb = hdev->reassembly[index];
+
+ if (!skb) {
+ switch (type) {
+ case HCI_ACLDATA_PKT:
+ len = HCI_MAX_FRAME_SIZE;
+ hlen = HCI_ACL_HDR_SIZE;
+ break;
+ case HCI_EVENT_PKT:
+ len = HCI_MAX_EVENT_SIZE;
+ hlen = HCI_EVENT_HDR_SIZE;
+ break;
+ case HCI_SCODATA_PKT:
+ len = HCI_MAX_SCO_SIZE;
+ hlen = HCI_SCO_HDR_SIZE;
+ break;
+ }
+
+ skb = bt_skb_alloc(len, GFP_ATOMIC);
+ if (!skb)
+ return -ENOMEM;
+
+ scb = (void *) skb->cb;
+ scb->expect = hlen;
+ scb->pkt_type = type;
+
+ skb->dev = (void *) hdev;
+ hdev->reassembly[index] = skb;
+ }
+
+ while (count) {
+ scb = (void *) skb->cb;
+ len = min_t(uint, scb->expect, count);
+
+ memcpy(skb_put(skb, len), data, len);
+
+ count -= len;
+ data += len;
+ scb->expect -= len;
+ remain = count;
+
+ switch (type) {
+ case HCI_EVENT_PKT:
+ if (skb->len == HCI_EVENT_HDR_SIZE) {
+ struct hci_event_hdr *h = hci_event_hdr(skb);
+ scb->expect = h->plen;
+
+ if (skb_tailroom(skb) < scb->expect) {
+ kfree_skb(skb);
+ hdev->reassembly[index] = NULL;
+ return -ENOMEM;
+ }
+ }
+ break;
+
+ case HCI_ACLDATA_PKT:
+ if (skb->len == HCI_ACL_HDR_SIZE) {
+ struct hci_acl_hdr *h = hci_acl_hdr(skb);
+ scb->expect = __le16_to_cpu(h->dlen);
+
+ if (skb_tailroom(skb) < scb->expect) {
+ kfree_skb(skb);
+ hdev->reassembly[index] = NULL;
+ return -ENOMEM;
+ }
+ }
+ break;
+
+ case HCI_SCODATA_PKT:
+ if (skb->len == HCI_SCO_HDR_SIZE) {
+ struct hci_sco_hdr *h = hci_sco_hdr(skb);
+ scb->expect = h->dlen;
+
+ if (skb_tailroom(skb) < scb->expect) {
+ kfree_skb(skb);
+ hdev->reassembly[index] = NULL;
+ return -ENOMEM;
+ }
+ }
+ break;
+ }
+
+ if (scb->expect == 0) {
+ /* Complete frame */
+ if(HCI_ACLDATA_PKT == type)
+ print_acl(skb,0);
+ if(HCI_SCODATA_PKT == type)
+ print_sco(skb,0);
+ if(HCI_EVENT_PKT == type)
+ print_event(skb);
+
+ bt_cb(skb)->pkt_type = type;
+ hci_recv_frame(skb);
+
+ hdev->reassembly[index] = NULL;
+ return remain;
+ }
+ }
+
+ return remain;
+}
+
+static int hci_recv_fragment(struct hci_dev *hdev, int type, void *data, int count)
+{
+ int rem = 0;
+
+ if (type < HCI_ACLDATA_PKT || type > HCI_EVENT_PKT)
+ return -EILSEQ;
+
+ while (count) {
+ rem = hci_reassembly(hdev, type, data, count, type - 1);
+ if (rem < 0)
+ return rem;
+
+ data += (count - rem);
+ count = rem;
+ }
+
+ return rem;
+}
+#endif //CONFIG_BLUEDROID
+
+void hci_hardware_error(void)
+{
+ struct sk_buff *aic_skb_copy = NULL;
+ int len = 3;
+ uint8_t hardware_err_pkt[4] = {HCI_EVENT_PKT, 0x10, 0x01, HCI_VENDOR_USB_DISC_HARDWARE_ERROR};
+
+ aic_skb_copy = alloc_skb(len, GFP_ATOMIC);
+ if (!aic_skb_copy) {
+ AICBT_ERR("%s: Failed to allocate mem", __func__);
+ return;
+ }
+
+ memcpy(skb_put(aic_skb_copy, len), hardware_err_pkt, len);
+ aic_enqueue(aic_skb_copy);
+
+ wake_up_interruptible(&btchr_read_wait);
+}
+
+static int btchr_open(struct inode *inode_p, struct file *file_p)
+{
+ struct btusb_data *data;
+ struct hci_dev *hdev;
+
+ AICBT_DBG("%s: BT usb char device is opening", __func__);
+ /* Not open unless wanna tracing log */
+ /* trace_printk("%s: open....\n", __func__); */
+
+ hdev = hci_dev_get(0);
+ if (!hdev) {
+ AICBT_DBG("%s: Failed to get hci dev[NULL]", __func__);
+ return -ENODEV;
+ }
+ data = GET_DRV_DATA(hdev);
+
+ atomic_inc(&hdev->promisc);
+ /*
+ * As bt device is not re-opened when hotplugged out, we cannot
+ * trust on file's private data(may be null) when other file ops
+ * are invoked.
+ */
+ file_p->private_data = data;
+
+ mutex_lock(&btchr_mutex);
+ hci_dev_open(0);
+ mutex_unlock(&btchr_mutex);
+
+ aic_clear_queue();
+ return nonseekable_open(inode_p, file_p);
+}
+
+static int btchr_close(struct inode *inode_p, struct file *file_p)
+{
+ struct btusb_data *data;
+ struct hci_dev *hdev;
+
+ AICBT_INFO("%s: BT usb char device is closing", __func__);
+ /* Not open unless wanna tracing log */
+ /* trace_printk("%s: close....\n", __func__); */
+
+ data = file_p->private_data;
+ file_p->private_data = NULL;
+
+#if CONFIG_BLUEDROID
+ /*
+ * If the upper layer closes bt char interfaces, no reset
+ * action required even bt device hotplugged out.
+ */
+ bt_reset = 0;
+#endif
+
+ hdev = hci_dev_get(0);
+ if (hdev) {
+ atomic_set(&hdev->promisc, 0);
+ mutex_lock(&btchr_mutex);
+ hci_dev_close(0);
+ mutex_unlock(&btchr_mutex);
+ }
+
+ return 0;
+}
+
+static ssize_t btchr_read(struct file *file_p,
+ char __user *buf_p,
+ size_t count,
+ loff_t *pos_p)
+{
+ struct hci_dev *hdev;
+ struct sk_buff *skb;
+ ssize_t ret = 0;
+
+ while (count) {
+ hdev = hci_dev_get(0);
+ if (!hdev) {
+ /*
+ * Note: Only when BT device hotplugged out, we wil get
+ * into such situation. In order to keep the upper layer
+ * stack alive (blocking the read), we should never return
+ * EFAULT or break the loop.
+ */
+ AICBT_ERR("%s: Failed to get hci dev[Null]", __func__);
+ }
+
+ ret = wait_event_interruptible(btchr_read_wait, !is_queue_empty());
+ if (ret < 0) {
+ AICBT_ERR("%s: wait event is signaled %d", __func__, (int)ret);
+ break;
+ }
+
+ skb = aic_dequeue_try(count);
+ if (skb) {
+ ret = usb_put_user(skb, buf_p, count);
+ if (ret < 0)
+ AICBT_ERR("%s: Failed to put data to user space", __func__);
+ kfree_skb(skb);
+ break;
+ }
+ }
+
+ return ret;
+}
+
+#ifdef CONFIG_SUPPORT_VENDOR_APCF
+void btchr_external_write(char* buff, int len){
+ struct hci_dev *hdev;
+ struct sk_buff *skb;
+ int i;
+ struct btusb_data *data;
+
+ AICBT_INFO("%s \r\n", __func__);
+ for(i=0;i<len;i++){
+ printk("0x%x ",(u8)buff[i]);
+ }
+ printk("\r\n");
+ hdev = hci_dev_get(0);
+ if (!hdev) {
+ AICBT_WARN("%s: Failed to get hci dev[Null]", __func__);
+ return;
+ }
+ /* Never trust on btusb_data, as bt device may be hotplugged out */
+ data = GET_DRV_DATA(hdev);
+ if (!data) {
+ AICBT_WARN("%s: Failed to get bt usb driver data[Null]", __func__);
+ return;
+ }
+ vendor_apcf_sent_done++;
+
+ skb = bt_skb_alloc(len, GFP_ATOMIC);
+ if (!skb)
+ return;
+ skb_reserve(skb, -1); // Add this line
+ skb->dev = (void *)hdev;
+ memcpy((__u8 *)skb->data,(__u8 *)buff,len);
+ skb_put(skb, len);
+ bt_cb(skb)->pkt_type = *((__u8 *)skb->data);
+ skb_pull(skb, 1);
+ data->hdev->send(skb);
+}
+
+EXPORT_SYMBOL(btchr_external_write);
+#endif //CONFIG_SUPPORT_VENDOR_APCF
+
+static ssize_t btchr_write(struct file *file_p,
+ const char __user *buf_p,
+ size_t count,
+ loff_t *pos_p)
+{
+ struct btusb_data *data = file_p->private_data;
+ struct hci_dev *hdev;
+ struct sk_buff *skb;
+
+ //AICBT_DBG("%s: BT usb char device is writing", __func__);
+ AICBT_DBG("%s", __func__);
+
+ hdev = hci_dev_get(0);
+ if (!hdev) {
+ AICBT_WARN("%s: Failed to get hci dev[Null]", __func__);
+ /*
+ * Note: we bypass the data from the upper layer if bt device
+ * is hotplugged out. Fortunatelly, H4 or H5 HCI stack does
+ * NOT check btchr_write's return value. However, returning
+ * count instead of EFAULT is preferable.
+ */
+ /* return -EFAULT; */
+ return count;
+ }
+
+ /* Never trust on btusb_data, as bt device may be hotplugged out */
+ data = GET_DRV_DATA(hdev);
+ if (!data) {
+ AICBT_WARN("%s: Failed to get bt usb driver data[Null]", __func__);
+ return count;
+ }
+
+ if (count > HCI_MAX_FRAME_SIZE)
+ return -EINVAL;
+
+ skb = bt_skb_alloc(count, GFP_ATOMIC);
+ if (!skb)
+ return -ENOMEM;
+ skb_reserve(skb, -1); // Add this line
+
+ if (copy_from_user(skb_put(skb, count), buf_p, count)) {
+ AICBT_ERR("%s: Failed to get data from user space", __func__);
+ kfree_skb(skb);
+ return -EFAULT;
+ }
+
+ skb->dev = (void *)hdev;
+ bt_cb(skb)->pkt_type = *((__u8 *)skb->data);
+ skb_pull(skb, 1);
+ data->hdev->send(skb);
+
+ return count;
+}
+
+static unsigned int btchr_poll(struct file *file_p, poll_table *wait)
+{
+ struct btusb_data *data = file_p->private_data;
+ struct hci_dev *hdev;
+
+ //AICBT_DBG("%s: BT usb char device is polling", __func__);
+
+ if(!bt_char_dev_registered) {
+ AICBT_ERR("%s: char device has not registered!", __func__);
+ return POLLERR | POLLHUP;
+ }
+
+ poll_wait(file_p, &btchr_read_wait, wait);
+
+ hdev = hci_dev_get(0);
+ if (!hdev) {
+ AICBT_ERR("%s: Failed to get hci dev[Null]", __func__);
+ mdelay(URB_CANCELING_DELAY_MS);
+ return POLLERR | POLLHUP;
+ return POLLOUT | POLLWRNORM;
+ }
+
+ /* Never trust on btusb_data, as bt device may be hotplugged out */
+ data = GET_DRV_DATA(hdev);
+ if (!data) {
+ /*
+ * When bt device is hotplugged out, btusb_data will
+ * be freed in disconnect.
+ */
+ AICBT_ERR("%s: Failed to get bt usb driver data[Null]", __func__);
+ mdelay(URB_CANCELING_DELAY_MS);
+ return POLLOUT | POLLWRNORM;
+ }
+
+ if (!is_queue_empty())
+ return POLLIN | POLLRDNORM;
+
+ return POLLOUT | POLLWRNORM;
+}
+static long btchr_ioctl(struct file *file_p,unsigned int cmd, unsigned long arg)
+{
+ int ret = 0;
+ struct hci_dev *hdev;
+ struct btusb_data *data;
+ firmware_info *fw_info;
+
+ if(!bt_char_dev_registered) {
+ return -ENODEV;
+ }
+
+ if(check_set_dlfw_state_value(1) != 1) {
+ AICBT_ERR("%s bt controller is disconnecting!", __func__);
+ return 0;
+ }
+
+ hdev = hci_dev_get(0);
+ if(!hdev) {
+ AICBT_ERR("%s device is NULL!", __func__);
+ set_dlfw_state_value(0);
+ return 0;
+ }
+ data = GET_DRV_DATA(hdev);
+ fw_info = data->fw_info;
+
+ AICBT_INFO(" btchr_ioctl DOWN_FW_CFG with Cmd:%d",cmd);
+ switch (cmd) {
+ case DOWN_FW_CFG:
+ AICBT_INFO(" btchr_ioctl DOWN_FW_CFG");
+ ret = usb_autopm_get_interface(data->intf);
+ if (ret < 0){
+ goto failed;
+ }
+
+ //ret = download_patch(fw_info,1);
+ usb_autopm_put_interface(data->intf);
+ if(ret < 0){
+ AICBT_ERR("%s:Failed in download_patch with ret:%d",__func__,ret);
+ goto failed;
+ }
+
+ ret = hdev->open(hdev);
+ if(ret < 0){
+ AICBT_ERR("%s:Failed in hdev->open(hdev):%d",__func__,ret);
+ goto failed;
+ }
+ set_bit(HCI_UP, &hdev->flags);
+ set_dlfw_state_value(0);
+ wake_up_interruptible(&bt_dlfw_wait);
+ return 1;
+ case DWFW_CMPLT:
+ AICBT_INFO(" btchr_ioctl DWFW_CMPLT");
+#if 1
+ case SET_ISO_CFG:
+ AICBT_INFO("btchr_ioctl SET_ISO_CFG");
+ if(copy_from_user(&(hdev->voice_setting), (__u16*)arg, sizeof(__u16))){
+ AICBT_INFO(" voice settings err");
+ }
+ //hdev->voice_setting = *(uint16_t*)arg;
+ AICBT_INFO(" voice settings = %d", hdev->voice_setting);
+ //return 1;
+#endif
+ case GET_USB_INFO:
+ //ret = download_patch(fw_info,1);
+ AICBT_INFO(" btchr_ioctl GET_USB_INFO");
+ ret = hdev->open(hdev);
+ if(ret < 0){
+ AICBT_ERR("%s:Failed in hdev->open(hdev):%d",__func__,ret);
+ //goto done;
+ }
+ set_bit(HCI_UP, &hdev->flags);
+ set_dlfw_state_value(0);
+ wake_up_interruptible(&bt_dlfw_wait);
+ return 1;
+ case RESET_CONTROLLER:
+ AICBT_INFO(" btchr_ioctl RESET_CONTROLLER");
+ //reset_controller(fw_info);
+ return 1;
+ default:
+ AICBT_ERR("%s:Failed with wrong Cmd:%d",__func__,cmd);
+ goto failed;
+ }
+ failed:
+ set_dlfw_state_value(0);
+ wake_up_interruptible(&bt_dlfw_wait);
+ return ret;
+
+}
+
+#ifdef CONFIG_PLATFORM_UBUNTU//AIDEN
+typedef u32 compat_uptr_t;
+static inline void __user *compat_ptr(compat_uptr_t uptr)
+{
+ return (void __user *)(unsigned long)uptr;
+}
+#endif
+
+#ifdef CONFIG_COMPAT
+static long compat_btchr_ioctl (struct file *filp, unsigned int cmd, unsigned long arg)
+{
+ AICBT_DBG("%s: enter",__func__);
+ return btchr_ioctl(filp, cmd, (unsigned long) compat_ptr(arg));
+}
+#endif
+static struct file_operations bt_chrdev_ops = {
+ open : btchr_open,
+ release : btchr_close,
+ read : btchr_read,
+ write : btchr_write,
+ poll : btchr_poll,
+ unlocked_ioctl : btchr_ioctl,
+#ifdef CONFIG_COMPAT
+ compat_ioctl : compat_btchr_ioctl,
+#endif
+};
+
+static int btchr_init(void)
+{
+ int res = 0;
+ struct device *dev;
+
+ AICBT_INFO("Register usb char device interface for BT driver");
+ /*
+ * btchr mutex is used to sync between
+ * 1) downloading patch and opening bt char driver
+ * 2) the file operations of bt char driver
+ */
+ mutex_init(&btchr_mutex);
+
+ skb_queue_head_init(&btchr_readq);
+ init_waitqueue_head(&btchr_read_wait);
+ init_waitqueue_head(&bt_dlfw_wait);
+
+ bt_char_class = class_create(THIS_MODULE, BT_CHAR_DEVICE_NAME);
+ if (IS_ERR(bt_char_class)) {
+ AICBT_ERR("Failed to create bt char class");
+ return PTR_ERR(bt_char_class);
+ }
+
+ res = alloc_chrdev_region(&bt_devid, 0, 1, BT_CHAR_DEVICE_NAME);
+ if (res < 0) {
+ AICBT_ERR("Failed to allocate bt char device");
+ goto err_alloc;
+ }
+
+ dev = device_create(bt_char_class, NULL, bt_devid, NULL, BT_CHAR_DEVICE_NAME);
+ if (IS_ERR(dev)) {
+ AICBT_ERR("Failed to create bt char device");
+ res = PTR_ERR(dev);
+ goto err_create;
+ }
+
+ cdev_init(&bt_char_dev, &bt_chrdev_ops);
+ res = cdev_add(&bt_char_dev, bt_devid, 1);
+ if (res < 0) {
+ AICBT_ERR("Failed to add bt char device");
+ goto err_add;
+ }
+
+ return 0;
+
+err_add:
+ device_destroy(bt_char_class, bt_devid);
+err_create:
+ unregister_chrdev_region(bt_devid, 1);
+err_alloc:
+ class_destroy(bt_char_class);
+ return res;
+}
+
+static void btchr_exit(void)
+{
+ AICBT_INFO("Unregister usb char device interface for BT driver");
+
+ device_destroy(bt_char_class, bt_devid);
+ cdev_del(&bt_char_dev);
+ unregister_chrdev_region(bt_devid, 1);
+ class_destroy(bt_char_class);
+
+ return;
+}
+#endif
+
+int send_hci_cmd(firmware_info *fw_info)
+{
+
+ int len = 0;
+ int ret_val = -1;
+ int i = 0;
+
+ if(g_chipid == PRODUCT_ID_AIC8801 || g_chipid == PRODUCT_ID_AIC8800D80){
+ ret_val = usb_bulk_msg(fw_info->udev, fw_info->pipe_out, fw_info->send_pkt, fw_info->pkt_len,
+ &len, 3000);
+ if (ret_val || (len != fw_info->pkt_len)) {
+ AICBT_INFO("Error in send hci cmd = %d,"
+ "len = %d, size = %d", ret_val, len, fw_info->pkt_len);
+ }
+ }else if(g_chipid == PRODUCT_ID_AIC8800DC){
+ while((ret_val<0)&&(i++<3))
+ {
+ ret_val = usb_control_msg(
+ fw_info->udev, fw_info->pipe_out,
+ 0, USB_TYPE_CLASS, 0, 0,
+ (void *)(fw_info->send_pkt),
+ fw_info->pkt_len, MSG_TO);
+ }
+
+ }
+ return ret_val;
+
+}
+
+int rcv_hci_evt(firmware_info *fw_info)
+{
+ int ret_len = 0, ret_val = 0;
+ int i;
+
+ while (1) {
+ for(i = 0; i < 5; i++) {
+ ret_val = usb_interrupt_msg(
+ fw_info->udev, fw_info->pipe_in,
+ (void *)(fw_info->rcv_pkt), RCV_PKT_LEN,
+ &ret_len, MSG_TO);
+ if (ret_val >= 0)
+ break;
+ }
+
+ if (ret_val < 0)
+ return ret_val;
+
+ if (CMD_CMP_EVT == fw_info->evt_hdr->evt) {
+ if (fw_info->cmd_hdr->opcode == fw_info->cmd_cmp->opcode)
+ return ret_len;
+ }
+ }
+}
+
+int set_bt_onoff(firmware_info *fw_info, uint8_t onoff)
+{
+ int ret_val;
+
+ AICBT_INFO("%s: %s", __func__, onoff != 0 ? "on" : "off");
+
+ fw_info->cmd_hdr->opcode = cpu_to_le16(BTOFF_OPCODE);
+ fw_info->cmd_hdr->plen = 1;
+ fw_info->pkt_len = CMD_HDR_LEN + 1;
+ fw_info->send_pkt[CMD_HDR_LEN] = onoff;
+
+ ret_val = send_hci_cmd(fw_info);
+ if (ret_val < 0) {
+ AICBT_ERR("%s: Failed to send bt %s cmd, errno %d",
+ __func__, onoff != 0 ? "on" : "off", ret_val);
+ return ret_val;
+ }
+
+ ret_val = rcv_hci_evt(fw_info);
+ if (ret_val < 0) {
+ AICBT_ERR("%s: Failed to receive bt %s event, errno %d",
+ __func__, onoff != 0 ? "on" : "off", ret_val);
+ return ret_val;
+ }
+
+ return ret_val;
+}
+
+//for 8800DC start
+u32 fwcfg_tbl[][2] = {
+ {0x40200028, 0x0021047e},
+ {0x40200024, 0x0000011d},
+};
+
+int fw_config(firmware_info* fw_info)
+{
+ int ret_val = -1;
+ struct hci_dbg_rd_mem_cmd *rd_cmd;
+ struct hci_dbg_rd_mem_cmd_evt *evt_para;
+ int len = 0, i = 0;
+ struct fw_status *evt_status;
+
+ rd_cmd = (struct hci_dbg_rd_mem_cmd *)(fw_info->req_para);
+ if (!rd_cmd)
+ return -ENOMEM;
+
+ rd_cmd->start_addr = 0x40200024;
+ rd_cmd->type = 32;
+ rd_cmd->length = 4;
+ fw_info->cmd_hdr->opcode = cpu_to_le16(HCI_VSC_DBG_RD_MEM_CMD);
+ fw_info->cmd_hdr->plen = sizeof(struct hci_dbg_rd_mem_cmd);
+ fw_info->pkt_len = CMD_HDR_LEN + sizeof(struct hci_dbg_rd_mem_cmd);
+
+ ret_val = send_hci_cmd(fw_info);
+ if (ret_val < 0) {
+ printk("%s: Failed to send hci cmd 0x%04x, errno %d",
+ __func__, fw_info->cmd_hdr->opcode, ret_val);
+ return ret_val;
+ }
+
+ ret_val = rcv_hci_evt(fw_info);
+ if (ret_val < 0) {
+ printk("%s: Failed to receive hci event, errno %d",
+ __func__, ret_val);
+ return ret_val;
+ }
+
+ evt_para = (struct hci_dbg_rd_mem_cmd_evt *)(fw_info->rsp_para);
+
+ printk("%s: fw status = 0x%04x, length %d, %x %x %x %x",
+ __func__, evt_para->status, evt_para->length,
+ evt_para->data[0],
+ evt_para->data[1],
+ evt_para->data[2],
+ evt_para->data[3]);
+
+ ret_val = evt_para->status;
+ if (evt_para->status == 0) {
+ uint16_t rd_data = (evt_para->data[0] | (evt_para->data[1] << 8));
+ printk("%s rd_data is %x\n", __func__, rd_data);
+ if (rd_data == 0x119) {
+ struct aicbt_patch_table_cmd *patch_table_cmd = (struct aicbt_patch_table_cmd *)(fw_info->req_para);
+ len = sizeof(fwcfg_tbl) / sizeof(u32) / 2;
+ patch_table_cmd->patch_num = len;
+ for (i = 0; i < len; i++) {
+ memcpy(&patch_table_cmd->patch_table_addr[i], &fwcfg_tbl[i][0], sizeof(uint32_t));
+ memcpy(&patch_table_cmd->patch_table_data[i], &fwcfg_tbl[i][1], sizeof(uint32_t));
+ printk("%s [%d] data: %08x %08x\n", __func__, i, patch_table_cmd->patch_table_addr[i],patch_table_cmd->patch_table_data[i]);
+ }
+ fw_info->cmd_hdr->opcode = cpu_to_le16(HCI_VSC_UPDATE_PT_CMD);
+ fw_info->cmd_hdr->plen = HCI_VSC_UPDATE_PT_SIZE;
+ fw_info->pkt_len = fw_info->cmd_hdr->plen + 3;
+ ret_val = send_hci_cmd(fw_info);
+ if (ret_val < 0) {
+ AICBT_ERR("%s: rcv_hci_evt err %d", __func__, ret_val);
+ return ret_val;
+ }
+ ret_val = rcv_hci_evt(fw_info);
+ if (ret_val < 0) {
+ printk("%s: Failed to receive hci event, errno %d",
+ __func__, ret_val);
+ return ret_val;
+ }
+ evt_status = (struct fw_status *)fw_info->rsp_para;
+ ret_val = evt_status->status;
+ if (0 != evt_status->status) {
+ ret_val = -1;
+ } else {
+ ret_val = 0;
+ }
+
+ }
+ }
+ return ret_val;
+}
+
+int system_config(firmware_info *fw_info)
+{
+ int ret_val = -1;
+ struct hci_dbg_rd_mem_cmd *rd_cmd;
+ struct hci_dbg_rd_mem_cmd_evt *evt_para;
+ //int len = 0, i = 0;
+ //struct fw_status *evt_status;
+
+ rd_cmd = (struct hci_dbg_rd_mem_cmd *)(fw_info->req_para);
+ if (!rd_cmd)
+ return -ENOMEM;
+
+ rd_cmd->start_addr = 0x40500000;
+ rd_cmd->type = 32;
+ rd_cmd->length = 4;
+ fw_info->cmd_hdr->opcode = cpu_to_le16(HCI_VSC_DBG_RD_MEM_CMD);
+ fw_info->cmd_hdr->plen = sizeof(struct hci_dbg_rd_mem_cmd);
+ fw_info->pkt_len = CMD_HDR_LEN + sizeof(struct hci_dbg_rd_mem_cmd);
+
+ ret_val = send_hci_cmd(fw_info);
+ if (ret_val < 0)
+ {
+ printk("%s: Failed to send hci cmd 0x%04x, errno %d",
+ __func__, fw_info->cmd_hdr->opcode, ret_val);
+ return ret_val;
+ }
+
+ ret_val = rcv_hci_evt(fw_info);
+ if (ret_val < 0)
+ {
+ printk("%s: Failed to receive hci event, errno %d",
+ __func__, ret_val);
+ return ret_val;
+ }
+
+ evt_para = (struct hci_dbg_rd_mem_cmd_evt *)(fw_info->rsp_para);
+
+ printk("%s: fw status = 0x%04x, length %d, %x %x %x %x",
+ __func__, evt_para->status, evt_para->length,
+ evt_para->data[0],
+ evt_para->data[1],
+ evt_para->data[2],
+ evt_para->data[3]);
+
+ ret_val = evt_para->status;
+ if (evt_para->status == 0)
+ {
+ uint32_t rd_data = (evt_para->data[0] | (evt_para->data[1] << 8) | (evt_para->data[2] << 16) | (evt_para->data[3] << 24));
+ //printk("%s 0x40500000 rd_data is %x\n", __func__, rd_data);
+ chip_id = (u8) (rd_data >> 16);
+ }
+
+ rd_cmd->start_addr = 0x20;
+ rd_cmd->type = 32;
+ rd_cmd->length = 4;
+ fw_info->cmd_hdr->opcode = cpu_to_le16(HCI_VSC_DBG_RD_MEM_CMD);
+ fw_info->cmd_hdr->plen = sizeof(struct hci_dbg_rd_mem_cmd);
+ fw_info->pkt_len = CMD_HDR_LEN + sizeof(struct hci_dbg_rd_mem_cmd);
+
+ ret_val = send_hci_cmd(fw_info);
+ if (ret_val < 0)
+ {
+ printk("%s: Failed to send hci cmd 0x%04x, errno %d",
+ __func__, fw_info->cmd_hdr->opcode, ret_val);
+ return ret_val;
+ }
+
+ ret_val = rcv_hci_evt(fw_info);
+ if (ret_val < 0)
+ {
+ printk("%s: Failed to receive hci event, errno %d",
+ __func__, ret_val);
+ return ret_val;
+ }
+
+ evt_para = (struct hci_dbg_rd_mem_cmd_evt *)(fw_info->rsp_para);
+
+ printk("%s: fw status = 0x%04x, length %d, %x %x %x %x",
+ __func__, evt_para->status, evt_para->length,
+ evt_para->data[0],
+ evt_para->data[1],
+ evt_para->data[2],
+ evt_para->data[3]);
+
+ ret_val = evt_para->status;
+ if (evt_para->status == 0)
+ {
+ uint32_t rd_data = (evt_para->data[0] | (evt_para->data[1] << 8) | (evt_para->data[2] << 16) | (evt_para->data[3] << 24));
+ //printk("%s 0x02 rd_data is %x\n", __func__, rd_data);
+ sub_chip_id = (u8) (rd_data);
+ }
+ printk("chip_id = %x, sub_chip_id = %x\n", chip_id, sub_chip_id);
+ return ret_val;
+}
+
+int check_fw_status(firmware_info* fw_info)
+{
+ struct fw_status *read_ver_rsp;
+ int ret_val = -1;
+
+ fw_info->cmd_hdr->opcode = cpu_to_le16(HCI_VSC_FW_STATUS_GET_CMD);
+ fw_info->cmd_hdr->plen = 0;
+ fw_info->pkt_len = CMD_HDR_LEN;
+
+ ret_val = send_hci_cmd(fw_info);
+ if (ret_val < 0) {
+ printk("%s: Failed to send hci cmd 0x%04x, errno %d",
+ __func__, fw_info->cmd_hdr->opcode, ret_val);
+ return ret_val;
+ }
+
+ ret_val = rcv_hci_evt(fw_info);
+ if (ret_val < 0) {
+ printk("%s: Failed to receive hci event, errno %d",
+ __func__, ret_val);
+ return ret_val;
+ }
+
+ read_ver_rsp = (struct fw_status *)(fw_info->rsp_para);
+
+ printk("%s: fw status = 0x%04x",
+ __func__, read_ver_rsp->status);
+ return read_ver_rsp->status;
+}
+
+int download_data(firmware_info *fw_info, u32 fw_addr, char *filename)
+{
+ unsigned int i=0;
+ int size;
+ u8 *dst=NULL;
+ int err=0;
+ struct hci_dbg_wr_mem_cmd *dl_cmd;
+ int hdr_len = sizeof(__le32) + sizeof(__u8) + sizeof(__u8);
+ int data_len = HCI_VSC_MEM_WR_SIZE;
+ int frag_len = data_len + hdr_len;
+ int ret_val;
+ int ncmd = 1;
+ struct fw_status *evt_para;
+
+ /* load aic firmware */
+ size = aic_load_firmware(&dst, filename, NULL);
+ if(size <= 0){
+ printk("wrong size of firmware file\n");
+ vfree(dst);
+ dst = NULL;
+ return -1;
+ }
+
+ dl_cmd = (struct hci_dbg_wr_mem_cmd *)(fw_info->req_para);
+ if (!dl_cmd)
+ return -ENOMEM;
+ evt_para = (struct fw_status *)fw_info->rsp_para;
+
+ /* Copy the file on the Embedded side */
+ printk("### Upload %s firmware, @ = %x size=%d\n", filename, fw_addr, size);
+
+ if (size > HCI_VSC_MEM_WR_SIZE) {// > 1KB data
+ for (i = 0; i < (size - HCI_VSC_MEM_WR_SIZE); i += HCI_VSC_MEM_WR_SIZE) {//each time write 240 bytes
+ data_len = HCI_VSC_MEM_WR_SIZE;
+ frag_len = data_len + hdr_len;
+ memcpy(dl_cmd->data, dst + i, data_len);
+ dl_cmd->length = data_len;
+ dl_cmd->type = 32;
+ dl_cmd->start_addr = fw_addr + i;
+ fw_info->cmd_hdr->opcode = cpu_to_le16(DOWNLOAD_OPCODE);
+ fw_info->cmd_hdr->plen = frag_len;
+ fw_info->pkt_len = frag_len + 3;
+ #if 0
+ printk("[%d] data_len %d, src %x, dst %x\n", i, data_len, dst + i, fw_addr + i);
+ printk("%p , %d\n", dl_cmd, fw_info->pkt_len);
+ print_hex_dump(KERN_ERR,"payload:",DUMP_PREFIX_NONE,16,1,dl_cmd->data,32,false);
+ /* Send download command */
+ print_hex_dump(KERN_ERR,"data:",DUMP_PREFIX_NONE,16,1,fw_info->send_pkt,32,false);
+ #endif
+ ret_val = send_hci_cmd(fw_info);
+
+ while (ncmd > 0) {
+ ret_val = rcv_hci_evt(fw_info);
+ printk("rcv_hci_evt %d\n", ret_val);
+ if (ret_val < 0) {
+ AICBT_ERR("%s: rcv_hci_evt err %d", __func__, ret_val);
+ goto out;
+ } else {
+ AICBT_DBG("%s: Receive acked frag num %d", __func__, evt_para->status);
+ ncmd--;
+ }
+ if (0 != evt_para->status) {
+ AICBT_ERR("%s: Receive acked frag num %d, err status %d",
+ __func__, ret_val, evt_para->status);
+ ret_val = -1;
+ goto out;
+ } else {
+ ret_val = 0;
+ }
+ }
+ ncmd = 1;
+ }
+ }
+
+ if (!err && (i < size)) {// <1KB data
+ data_len = size - i;
+ frag_len = data_len + hdr_len;
+ memcpy(dl_cmd->data, dst + i, data_len);
+ dl_cmd->length = data_len;
+ dl_cmd->type = 32;
+ dl_cmd->start_addr = fw_addr + i;
+ fw_info->cmd_hdr->opcode = cpu_to_le16(DOWNLOAD_OPCODE);
+ fw_info->cmd_hdr->plen = frag_len;
+ fw_info->pkt_len = frag_len + 3;
+ ret_val = send_hci_cmd(fw_info);
+ //printk("(%d) data_len %d, src %x, dst %x\n", i, data_len, (dst + i), fw_addr + i);
+ //printk("%p , %d\n", dl_cmd, fw_info->pkt_len);
+ while (ncmd > 0) {
+ ret_val = rcv_hci_evt(fw_info);
+ if (ret_val < 0) {
+ AICBT_ERR("%s: rcv_hci_evt err %d", __func__, ret_val);
+ goto out;
+ } else {
+ AICBT_DBG("%s: Receive acked frag num %d", __func__, evt_para->status);
+ ncmd--;
+ }
+ if (0 != evt_para->status) {
+ AICBT_ERR("%s: Receive acked frag num %d, err status %d",
+ __func__, ret_val, evt_para->status);
+ ret_val = -1;
+ goto out;
+ } else {
+ ret_val = 0;
+ }
+ }
+ ncmd = 0;
+ }
+
+out:
+ if (dst) {
+ vfree(dst);
+ dst = NULL;
+ }
+
+ printk("fw download complete\n\n");
+ return ret_val;
+
+}
+
+
+struct aicbt_info_t {
+ uint32_t btmode;
+ uint32_t btport;
+ uint32_t uart_baud;
+ uint32_t uart_flowctrl;
+ uint32_t lpm_enable;
+ uint32_t txpwr_lvl;
+};
+
+struct aicbsp_info_t {
+ int hwinfo;
+ uint32_t cpmode;
+};
+
+enum aicbsp_cpmode_type {
+ AICBSP_CPMODE_WORK,
+ AICBSP_CPMODE_TEST,
+};
+
+/* btmode
+ * used for force bt mode,if not AICBSP_MODE_NULL
+ * efuse valid and vendor_info will be invalid, even has beed set valid
+*/
+enum aicbt_btmode_type {
+ AICBT_BTMODE_BT_ONLY_SW = 0x0, // bt only mode with switch
+ AICBT_BTMODE_BT_WIFI_COMBO, // wifi/bt combo mode
+ AICBT_BTMODE_BT_ONLY, // bt only mode without switch
+ AICBT_BTMODE_BT_ONLY_TEST, // bt only test mode
+ AICBT_BTMODE_BT_WIFI_COMBO_TEST, // wifi/bt combo test mode
+ AICBT_MODE_NULL = 0xFF, // invalid value
+};
+
+enum aicbt_btport_type {
+ AICBT_BTPORT_NULL,
+ AICBT_BTPORT_MB,
+ AICBT_BTPORT_UART,
+};
+
+enum aicbt_uart_baud_type {
+ AICBT_UART_BAUD_115200 = 115200,
+ AICBT_UART_BAUD_921600 = 921600,
+ AICBT_UART_BAUD_1_5M = 1500000,
+ AICBT_UART_BAUD_3_25M = 3250000,
+};
+
+enum aicbt_uart_flowctrl_type {
+ AICBT_UART_FLOWCTRL_DISABLE = 0x0, // uart without flow ctrl
+ AICBT_UART_FLOWCTRL_ENABLE, // uart with flow ctrl
+};
+
+#define AICBSP_HWINFO_DEFAULT (-1)
+#define AICBSP_CPMODE_DEFAULT AICBSP_CPMODE_WORK
+#define AICBT_TXPWR_DFT 0x6F2F
+
+
+#define AICBT_BTMODE_DEFAULT AICBT_BTMODE_BT_WIFI_COMBO
+#define AICBT_BTPORT_DEFAULT AICBT_BTPORT_MB
+#define AICBT_UART_BAUD_DEFAULT AICBT_UART_BAUD_1_5M
+#define AICBT_UART_FC_DEFAULT AICBT_UART_FLOWCTRL_ENABLE
+#define AICBT_LPM_ENABLE_DEFAULT 0
+#define AICBT_TXPWR_LVL_DEFAULT AICBT_TXPWR_DFT
+
+struct aicbsp_info_t aicbsp_info = {
+ .hwinfo = AICBSP_HWINFO_DEFAULT,
+ .cpmode = AICBSP_CPMODE_DEFAULT,
+};
+
+#ifndef CONFIG_USE_FW_REQUEST
+#define FW_PATH_MAX 200
+
+char aic_fw_path[FW_PATH_MAX];
+#if (CONFIG_BLUEDROID == 0)
+static const char* aic_default_fw_path = "/lib/firmware/aic8800DC";
+#else
+static const char* aic_default_fw_path = "/vendor/etc/firmware";
+#endif
+#endif //CONFIG_USE_FW_REQUEST
+
+static struct aicbt_info_t aicbt_info = {
+ .btmode = AICBT_BTMODE_DEFAULT,
+ .btport = AICBT_BTPORT_DEFAULT,
+ .uart_baud = AICBT_UART_BAUD_DEFAULT,
+ .uart_flowctrl = AICBT_UART_FC_DEFAULT,
+ .lpm_enable = AICBT_LPM_ENABLE_DEFAULT,
+ .txpwr_lvl = AICBT_TXPWR_LVL_DEFAULT,
+};
+
+int patch_table_load(firmware_info *fw_info, struct aicbt_patch_table *_head)
+{
+ struct aicbt_patch_table *head, *p;
+ int i;
+ uint32_t *data = NULL;
+ struct aicbt_patch_table_cmd *patch_table_cmd = (struct aicbt_patch_table_cmd *)(fw_info->req_para);
+ struct fw_status *evt_para;
+ int ret_val = 0;
+ int ncmd = 1;
+ uint32_t len = 0;
+ uint32_t tot_len = 0;
+ head = _head;
+ for (p = head; p != NULL; p = p->next) {
+ data = p->data;
+ if(AICBT_PT_BTMODE == p->type){
+ *(data + 1) = aicbsp_info.hwinfo < 0;
+ *(data + 3) = aicbsp_info.hwinfo;
+ *(data + 5) = aicbsp_info.cpmode;
+
+ *(data + 7) = aicbt_info.btmode;
+ *(data + 9) = aicbt_info.btport;
+ *(data + 11) = aicbt_info.uart_baud;
+ *(data + 13) = aicbt_info.uart_flowctrl;
+ *(data + 15) = aicbt_info.lpm_enable;
+ *(data + 17) = aicbt_info.txpwr_lvl;
+
+ }
+ if (p->type == AICBT_PT_NULL || p->type == AICBT_PT_PWRON) {
+ continue;
+ }
+ if (p->type == AICBT_PT_VER) {
+ char *data_s = (char *)p->data;
+ printk("patch version %s\n", data_s);
+ continue;
+ }
+ if (p->len == 0) {
+ printk("len is 0\n");
+ continue;
+ }
+ tot_len = p->len;
+ while (tot_len) {
+ if (tot_len > HCI_PT_MAX_LEN) {
+ len = HCI_PT_MAX_LEN;
+ } else {
+ len = tot_len;
+ }
+ for (i = 0; i < len; i++) {
+ patch_table_cmd->patch_num = len;
+ memcpy(&patch_table_cmd->patch_table_addr[i], data, sizeof(uint32_t));
+ memcpy(&patch_table_cmd->patch_table_data[i], data + 1, sizeof(uint32_t));
+ printk("[%d] data: %08x %08x\n", i, patch_table_cmd->patch_table_addr[i],patch_table_cmd->patch_table_data[i]);
+ data += 2;
+ }
+ tot_len -= len;
+ evt_para = (struct fw_status *)fw_info->rsp_para;
+ //print_hex_dump(KERN_ERR,"data0:",DUMP_PREFIX_NONE,16,1,patch_table_cmd,sizeof(struct aicbt_patch_table_cmd),false);
+
+ //printk("patch num %x %d\n", patch_table_cmd->patch_num, sizeof(struct aicbt_patch_table_cmd));
+ fw_info->cmd_hdr->opcode = cpu_to_le16(HCI_VSC_UPDATE_PT_CMD);
+ fw_info->cmd_hdr->plen = HCI_VSC_UPDATE_PT_SIZE;
+ fw_info->pkt_len = fw_info->cmd_hdr->plen + 3;
+ AICBT_DBG("patch num 0x%x, plen 0x%x\n", patch_table_cmd->patch_num, fw_info->cmd_hdr->plen );
+ //print_hex_dump(KERN_ERR,"patch table:",DUMP_PREFIX_NONE,16,1,fw_info->send_pkt,32,false);
+ ret_val = send_hci_cmd(fw_info);
+ while (ncmd > 0) {
+ ret_val = rcv_hci_evt(fw_info);
+ if (ret_val < 0) {
+ AICBT_ERR("%s: rcv_hci_evt err %d", __func__, ret_val);
+ goto out;
+ } else {
+ AICBT_DBG("%s: Receive acked frag num %d", __func__, evt_para->status);
+ ncmd--;
+ }
+ if (0 != evt_para->status) {
+ AICBT_ERR("%s: Receive acked frag num %d, err status %d",
+ __func__, ret_val, evt_para->status);
+ ret_val = -1;
+ goto out;
+ }
+ }
+ ncmd = 1;
+ }
+ }
+out:
+ aicbt_patch_table_free(&head);
+ return ret_val;
+}
+
+int aic_load_firmware(u8 ** fw_buf, const char *name, struct device *device)
+{
+
+#ifdef CONFIG_USE_FW_REQUEST
+ const struct firmware *fw = NULL;
+ u32 *dst = NULL;
+ void *buffer=NULL;
+ int size = 0;
+ int ret = 0;
+
+ printk("%s: request firmware = %s \n", __func__ ,name);
+
+
+ ret = request_firmware(&fw, name, NULL);
+
+ if (ret < 0) {
+ printk("Load %s fail\n", name);
+ release_firmware(fw);
+ return -1;
+ }
+
+ size = fw->size;
+ dst = (u32 *)fw->data;
+
+ if (size <= 0) {
+ printk("wrong size of firmware file\n");
+ release_firmware(fw);
+ return -1;
+ }
+
+
+ buffer = vmalloc(size);
+ memset(buffer, 0, size);
+ memcpy(buffer, dst, size);
+
+ *fw_buf = buffer;
+
+ release_firmware(fw);
+
+ return size;
+
+#else
+ u8 *buffer=NULL;
+ char *path=NULL;
+ struct file *fp=NULL;
+ int size = 0, len=0;
+ ssize_t rdlen=0;
+
+ /* get the firmware path */
+ path = __getname();
+ if (!path){
+ *fw_buf=NULL;
+ return -1;
+ }
+
+ if (strlen(aic_fw_path) > 0) {
+ printk("%s: use customer define fw_path\n", __func__);
+ len = snprintf(path, FW_PATH_MAX, "%s/%s", aic_fw_path, name);
+ } else {
+ len = snprintf(path, FW_PATH_MAX, "%s/%s",aic_default_fw_path, name);
+ }
+
+ if (len >= FW_PATH_MAX) {
+ printk("%s: %s file's path too long\n", __func__, name);
+ *fw_buf=NULL;
+ __putname(path);
+ return -1;
+ }
+
+ printk("%s :firmware path = %s \n", __func__ ,path);
+
+
+ /* open the firmware file */
+ fp=filp_open(path, O_RDONLY, 0);
+ if(IS_ERR(fp) || (!fp)){
+ printk("%s: %s file failed to open\n", __func__, name);
+ if(IS_ERR(fp))
+ printk("is_Err\n");
+ if((!fp))
+ printk("null\n");
+ *fw_buf=NULL;
+ __putname(path);
+ fp=NULL;
+ return -1;
+ }
+
+ size = i_size_read(file_inode(fp));
+ if(size<=0){
+ printk("%s: %s file size invalid %d\n", __func__, name, size);
+ *fw_buf=NULL;
+ __putname(path);
+ filp_close(fp,NULL);
+ fp=NULL;
+ return -1;
+}
+
+ /* start to read from firmware file */
+ buffer = vmalloc(size);
+ memset(buffer, 0, size);
+ if(!buffer){
+ *fw_buf=NULL;
+ __putname(path);
+ filp_close(fp,NULL);
+ fp=NULL;
+ return -1;
+ }
+
+
+ #if LINUX_VERSION_CODE > KERNEL_VERSION(4, 13, 16)
+ rdlen = kernel_read(fp, buffer, size, &fp->f_pos);
+ #else
+ rdlen = kernel_read(fp, fp->f_pos, buffer, size);
+ #endif
+
+ if(size != rdlen){
+ printk("%s: %s file rdlen invalid %d %d\n", __func__, name, (int)rdlen, size);
+ *fw_buf=NULL;
+ __putname(path);
+ filp_close(fp,NULL);
+ fp=NULL;
+ vfree(buffer);
+ buffer=NULL;
+ return -1;
+ }
+ if(rdlen > 0){
+ fp->f_pos += rdlen;
+ //printk("f_pos=%d\n", (int)fp->f_pos);
+ }
+ *fw_buf = buffer;
+
+#if 0
+ MD5Init(&md5);
+ MD5Update(&md5, (unsigned char *)dst, size);
+ MD5Final(&md5, decrypt);
+
+ printk(MD5PINRT, MD5(decrypt));
+
+#endif
+ return size;
+#endif
+}
+
+int aicbt_patch_table_free(struct aicbt_patch_table **head)
+{
+ struct aicbt_patch_table *p = *head, *n = NULL;
+ while (p) {
+ n = p->next;
+ kfree(p->name);
+ kfree(p->data);
+ kfree(p);
+ p = n;
+ }
+ *head = NULL;
+ return 0;
+}
+
+int get_patch_addr_from_patch_table(firmware_info *fw_info, char *filename, uint32_t *fw_patch_base_addr)
+{
+ int size;
+ int ret = 0;
+ uint8_t *rawdata=NULL;
+ uint8_t *p = NULL;
+ uint32_t *data = NULL;
+ uint32_t type = 0, len = 0;
+ int j;
+
+ /* load aic firmware */
+ size = aic_load_firmware((u8 **)&rawdata, filename, NULL);
+
+ /* Copy the file on the Embedded side */
+ printk("### Upload %s fw_patch_table, size=%d\n", filename, size);
+
+ if (size <= 0) {
+ printk("wrong size of firmware file\n");
+ ret = -1;
+ goto err;
+ }
+
+ p = rawdata;
+
+ if (memcmp(p, AICBT_PT_TAG, sizeof(AICBT_PT_TAG) < 16 ? sizeof(AICBT_PT_TAG) : 16)) {
+ printk("TAG err\n");
+ ret = -1;
+ goto err;
+ }
+ p += 16;
+
+ while (p - rawdata < size) {
+ printk("size = %d p - rawdata = 0x%0lx \r\n", size, p - rawdata);
+ p += 16;
+
+ type = *(uint32_t *)p;
+ p += 4;
+
+ len = *(uint32_t *)p;
+ p += 4;
+ printk("cur->type %x, len %d\n", type, len);
+
+ if(type >= 1000 ) {//Temp Workaround
+ len = 0;
+ }else{
+ data = (uint32_t *)p;
+ if (type == AICBT_PT_NULL) {
+ *(fw_patch_base_addr) = *(data + 3);
+ printk("addr found %x\n", *(fw_patch_base_addr));
+ for (j = 0; j < len; j++) {
+ printk("addr %x\n", *(data+j));
+ }
+ break;
+ }
+ p += len * 8;
+ }
+ }
+
+ vfree(rawdata);
+ return ret;
+err:
+ //aicbt_patch_table_free(&head);
+
+ if (rawdata){
+ vfree(rawdata);
+ }
+ return ret;
+}
+
+
+
+int patch_table_download(firmware_info *fw_info, char *filename)
+{
+ struct aicbt_patch_table *head = NULL;
+ struct aicbt_patch_table *new = NULL;
+ struct aicbt_patch_table *cur = NULL;
+ int size;
+ int ret = 0;
+ uint8_t *rawdata=NULL;
+ uint8_t *p = NULL;
+
+ /* load aic firmware */
+ size = aic_load_firmware((u8 **)&rawdata, filename, NULL);
+
+ /* Copy the file on the Embedded side */
+ printk("### Upload %s fw_patch_table, size=%d\n", filename, size);
+
+ if (size <= 0) {
+ printk("wrong size of firmware file\n");
+ ret = -1;
+ goto err;
+ }
+
+ p = rawdata;
+
+ if (memcmp(p, AICBT_PT_TAG, sizeof(AICBT_PT_TAG) < 16 ? sizeof(AICBT_PT_TAG) : 16)) {
+ printk("TAG err\n");
+ ret = -1;
+ goto err;
+ }
+ p += 16;
+
+ while (p - rawdata < size) {
+ printk("size = %d p - rawdata = 0x%0lx \r\n", size, p - rawdata);
+ new = (struct aicbt_patch_table *)kmalloc(sizeof(struct aicbt_patch_table), GFP_KERNEL);
+ memset(new, 0, sizeof(struct aicbt_patch_table));
+ if (head == NULL) {
+ head = new;
+ cur = new;
+ } else {
+ cur->next = new;
+ cur = cur->next;
+ }
+
+ cur->name = (char *)kmalloc(sizeof(char) * 16, GFP_KERNEL);
+ memset(cur->name, 0, sizeof(char) * 16);
+ memcpy(cur->name, p, 16);
+ p += 16;
+
+ cur->type = *(uint32_t *)p;
+ p += 4;
+
+ cur->len = *(uint32_t *)p;
+ p += 4;
+ printk("cur->type %x, len %d\n", cur->type, cur->len);
+
+ if((cur->type ) >= 1000 ) {//Temp Workaround
+ cur->len = 0;
+ }else{
+ cur->data = (uint32_t *)kmalloc(sizeof(uint8_t) * cur->len * 8, GFP_KERNEL);
+ memset(cur->data, 0, sizeof(uint8_t) * cur->len * 8);
+ memcpy(cur->data, p, cur->len * 8);
+ p += cur->len * 8;
+ }
+ }
+
+ vfree(rawdata);
+ patch_table_load(fw_info, head);
+ printk("fw_patch_table download complete\n\n");
+
+ return ret;
+err:
+ //aicbt_patch_table_free(&head);
+
+ if (rawdata){
+ vfree(rawdata);
+ }
+ return ret;
+}
+
+
+int download_patch(firmware_info *fw_info, int cached)
+{
+ int ret_val = 0;
+
+ printk("%s: Download fw patch start, cached %d", __func__, cached);
+
+ if (!fw_info) {
+ printk("%s: No patch entry exists(fw_info %p)", __func__, fw_info);
+ ret_val = -1;
+ goto end;
+ }
+
+ ret_val = fw_config(fw_info);
+ if (ret_val) {
+ printk("%s: fw config failed %d", __func__, ret_val);
+ goto free;
+ }
+
+ ret_val = system_config(fw_info);
+ if (ret_val)
+ {
+ printk("%s: system config failed %d", __func__, ret_val);
+ goto free;
+ }
+
+ /*
+ * step1: check firmware statis
+ * step2: download firmware if updated
+ */
+
+
+ ret_val = check_fw_status(fw_info);
+
+
+ if (ret_val) {
+ #if 0
+ ret_val = download_data(fw_info, FW_RAM_ADID_BASE_ADDR, FW_ADID_BASE_NAME);
+ if (ret_val) {
+ printk("aic load adid fail %d\n", ret_val);
+ goto free;
+ }
+ #endif
+ if (sub_chip_id == 0) {
+ ret_val= download_data(fw_info, FW_RAM_PATCH_BASE_ADDR, FW_PATCH_BASE_NAME);
+ if (ret_val) {
+ printk("aic load patch fail %d\n", ret_val);
+ goto free;
+ }
+
+ ret_val= patch_table_download(fw_info, FW_PATCH_TABLE_NAME);
+ if (ret_val) {
+ printk("aic load patch ftable ail %d\n", ret_val);
+ goto free;
+ }
+ } else if (sub_chip_id == 1) {
+ uint32_t fw_ram_patch_base_addr = FW_RAM_PATCH_BASE_ADDR;
+
+ ret_val = get_patch_addr_from_patch_table(fw_info, FW_PATCH_TABLE_NAME_U02, &fw_ram_patch_base_addr);
+ if (ret_val)
+ {
+ printk("aic get patch addr fail %d\n", ret_val);
+ goto free;
+ }
+ printk("%s %x\n", __func__, fw_ram_patch_base_addr);
+ ret_val = download_data(fw_info, fw_ram_patch_base_addr, FW_PATCH_BASE_NAME_U02);
+ if (ret_val)
+ {
+ printk("aic load patch fail %d\n", ret_val);
+ goto free;
+ }
+
+ ret_val = patch_table_download(fw_info, FW_PATCH_TABLE_NAME_U02);
+ if (ret_val)
+ {
+ printk("aic load patch ftable ail %d\n", ret_val);
+ goto free;
+ }
+ } else if (sub_chip_id == 2) {
+ uint32_t fw_ram_patch_base_addr = FW_RAM_PATCH_BASE_ADDR;
+
+ ret_val = get_patch_addr_from_patch_table(fw_info, FW_PATCH_TABLE_NAME_U02H, &fw_ram_patch_base_addr);
+ if (ret_val)
+ {
+ printk("aic get patch addr fail %d\n", ret_val);
+ goto free;
+ }
+ printk("U02H %s %x\n", __func__, fw_ram_patch_base_addr);
+ ret_val = download_data(fw_info, fw_ram_patch_base_addr, FW_PATCH_BASE_NAME_U02H);
+ if (ret_val)
+ {
+ printk("aic load patch fail %d\n", ret_val);
+ goto free;
+ }
+
+ ret_val = patch_table_download(fw_info, FW_PATCH_TABLE_NAME_U02H);
+ if (ret_val)
+ {
+ printk("aic load patch ftable ail %d\n", ret_val);
+ goto free;
+ }
+ } else {
+ printk("%s unsupported sub_chip_id %x\n", __func__, sub_chip_id);
+ }
+ }
+
+free:
+ /* Free fw data after download finished */
+ kfree(fw_info->fw_data);
+ fw_info->fw_data = NULL;
+
+end:
+ return ret_val;
+}
+
+//for 8800dc end
+
+firmware_info *firmware_info_init(struct usb_interface *intf)
+{
+ struct usb_device *udev = interface_to_usbdev(intf);
+ firmware_info *fw_info;
+
+ AICBT_DBG("%s: start", __func__);
+
+ fw_info = kzalloc(sizeof(*fw_info), GFP_KERNEL);
+ if (!fw_info)
+ return NULL;
+
+ fw_info->send_pkt = kzalloc(SEND_PKT_LEN, GFP_KERNEL);
+ if (!fw_info->send_pkt) {
+ kfree(fw_info);
+ return NULL;
+ }
+
+ fw_info->rcv_pkt = kzalloc(RCV_PKT_LEN, GFP_KERNEL);
+ if (!fw_info->rcv_pkt) {
+ kfree(fw_info->send_pkt);
+ kfree(fw_info);
+ return NULL;
+ }
+
+ fw_info->intf = intf;
+ fw_info->udev = udev;
+if(g_chipid == PRODUCT_ID_AIC8801 || g_chipid == PRODUCT_ID_AIC8800D80){
+ fw_info->pipe_in = usb_rcvbulkpipe(fw_info->udev, BULK_EP);
+ fw_info->pipe_out = usb_rcvbulkpipe(fw_info->udev, CTRL_EP);
+}else if(g_chipid == PRODUCT_ID_AIC8800DC){
+ fw_info->pipe_in = usb_rcvintpipe(fw_info->udev, INTR_EP);
+ fw_info->pipe_out = usb_sndctrlpipe(fw_info->udev, CTRL_EP);
+}
+ fw_info->cmd_hdr = (struct hci_command_hdr *)(fw_info->send_pkt);
+ fw_info->evt_hdr = (struct hci_event_hdr *)(fw_info->rcv_pkt);
+ fw_info->cmd_cmp = (struct hci_ev_cmd_complete *)(fw_info->rcv_pkt + EVT_HDR_LEN);
+ fw_info->req_para = fw_info->send_pkt + CMD_HDR_LEN;
+ fw_info->rsp_para = fw_info->rcv_pkt + EVT_HDR_LEN + CMD_CMP_LEN;
+
+#if BTUSB_RPM
+ AICBT_INFO("%s: Auto suspend is enabled", __func__);
+ usb_enable_autosuspend(udev);
+ pm_runtime_set_autosuspend_delay(&(udev->dev), 2000);
+#else
+ AICBT_INFO("%s: Auto suspend is disabled", __func__);
+ usb_disable_autosuspend(udev);
+#endif
+
+#if BTUSB_WAKEUP_HOST
+ device_wakeup_enable(&udev->dev);
+#endif
+
+ return fw_info;
+}
+
+
+void firmware_info_destroy(struct usb_interface *intf)
+{
+ firmware_info *fw_info;
+ struct usb_device *udev;
+ struct btusb_data *data;
+
+ udev = interface_to_usbdev(intf);
+ data = usb_get_intfdata(intf);
+
+ fw_info = data->fw_info;
+ if (!fw_info)
+ return;
+
+#if BTUSB_RPM
+ usb_disable_autosuspend(udev);
+#endif
+
+ /*
+ * In order to reclaim fw data mem, we free fw_data immediately
+ * after download patch finished instead of here.
+ */
+ kfree(fw_info->rcv_pkt);
+ kfree(fw_info->send_pkt);
+ kfree(fw_info);
+
+
+}
+
+static struct usb_driver btusb_driver;
+
+static struct usb_device_id btusb_table[] = {
+ #if 0
+ { .match_flags = USB_DEVICE_ID_MATCH_VENDOR |
+ USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0xa69d,
+ .bInterfaceClass = 0xe0,
+ .bInterfaceSubClass = 0x01,
+ .bInterfaceProtocol = 0x01 },
+ #endif
+ {USB_DEVICE_AND_INTERFACE_INFO(USB_VENDOR_ID_AIC, USB_PRODUCT_ID_AIC8801, 0xe0, 0x01,0x01)},
+ {USB_DEVICE_AND_INTERFACE_INFO(USB_VENDOR_ID_AIC, USB_PRODUCT_ID_AIC8800D80, 0xe0, 0x01,0x01)},
+ {USB_DEVICE_AND_INTERFACE_INFO(USB_VENDOR_ID_AIC, USB_PRODUCT_ID_AIC8800DC, 0xe0, 0x01,0x01)},
+ {}
+};
+
+MODULE_DEVICE_TABLE(usb, btusb_table);
+
+static int inc_tx(struct btusb_data *data)
+{
+ unsigned long flags;
+ int rv;
+
+ spin_lock_irqsave(&data->txlock, flags);
+ rv = test_bit(BTUSB_SUSPENDING, &data->flags);
+ if (!rv)
+ data->tx_in_flight++;
+ spin_unlock_irqrestore(&data->txlock, flags);
+
+ return rv;
+}
+
+void check_sco_event(struct urb *urb)
+{
+ u8* opcode = (u8*)(urb->transfer_buffer);
+ u8 status;
+ static uint16_t sco_handle = 0;
+ uint16_t handle;
+ u8 air_mode = 0;
+ struct hci_dev *hdev = urb->context;
+#ifdef CONFIG_SCO_OVER_HCI
+ struct btusb_data *data = GET_DRV_DATA(hdev);
+ AIC_sco_card_t *pSCOSnd = data->pSCOSnd;
+#endif
+
+ switch (*opcode) {
+ case HCI_EV_SYNC_CONN_COMPLETE:
+ AICBT_INFO("%s: HCI_EV_SYNC_CONN_COMPLETE(0x%02x)", __func__, *opcode);
+ status = *(opcode + 2);
+ sco_handle = *(opcode + 3) | *(opcode + 4) << 8;
+ air_mode = *(opcode + 18);
+ printk("%s status:%d,air_mode:%d \r\n", __func__, status,air_mode);
+ if (status == 0) {
+ hdev->conn_hash.sco_num++;
+ hdev->notify(hdev, 0);
+ //schedule_work(&data->work);
+ if (air_mode == 0x03) {
+ set_select_msbc(CODEC_MSBC);
+ }
+ }
+ break;
+ case HCI_EV_DISCONN_COMPLETE:
+ AICBT_INFO("%s: HCI_EV_DISCONN_COMPLETE(0x%02x)", __func__, *opcode);
+ status = *(opcode + 2);
+ handle = *(opcode + 3) | *(opcode + 4) << 8;
+ if (status == 0 && sco_handle == handle) {
+ hdev->conn_hash.sco_num--;
+ hdev->notify(hdev, 0);
+ set_select_msbc(CODEC_CVSD);
+ //schedule_work(&data->work);
+#ifdef CONFIG_SCO_OVER_HCI
+ if (test_bit(ALSA_CAPTURE_RUNNING, &pSCOSnd->states)) {
+ mod_timer(&snd_cap_timer.cap_timer,jiffies + msecs_to_jiffies(3));
+ }
+#endif
+ }
+ break;
+ default:
+ AICBT_DBG("%s: event 0x%02x", __func__, *opcode);
+ break;
+ }
+}
+
+#if (CONFIG_BLUEDROID == 0)
+#if HCI_VERSION_CODE >= KERNEL_VERSION(3, 18, 0)
+static inline void btusb_free_frags(struct btusb_data *data)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&data->rxlock, flags);
+
+ kfree_skb(data->evt_skb);
+ data->evt_skb = NULL;
+
+ kfree_skb(data->acl_skb);
+ data->acl_skb = NULL;
+
+ kfree_skb(data->sco_skb);
+ data->sco_skb = NULL;
+
+ spin_unlock_irqrestore(&data->rxlock, flags);
+}
+
+static int btusb_recv_intr(struct btusb_data *data, void *buffer, int count)
+{
+ struct sk_buff *skb;
+ int err = 0;
+
+ spin_lock(&data->rxlock);
+ skb = data->evt_skb;
+ //printk("%s count %d\n", __func__, count);
+
+#if 1
+ while (count) {
+ int len;
+
+ if (!skb) {
+ skb = bt_skb_alloc(HCI_MAX_EVENT_SIZE, GFP_ATOMIC);
+ if (!skb) {
+ err = -ENOMEM;
+ break;
+ }
+
+ bt_cb(skb)->pkt_type = HCI_EVENT_PKT;
+ bt_cb(skb)->expect = HCI_EVENT_HDR_SIZE;
+ }
+
+ len = min_t(uint, bt_cb(skb)->expect, count);
+ memcpy(skb_put(skb, len), buffer, len);
+
+ count -= len;
+ buffer += len;
+ bt_cb(skb)->expect -= len;
+
+ if (skb->len == HCI_EVENT_HDR_SIZE) {
+ /* Complete event header */
+ bt_cb(skb)->expect = hci_event_hdr(skb)->plen;
+
+ if (skb_tailroom(skb) < bt_cb(skb)->expect) {
+ kfree_skb(skb);
+ skb = NULL;
+
+ err = -EILSEQ;
+ break;
+ }
+ }
+
+ if (bt_cb(skb)->expect == 0) {
+ /* Complete frame */
+ hci_recv_frame(data->hdev, skb);
+ skb = NULL;
+ }
+ }
+#endif
+
+ data->evt_skb = skb;
+ spin_unlock(&data->rxlock);
+
+ return err;
+}
+
+static int btusb_recv_bulk(struct btusb_data *data, void *buffer, int count)
+{
+ struct sk_buff *skb;
+ int err = 0;
+
+ spin_lock(&data->rxlock);
+ skb = data->acl_skb;
+
+ while (count) {
+ int len;
+
+ if (!skb) {
+ skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC);
+ if (!skb) {
+ err = -ENOMEM;
+ break;
+ }
+
+ bt_cb(skb)->pkt_type = HCI_ACLDATA_PKT;
+ bt_cb(skb)->expect = HCI_ACL_HDR_SIZE;
+ }
+
+ len = min_t(uint, bt_cb(skb)->expect, count);
+ memcpy(skb_put(skb, len), buffer, len);
+
+ count -= len;
+ buffer += len;
+ bt_cb(skb)->expect -= len;
+
+ if (skb->len == HCI_ACL_HDR_SIZE) {
+ __le16 dlen = hci_acl_hdr(skb)->dlen;
+
+ /* Complete ACL header */
+ bt_cb(skb)->expect = __le16_to_cpu(dlen);
+
+ if (skb_tailroom(skb) < bt_cb(skb)->expect) {
+ kfree_skb(skb);
+ skb = NULL;
+
+ err = -EILSEQ;
+ break;
+ }
+ }
+
+ if (bt_cb(skb)->expect == 0) {
+ /* Complete frame */
+ hci_recv_frame(data->hdev, skb);
+ skb = NULL;
+ }
+ }
+
+ data->acl_skb = skb;
+ spin_unlock(&data->rxlock);
+
+ return err;
+}
+
+static int btusb_recv_isoc(struct btusb_data *data, void *buffer, int count)
+{
+ struct sk_buff *skb;
+ int err = 0;
+
+ spin_lock(&data->rxlock);
+ skb = data->sco_skb;
+
+ while (count) {
+ int len;
+
+ if (!skb) {
+ skb = bt_skb_alloc(HCI_MAX_SCO_SIZE, GFP_ATOMIC);
+ if (!skb) {
+ err = -ENOMEM;
+ break;
+ }
+
+ bt_cb(skb)->pkt_type = HCI_SCODATA_PKT;
+ bt_cb(skb)->expect = HCI_SCO_HDR_SIZE;
+ }
+
+ len = min_t(uint, bt_cb(skb)->expect, count);
+ memcpy(skb_put(skb, len), buffer, len);
+
+ count -= len;
+ buffer += len;
+ bt_cb(skb)->expect -= len;
+
+ if (skb->len == HCI_SCO_HDR_SIZE) {
+ /* Complete SCO header */
+ bt_cb(skb)->expect = hci_sco_hdr(skb)->dlen;
+
+ if (skb_tailroom(skb) < bt_cb(skb)->expect) {
+ kfree_skb(skb);
+ skb = NULL;
+
+ err = -EILSEQ;
+ break;
+ }
+ }
+
+ if (bt_cb(skb)->expect == 0) {
+ /* Complete frame */
+ hci_recv_frame(data->hdev, skb);
+ skb = NULL;
+ }
+ }
+
+ data->sco_skb = skb;
+ spin_unlock(&data->rxlock);
+
+ return err;
+}
+#endif
+#endif // (CONFIG_BLUEDROID == 0)
+
+
+static void btusb_intr_complete(struct urb *urb)
+{
+ struct hci_dev *hdev = urb->context;
+ struct btusb_data *data = GET_DRV_DATA(hdev);
+ int err;
+
+ AICBT_DBG("%s: urb %p status %d count %d ", __func__,
+ urb, urb->status, urb->actual_length);
+
+ if (!test_bit(HCI_RUNNING, &hdev->flags)) {
+ printk("%s return \n", __func__);
+ return;
+ }
+ if (urb->status == 0) {
+ hdev->stat.byte_rx += urb->actual_length;
+
+#if (CONFIG_BLUEDROID) || (HCI_VERSION_CODE < KERNEL_VERSION(3, 18, 0))
+ if (hci_recv_fragment(hdev, HCI_EVENT_PKT,
+ urb->transfer_buffer,
+ urb->actual_length) < 0) {
+ AICBT_ERR("%s: Corrupted event packet", __func__);
+ hdev->stat.err_rx++;
+ }
+#else
+ if (btusb_recv_intr(data, urb->transfer_buffer,
+ urb->actual_length) < 0) {
+ AICBT_ERR("%s corrupted event packet", hdev->name);
+ hdev->stat.err_rx++;
+ }
+#endif
+
+#ifdef CONFIG_SCO_OVER_HCI
+ check_sco_event(urb);
+#endif
+#ifdef CONFIG_USB_AIC_UART_SCO_DRIVER
+ check_sco_event(urb);
+#endif
+
+ }
+ /* Avoid suspend failed when usb_kill_urb */
+ else if(urb->status == -ENOENT) {
+ return;
+ }
+
+
+ if (!test_bit(BTUSB_INTR_RUNNING, &data->flags))
+ return;
+
+ usb_mark_last_busy(data->udev);
+ usb_anchor_urb(urb, &data->intr_anchor);
+
+ err = usb_submit_urb(urb, GFP_ATOMIC);
+ if (err < 0) {
+ if (err != -EPERM && err != -ENODEV)
+ AICBT_ERR("%s: Failed to re-submit urb %p, err %d",
+ __func__, urb, err);
+ usb_unanchor_urb(urb);
+ }
+}
+
+static int btusb_submit_intr_urb(struct hci_dev *hdev, gfp_t mem_flags)
+{
+ struct btusb_data *data = GET_DRV_DATA(hdev);
+ struct urb *urb;
+ unsigned char *buf;
+ unsigned int pipe;
+ int err, size;
+
+ if (!data->intr_ep)
+ return -ENODEV;
+
+ urb = usb_alloc_urb(0, mem_flags);
+ if (!urb)
+ return -ENOMEM;
+
+ size = le16_to_cpu(data->intr_ep->wMaxPacketSize);
+
+ buf = kmalloc(size, mem_flags);
+ if (!buf) {
+ usb_free_urb(urb);
+ return -ENOMEM;
+ }
+
+ AICBT_DBG("%s: mMaxPacketSize %d, bEndpointAddress 0x%02x",
+ __func__, size, data->intr_ep->bEndpointAddress);
+
+ pipe = usb_rcvintpipe(data->udev, data->intr_ep->bEndpointAddress);
+
+ usb_fill_int_urb(urb, data->udev, pipe, buf, size,
+ btusb_intr_complete, hdev,
+ data->intr_ep->bInterval);
+
+ urb->transfer_flags |= URB_FREE_BUFFER;
+
+ usb_anchor_urb(urb, &data->intr_anchor);
+
+ err = usb_submit_urb(urb, mem_flags);
+ if (err < 0) {
+ AICBT_ERR("%s: Failed to submit urb %p, err %d",
+ __func__, urb, err);
+ usb_unanchor_urb(urb);
+ }
+
+ usb_free_urb(urb);
+
+ return err;
+}
+
+static void btusb_bulk_complete(struct urb *urb)
+{
+ struct hci_dev *hdev = urb->context;
+ struct btusb_data *data = GET_DRV_DATA(hdev);
+ int err;
+
+ AICBT_DBG("%s: urb %p status %d count %d",
+ __func__, urb, urb->status, urb->actual_length);
+
+ if (!test_bit(HCI_RUNNING, &hdev->flags)) {
+ printk("%s HCI_RUNNING\n", __func__);
+ return;
+ }
+ if (urb->status == 0) {
+ hdev->stat.byte_rx += urb->actual_length;
+
+#if (CONFIG_BLUEDROID) || (HCI_VERSION_CODE < KERNEL_VERSION(3, 18, 0))
+ if (hci_recv_fragment(hdev, HCI_ACLDATA_PKT,
+ urb->transfer_buffer,
+ urb->actual_length) < 0) {
+ AICBT_ERR("%s: Corrupted ACL packet", __func__);
+ hdev->stat.err_rx++;
+ }
+#else
+ if (data->recv_bulk(data, urb->transfer_buffer,
+ urb->actual_length) < 0) {
+ AICBT_ERR("%s Corrupted ACL packet", hdev->name);
+ hdev->stat.err_rx++;
+ }
+#endif
+
+ }
+ /* Avoid suspend failed when usb_kill_urb */
+ else if(urb->status == -ENOENT) {
+ printk("%s ENOENT\n", __func__);
+ return;
+ }
+ AICBT_DBG("%s: OUT", __func__);
+
+ if (!test_bit(BTUSB_BULK_RUNNING, &data->flags)) {
+ printk("%s BTUSB_BULK_RUNNING\n", __func__);
+ return;
+ }
+ usb_anchor_urb(urb, &data->bulk_anchor);
+ usb_mark_last_busy(data->udev);
+
+ //printk("LIULI bulk submit\n");
+ err = usb_submit_urb(urb, GFP_ATOMIC);
+ if (err < 0) {
+ /* -EPERM: urb is being killed;
+ * -ENODEV: device got disconnected */
+ if (err != -EPERM && err != -ENODEV)
+ AICBT_ERR("btusb_bulk_complete %s urb %p failed to resubmit (%d)",
+ hdev->name, urb, -err);
+ usb_unanchor_urb(urb);
+ }
+}
+
+static int btusb_submit_bulk_urb(struct hci_dev *hdev, gfp_t mem_flags)
+{
+ struct btusb_data *data = GET_DRV_DATA(hdev);
+ struct urb *urb;
+ unsigned char *buf;
+ unsigned int pipe;
+ int err, size = HCI_MAX_FRAME_SIZE;
+
+ AICBT_DBG("%s: hdev name %s", __func__, hdev->name);
+ AICBT_DBG("%s: mMaxPacketSize %d, bEndpointAddress 0x%02x",
+ __func__, size, data->bulk_rx_ep->bEndpointAddress);
+
+ if (!data->bulk_rx_ep)
+ return -ENODEV;
+
+ urb = usb_alloc_urb(0, mem_flags);
+ if (!urb)
+ return -ENOMEM;
+
+ buf = kmalloc(size, mem_flags);
+ if (!buf) {
+ usb_free_urb(urb);
+ return -ENOMEM;
+ }
+
+ pipe = usb_rcvbulkpipe(data->udev, data->bulk_rx_ep->bEndpointAddress);
+
+ usb_fill_bulk_urb(urb, data->udev, pipe,
+ buf, size, btusb_bulk_complete, hdev);
+
+ urb->transfer_flags |= URB_FREE_BUFFER;
+
+ usb_mark_last_busy(data->udev);
+ usb_anchor_urb(urb, &data->bulk_anchor);
+
+ err = usb_submit_urb(urb, mem_flags);
+ if (err < 0) {
+ AICBT_ERR("%s: Failed to submit urb %p, err %d", __func__, urb, err);
+ usb_unanchor_urb(urb);
+ }
+
+ usb_free_urb(urb);
+
+ return err;
+}
+
+static void btusb_isoc_complete(struct urb *urb)
+{
+ struct hci_dev *hdev = urb->context;
+ struct btusb_data *data = GET_DRV_DATA(hdev);
+ int i, err;
+ unsigned int total_length = 0;
+
+ AICBT_DBG("%s: urb %p status %d count %d",
+ __func__, urb, urb->status, urb->actual_length);
+
+ if (!test_bit(HCI_RUNNING, &hdev->flags))
+ return;
+
+ if (urb->status == 0) {
+ for (i = 0; i < urb->number_of_packets; i++) {
+ unsigned int offset = urb->iso_frame_desc[i].offset;
+ unsigned int length = urb->iso_frame_desc[i].actual_length;
+ //u8 *data = (u8 *)(urb->transfer_buffer + offset);
+ //AICBT_DBG("%d,%d ,%x,%x,%x s %d.",
+ //offset, length, data[0], data[1],data[2],urb->iso_frame_desc[i].status);
+
+ if(total_length >= urb->actual_length){
+ AICBT_ERR("total_len >= actual_length ,return");
+ break;
+ }
+ total_length += length;
+
+ if (urb->iso_frame_desc[i].status)
+ continue;
+
+ hdev->stat.byte_rx += length;
+ if(length){
+#if (CONFIG_BLUEDROID) || (HCI_VERSION_CODE < KERNEL_VERSION(3, 18, 0))
+ if (hci_recv_fragment(hdev, HCI_SCODATA_PKT,
+ urb->transfer_buffer + offset,
+ length) < 0) {
+ AICBT_ERR("%s: Corrupted SCO packet", __func__);
+ hdev->stat.err_rx++;
+ }
+#else
+ if (btusb_recv_isoc(data, urb->transfer_buffer + offset,
+ length) < 0) {
+ AICBT_ERR("%s corrupted SCO packet",
+ hdev->name);
+ hdev->stat.err_rx++;
+ }
+#endif
+
+ }
+ }
+ }
+ /* Avoid suspend failed when usb_kill_urb */
+ else if(urb->status == -ENOENT) {
+ return;
+ }
+
+
+ if (!test_bit(BTUSB_ISOC_RUNNING, &data->flags))
+ return;
+
+ usb_anchor_urb(urb, &data->isoc_anchor);
+ i = 0;
+retry:
+ err = usb_submit_urb(urb, GFP_ATOMIC);
+ if (err < 0) {
+ /* -EPERM: urb is being killed;
+ * -ENODEV: device got disconnected */
+ if (err != -EPERM && err != -ENODEV)
+ AICBT_ERR("%s: Failed to re-sumbit urb %p, retry %d, err %d",
+ __func__, urb, i, err);
+ if (i < 10) {
+ i++;
+ mdelay(1);
+ goto retry;
+ }
+
+ usb_unanchor_urb(urb);
+ }
+}
+
+static inline void fill_isoc_descriptor(struct urb *urb, int len, int mtu)
+{
+ int i, offset = 0;
+
+ AICBT_DBG("%s: len %d mtu %d", __func__, len, mtu);
+
+ for (i = 0; i < BTUSB_MAX_ISOC_FRAMES && len >= mtu;
+ i++, offset += mtu, len -= mtu) {
+ urb->iso_frame_desc[i].offset = offset;
+ urb->iso_frame_desc[i].length = mtu;
+ }
+
+ if (len && i < BTUSB_MAX_ISOC_FRAMES) {
+ urb->iso_frame_desc[i].offset = offset;
+ urb->iso_frame_desc[i].length = len;
+ i++;
+ }
+
+ urb->number_of_packets = i;
+}
+
+static int btusb_submit_isoc_urb(struct hci_dev *hdev, gfp_t mem_flags)
+{
+ struct btusb_data *data = GET_DRV_DATA(hdev);
+ struct urb *urb;
+ unsigned char *buf;
+ unsigned int pipe;
+ int err, size;
+ int interval;
+
+ if (!data->isoc_rx_ep)
+ return -ENODEV;
+ AICBT_DBG("%s: mMaxPacketSize %d, bEndpointAddress 0x%02x",
+ __func__, size, data->isoc_rx_ep->bEndpointAddress);
+
+ urb = usb_alloc_urb(BTUSB_MAX_ISOC_FRAMES, mem_flags);
+ if (!urb)
+ return -ENOMEM;
+
+ size = le16_to_cpu(data->isoc_rx_ep->wMaxPacketSize) *
+ BTUSB_MAX_ISOC_FRAMES;
+
+ buf = kmalloc(size, mem_flags);
+ if (!buf) {
+ usb_free_urb(urb);
+ return -ENOMEM;
+ }
+
+ pipe = usb_rcvisocpipe(data->udev, data->isoc_rx_ep->bEndpointAddress);
+
+ urb->dev = data->udev;
+ urb->pipe = pipe;
+ urb->context = hdev;
+ urb->complete = btusb_isoc_complete;
+ if (urb->dev->speed == USB_SPEED_HIGH || urb->dev->speed >= USB_SPEED_SUPER) {
+ /* make sure interval is within allowed range */
+ interval = clamp((int)data->isoc_rx_ep->bInterval, 1, 16);
+ urb->interval = 1 << (interval - 1);
+ } else {
+ urb->interval = data->isoc_rx_ep->bInterval;
+ }
+
+ AICBT_INFO("urb->interval %d \r\n", urb->interval);
+
+ urb->transfer_flags = URB_FREE_BUFFER | URB_ISO_ASAP;
+ urb->transfer_buffer = buf;
+ urb->transfer_buffer_length = size;
+
+ fill_isoc_descriptor(urb, size,
+ le16_to_cpu(data->isoc_rx_ep->wMaxPacketSize));
+
+ usb_anchor_urb(urb, &data->isoc_anchor);
+
+ err = usb_submit_urb(urb, mem_flags);
+ if (err < 0) {
+ AICBT_ERR("%s: Failed to submit urb %p, err %d", __func__, urb, err);
+ usb_unanchor_urb(urb);
+ }
+
+ usb_free_urb(urb);
+
+ return err;
+}
+
+static void btusb_tx_complete(struct urb *urb)
+{
+ struct sk_buff *skb = urb->context;
+ struct hci_dev *hdev = (struct hci_dev *) skb->dev;
+ struct btusb_data *data = GET_DRV_DATA(hdev);
+
+ if (!test_bit(HCI_RUNNING, &hdev->flags))
+ goto done;
+
+ if (!urb->status)
+ hdev->stat.byte_tx += urb->transfer_buffer_length;
+ else
+ hdev->stat.err_tx++;
+
+done:
+ spin_lock(&data->txlock);
+ data->tx_in_flight--;
+ spin_unlock(&data->txlock);
+
+ kfree(urb->setup_packet);
+
+ kfree_skb(skb);
+}
+
+static void btusb_isoc_tx_complete(struct urb *urb)
+{
+ struct sk_buff *skb = urb->context;
+ struct hci_dev *hdev = (struct hci_dev *) skb->dev;
+
+ AICBT_DBG("%s: urb %p status %d count %d",
+ __func__, urb, urb->status, urb->actual_length);
+
+ if (skb && hdev) {
+ if (!test_bit(HCI_RUNNING, &hdev->flags))
+ goto done;
+
+ if (!urb->status)
+ hdev->stat.byte_tx += urb->transfer_buffer_length;
+ else
+ hdev->stat.err_tx++;
+ } else
+ AICBT_ERR("%s: skb 0x%p hdev 0x%p", __func__, skb, hdev);
+
+done:
+ kfree(urb->setup_packet);
+
+ kfree_skb(skb);
+}
+
+#if (CONFIG_BLUEDROID == 0)
+#if LINUX_VERSION_CODE > KERNEL_VERSION(4, 0, 9)
+static int btusb_shutdown(struct hci_dev *hdev)
+{
+ struct sk_buff *skb;
+ printk("aic %s\n", __func__);
+
+ skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT);
+ if (IS_ERR(skb)) {
+ printk("HCI reset during shutdown failed\n");
+ return PTR_ERR(skb);
+ }
+ kfree_skb(skb);
+
+ return 0;
+}
+#endif
+#endif //(CONFIG_BLUEDROID == 0)
+
+static int btusb_open(struct hci_dev *hdev)
+{
+ struct btusb_data *data = GET_DRV_DATA(hdev);
+ int err = 0;
+
+ AICBT_INFO("%s: Start", __func__);
+
+ err = usb_autopm_get_interface(data->intf);
+ if (err < 0)
+ return err;
+
+ data->intf->needs_remote_wakeup = 1;
+
+#if (CONFIG_BLUEDROID == 0)
+ //err = download_patch(data->fw_info,1);
+ printk(" download_patch %d", err);
+ if (err < 0) {
+ goto failed;
+ }
+#endif
+
+
+ if (test_and_set_bit(HCI_RUNNING, &hdev->flags)){
+ goto done;
+ }
+
+ if (test_and_set_bit(BTUSB_INTR_RUNNING, &data->flags)){
+ goto done;
+ }
+
+ err = btusb_submit_intr_urb(hdev, GFP_KERNEL);
+ if (err < 0)
+ goto failed;
+
+ err = btusb_submit_bulk_urb(hdev, GFP_KERNEL);
+ if (err < 0) {
+ mdelay(URB_CANCELING_DELAY_MS);
+ usb_kill_anchored_urbs(&data->intr_anchor);
+ goto failed;
+ }
+
+ set_bit(BTUSB_BULK_RUNNING, &data->flags);
+ btusb_submit_bulk_urb(hdev, GFP_KERNEL);
+
+done:
+ usb_autopm_put_interface(data->intf);
+ AICBT_INFO("%s: End", __func__);
+ return 0;
+
+failed:
+ clear_bit(BTUSB_INTR_RUNNING, &data->flags);
+ clear_bit(HCI_RUNNING, &hdev->flags);
+ usb_autopm_put_interface(data->intf);
+ AICBT_ERR("%s: Failed", __func__);
+ return err;
+}
+
+static void btusb_stop_traffic(struct btusb_data *data)
+{
+ mdelay(URB_CANCELING_DELAY_MS);
+ usb_kill_anchored_urbs(&data->intr_anchor);
+ usb_kill_anchored_urbs(&data->bulk_anchor);
+ usb_kill_anchored_urbs(&data->isoc_anchor);
+}
+
+static int btusb_close(struct hci_dev *hdev)
+{
+ struct btusb_data *data = GET_DRV_DATA(hdev);
+#if (CONFIG_BLUEDROID) || (HCI_VERSION_CODE < KERNEL_VERSION(4, 1, 0))
+ int i;
+#endif
+ int err;
+
+ AICBT_INFO("%s: hci running %lu", __func__, hdev->flags & HCI_RUNNING);
+
+ if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags)){
+ return 0;
+ }
+
+ if (!test_and_clear_bit(BTUSB_INTR_RUNNING, &data->flags)){
+ return 0;
+ }
+
+#if (CONFIG_BLUEDROID) || (HCI_VERSION_CODE < KERNEL_VERSION(4, 1, 0))
+ for (i = 0; i < NUM_REASSEMBLY; i++) {
+ if (hdev->reassembly[i]) {
+ AICBT_DBG("%s: free ressembly[%d]", __func__, i);
+ kfree_skb(hdev->reassembly[i]);
+ hdev->reassembly[i] = NULL;
+ }
+ }
+#endif
+
+ cancel_work_sync(&data->work);
+ cancel_work_sync(&data->waker);
+
+ clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
+ clear_bit(BTUSB_BULK_RUNNING, &data->flags);
+ clear_bit(BTUSB_INTR_RUNNING, &data->flags);
+
+ btusb_stop_traffic(data);
+ err = usb_autopm_get_interface(data->intf);
+ if (err < 0)
+ goto failed;
+
+ data->intf->needs_remote_wakeup = 0;
+ usb_autopm_put_interface(data->intf);
+
+failed:
+ mdelay(URB_CANCELING_DELAY_MS);
+ usb_scuttle_anchored_urbs(&data->deferred);
+ return 0;
+}
+
+static int btusb_flush(struct hci_dev *hdev)
+{
+ struct btusb_data *data = GET_DRV_DATA(hdev);
+
+ AICBT_DBG("%s", __func__);
+
+ mdelay(URB_CANCELING_DELAY_MS);
+ usb_kill_anchored_urbs(&data->tx_anchor);
+
+ return 0;
+}
+
+#ifdef CONFIG_SCO_OVER_HCI
+static void btusb_isoc_snd_tx_complete(struct urb *urb);
+
+static int snd_send_sco_frame(struct sk_buff *skb)
+{
+ struct hci_dev *hdev = (struct hci_dev *) skb->dev;
+
+ struct btusb_data *data = GET_DRV_DATA(hdev);
+ //struct usb_ctrlrequest *dr;
+ struct urb *urb;
+ unsigned int pipe;
+ int err;
+
+ AICBT_DBG("%s:pkt type %d, packet_len : %d",
+ __func__,bt_cb(skb)->pkt_type, skb->len);
+
+ if (!hdev && !test_bit(HCI_RUNNING, &hdev->flags))
+ return -EBUSY;
+
+ if (!data->isoc_tx_ep || hdev->conn_hash.sco_num < 1) {
+ kfree(skb);
+ return -ENODEV;
+ }
+
+ urb = usb_alloc_urb(BTUSB_MAX_ISOC_FRAMES, GFP_ATOMIC);
+ if (!urb) {
+ AICBT_ERR("%s: Failed to allocate mem for sco pkts", __func__);
+ kfree(skb);
+ return -ENOMEM;
+ }
+
+ pipe = usb_sndisocpipe(data->udev, data->isoc_tx_ep->bEndpointAddress);
+
+ usb_fill_int_urb(urb, data->udev, pipe,
+ skb->data, skb->len, btusb_isoc_snd_tx_complete,
+ skb, data->isoc_tx_ep->bInterval);
+
+ urb->transfer_flags = URB_ISO_ASAP;
+
+ fill_isoc_descriptor(urb, skb->len,
+ le16_to_cpu(data->isoc_tx_ep->wMaxPacketSize));
+
+ hdev->stat.sco_tx++;
+
+ usb_anchor_urb(urb, &data->tx_anchor);
+
+ err = usb_submit_urb(urb, GFP_ATOMIC);
+ if (err < 0) {
+ AICBT_ERR("%s: Failed to submit urb %p, pkt type %d, err %d",
+ __func__, urb, bt_cb(skb)->pkt_type, err);
+ kfree(urb->setup_packet);
+ usb_unanchor_urb(urb);
+ } else
+ usb_mark_last_busy(data->udev);
+ usb_free_urb(urb);
+
+ return err;
+
+}
+
+static bool snd_copy_send_sco_data( AIC_sco_card_t *pSCOSnd)
+{
+ struct snd_pcm_runtime *runtime = pSCOSnd->playback.substream->runtime;
+ unsigned int frame_bytes = 2, frames1;
+ const u8 *source;
+
+ snd_pcm_uframes_t period_size = runtime->period_size;
+ int i, count;
+ u8 buffer[period_size * 3];
+ int sco_packet_bytes = pSCOSnd->playback.sco_packet_bytes;
+ struct sk_buff *skb;
+
+ count = frames_to_bytes(runtime, period_size)/sco_packet_bytes;
+ skb = bt_skb_alloc(((sco_packet_bytes + HCI_SCO_HDR_SIZE) * count), GFP_ATOMIC);
+ skb->dev = (void *)hci_dev_get(0);
+ bt_cb(skb)->pkt_type = HCI_SCODATA_PKT;
+ skb_put(skb, ((sco_packet_bytes + HCI_SCO_HDR_SIZE) * count));
+ if(!skb)
+ return false;
+
+ AICBT_DBG("%s, buffer_pos:%d sco_handle:%d sco_packet_bytes:%d count:%d", __FUNCTION__, pSCOSnd->playback.buffer_pos, pSCOSnd->usb_data->sco_handle,
+ sco_packet_bytes, count);
+
+ source = runtime->dma_area + pSCOSnd->playback.buffer_pos * frame_bytes;
+
+ if (pSCOSnd->playback.buffer_pos + period_size <= runtime->buffer_size) {
+ memcpy(buffer, source, period_size * frame_bytes);
+ } else {
+ /* wrap around at end of ring buffer */
+ frames1 = runtime->buffer_size - pSCOSnd->playback.buffer_pos;
+ memcpy(buffer, source, frames1 * frame_bytes);
+ memcpy(&buffer[frames1 * frame_bytes],
+ runtime->dma_area, (period_size - frames1) * frame_bytes);
+ }
+
+ pSCOSnd->playback.buffer_pos += period_size;
+ if ( pSCOSnd->playback.buffer_pos >= runtime->buffer_size)
+ pSCOSnd->playback.buffer_pos -= runtime->buffer_size;
+
+ for(i = 0; i < count; i++) {
+ *((__u16 *)(skb->data + i * (sco_packet_bytes + HCI_SCO_HDR_SIZE))) = pSCOSnd->usb_data->sco_handle;
+ *((__u8 *)(skb->data + i*(sco_packet_bytes + HCI_SCO_HDR_SIZE) + 2)) = sco_packet_bytes;
+ memcpy((skb->data + i * (sco_packet_bytes + HCI_SCO_HDR_SIZE) + HCI_SCO_HDR_SIZE),
+ &buffer[sco_packet_bytes * i], sco_packet_bytes);
+ }
+
+ if(test_bit(ALSA_PLAYBACK_RUNNING, &pSCOSnd->states)) {
+ snd_pcm_period_elapsed(pSCOSnd->playback.substream);
+ }
+ snd_send_sco_frame(skb);
+ return true;
+}
+
+static void btusb_isoc_snd_tx_complete(struct urb *urb)
+{
+ struct sk_buff *skb = urb->context;
+ struct hci_dev *hdev = (struct hci_dev *) skb->dev;
+ struct btusb_data *data = GET_DRV_DATA(hdev);
+ AIC_sco_card_t *pSCOSnd = data->pSCOSnd;
+
+ AICBT_DBG("%s: status %d count %d",
+ __func__,urb->status, urb->actual_length);
+
+ if (skb && hdev) {
+ if (!test_bit(HCI_RUNNING, &hdev->flags))
+ goto done;
+
+ if (!urb->status)
+ hdev->stat.byte_tx += urb->transfer_buffer_length;
+ else
+ hdev->stat.err_tx++;
+ } else
+ AICBT_ERR("%s: skb 0x%p hdev 0x%p", __func__, skb, hdev);
+
+done:
+ kfree(urb->setup_packet);
+ kfree_skb(skb);
+ if(test_bit(ALSA_PLAYBACK_RUNNING, &pSCOSnd->states)){
+ snd_copy_send_sco_data(pSCOSnd);
+ //schedule_work(&pSCOSnd->send_sco_work);
+ }
+}
+
+static void playback_work(struct work_struct *work)
+{
+ AIC_sco_card_t *pSCOSnd = container_of(work, AIC_sco_card_t, send_sco_work);
+
+ snd_copy_send_sco_data(pSCOSnd);
+}
+
+#endif
+
+#if (CONFIG_BLUEDROID) || (HCI_VERSION_CODE < KERNEL_VERSION(3, 13, 0))
+int btusb_send_frame(struct sk_buff *skb)
+{
+ struct hci_dev *hdev = (struct hci_dev *) skb->dev;
+#else
+int btusb_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
+{
+#endif
+ //struct hci_dev *hdev = (struct hci_dev *) skb->dev;
+
+ struct btusb_data *data = GET_DRV_DATA(hdev);
+ struct usb_ctrlrequest *dr;
+ struct urb *urb;
+ unsigned int pipe;
+ int err = 0;
+ int retries = 0;
+ u16 *opcode = NULL;
+
+ AICBT_DBG("%s: hdev %p, btusb data %p, pkt type %d",
+ __func__, hdev, data, bt_cb(skb)->pkt_type);
+
+ //printk("aic %d %d\r\n", bt_cb(skb)->pkt_type, skb->len);
+ if (!test_bit(HCI_RUNNING, &hdev->flags))
+ return -EBUSY;
+
+#if (CONFIG_BLUEDROID == 0)
+#if HCI_VERSION_CODE >= KERNEL_VERSION(3, 13, 0)
+ skb->dev = (void *)hdev;
+#endif
+#endif
+
+ switch (bt_cb(skb)->pkt_type) {
+ case HCI_COMMAND_PKT:
+ print_command(skb);
+ urb = usb_alloc_urb(0, GFP_ATOMIC);
+ if (!urb)
+ return -ENOMEM;
+
+ dr = kmalloc(sizeof(*dr), GFP_ATOMIC);
+ if (!dr) {
+ usb_free_urb(urb);
+ return -ENOMEM;
+ }
+
+ dr->bRequestType = data->cmdreq_type;
+ dr->bRequest = 0;
+ dr->wIndex = 0;
+ dr->wValue = 0;
+ dr->wLength = __cpu_to_le16(skb->len);
+
+ pipe = usb_sndctrlpipe(data->udev, 0x00);
+
+ usb_fill_control_urb(urb, data->udev, pipe, (void *) dr,
+ skb->data, skb->len, btusb_tx_complete, skb);
+
+ hdev->stat.cmd_tx++;
+ break;
+
+ case HCI_ACLDATA_PKT:
+ if (bt_cb(skb)->pkt_type == HCI_COMMAND_PKT) {
+ print_command(skb);
+ opcode = (u16*)(skb->data);
+ printk("aic cmd:0x%04x", *opcode);
+ } else {
+ print_acl(skb, 1);
+ }
+ if (!data->bulk_tx_ep)
+ return -ENODEV;
+
+ urb = usb_alloc_urb(0, GFP_ATOMIC);
+ if (!urb)
+ return -ENOMEM;
+
+ pipe = usb_sndbulkpipe(data->udev,
+ data->bulk_tx_ep->bEndpointAddress);
+
+ usb_fill_bulk_urb(urb, data->udev, pipe,
+ skb->data, skb->len, btusb_tx_complete, skb);
+
+ hdev->stat.acl_tx++;
+ break;
+
+ case HCI_SCODATA_PKT:
+ print_sco(skb, 1);
+ if (!data->isoc_tx_ep || SCO_NUM < 1) {
+ kfree(skb);
+ return -ENODEV;
+ }
+
+ urb = usb_alloc_urb(BTUSB_MAX_ISOC_FRAMES, GFP_ATOMIC);
+ if (!urb) {
+ AICBT_ERR("%s: Failed to allocate mem for sco pkts", __func__);
+ kfree(skb);
+ return -ENOMEM;
+ }
+
+ pipe = usb_sndisocpipe(data->udev, data->isoc_tx_ep->bEndpointAddress);
+
+ usb_fill_int_urb(urb, data->udev, pipe,
+ skb->data, skb->len, btusb_isoc_tx_complete,
+ skb, data->isoc_tx_ep->bInterval);
+
+ urb->transfer_flags = URB_ISO_ASAP;
+
+ fill_isoc_descriptor(urb, skb->len,
+ le16_to_cpu(data->isoc_tx_ep->wMaxPacketSize));
+
+ hdev->stat.sco_tx++;
+ goto skip_waking;
+
+ default:
+ return -EILSEQ;
+ }
+
+ err = inc_tx(data);
+ if (err) {
+ usb_anchor_urb(urb, &data->deferred);
+ schedule_work(&data->waker);
+ err = 0;
+ goto done;
+ }
+
+skip_waking:
+ usb_anchor_urb(urb, &data->tx_anchor);
+retry:
+ err = usb_submit_urb(urb, GFP_ATOMIC);
+ if (err < 0) {
+ AICBT_ERR("%s: Failed to submit urb %p, pkt type %d, err %d, retries %d",
+ __func__, urb, bt_cb(skb)->pkt_type, err, retries);
+ if ((bt_cb(skb)->pkt_type != HCI_SCODATA_PKT) && (retries < 10)) {
+ mdelay(1);
+
+ if (bt_cb(skb)->pkt_type == HCI_COMMAND_PKT)
+ print_error_command(skb);
+ retries++;
+ goto retry;
+ }
+ kfree(urb->setup_packet);
+ usb_unanchor_urb(urb);
+ } else
+ usb_mark_last_busy(data->udev);
+ usb_free_urb(urb);
+
+done:
+ return err;
+}
+
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(3, 4, 0)
+static void btusb_destruct(struct hci_dev *hdev)
+{
+ struct btusb_data *data = GET_DRV_DATA(hdev);
+
+ AICBT_DBG("%s: name %s", __func__, hdev->name);
+
+ kfree(data);
+}
+#endif
+
+static void btusb_notify(struct hci_dev *hdev, unsigned int evt)
+{
+ struct btusb_data *data = GET_DRV_DATA(hdev);
+
+ AICBT_DBG("%s: name %s, evt %d", __func__, hdev->name, evt);
+
+ if (SCO_NUM != data->sco_num) {
+ data->sco_num = SCO_NUM;
+ schedule_work(&data->work);
+ }
+}
+
+static inline int set_isoc_interface(struct hci_dev *hdev, int altsetting)
+{
+ struct btusb_data *data = GET_DRV_DATA(hdev);
+ struct usb_interface *intf = data->isoc;
+ struct usb_endpoint_descriptor *ep_desc;
+ int i, err;
+
+ if (!data->isoc)
+ return -ENODEV;
+
+ err = usb_set_interface(data->udev, 1, altsetting);
+ if (err < 0) {
+ AICBT_ERR("%s: Failed to set interface, altsetting %d, err %d",
+ __func__, altsetting, err);
+ return err;
+ }
+
+ data->isoc_altsetting = altsetting;
+
+ data->isoc_tx_ep = NULL;
+ data->isoc_rx_ep = NULL;
+
+ for (i = 0; i < intf->cur_altsetting->desc.bNumEndpoints; i++) {
+ ep_desc = &intf->cur_altsetting->endpoint[i].desc;
+
+ if (!data->isoc_tx_ep && usb_endpoint_is_isoc_out(ep_desc)) {
+ data->isoc_tx_ep = ep_desc;
+ continue;
+ }
+
+ if (!data->isoc_rx_ep && usb_endpoint_is_isoc_in(ep_desc)) {
+ data->isoc_rx_ep = ep_desc;
+ continue;
+ }
+ }
+
+ if (!data->isoc_tx_ep || !data->isoc_rx_ep) {
+ AICBT_ERR("%s: Invalid SCO descriptors", __func__);
+ return -ENODEV;
+ }
+
+ AICBT_ERR("%s: hdev->reassembly implemant\r\n",
+ __func__);
+
+#if CONFIG_BLUEDROID
+ if(hdev->reassembly[HCI_SCODATA_PKT - 1]) {
+ kfree_skb(hdev->reassembly[HCI_SCODATA_PKT - 1]);
+ hdev->reassembly[HCI_SCODATA_PKT - 1] = NULL;
+ }
+#endif
+ return 0;
+}
+
+static void set_select_msbc(enum CODEC_TYPE type)
+{
+ printk("%s codec type = %d", __func__, (int)type);
+ codec_type = type;
+}
+
+static enum CODEC_TYPE check_select_msbc(void)
+{
+ return codec_type;
+}
+
+#ifdef CONFIG_SCO_OVER_HCI
+static int check_controller_support_msbc( struct usb_device *udev)
+{
+ //fix this in the future,when new card support msbc decode and encode
+ AICBT_INFO("%s:pid = 0x%02x, vid = 0x%02x",__func__,udev->descriptor.idProduct, udev->descriptor.idVendor);
+ switch (udev->descriptor.idProduct) {
+
+ default:
+ return 0;
+ }
+ return 0;
+}
+#endif
+static void btusb_work(struct work_struct *work)
+{
+ struct btusb_data *data = container_of(work, struct btusb_data, work);
+ struct hci_dev *hdev = data->hdev;
+ int err;
+ int new_alts;
+#ifdef CONFIG_SCO_OVER_HCI
+ AIC_sco_card_t *pSCOSnd = data->pSCOSnd;
+#endif
+ printk("%s data->sco_num:%d \r\n", __func__, data->sco_num);
+
+ if (data->sco_num > 0) {
+ if (!test_bit(BTUSB_DID_ISO_RESUME, &data->flags)) {
+ err = usb_autopm_get_interface(data->isoc ? data->isoc : data->intf);
+ if (err < 0) {
+ clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
+ mdelay(URB_CANCELING_DELAY_MS);
+ usb_kill_anchored_urbs(&data->isoc_anchor);
+ printk("%s usb_kill_anchored_urbs after \r\n", __func__);
+ return;
+ }
+
+ set_bit(BTUSB_DID_ISO_RESUME, &data->flags);
+ }
+
+ hdev->voice_setting = 93;
+ AICBT_INFO("%s voice settings = 0x%04x", __func__, hdev->voice_setting);
+ if (!(hdev->voice_setting & 0x0003)) {
+ if(data->sco_num == 1)
+ if(check_select_msbc()) {
+ new_alts = 1;
+ } else {
+ new_alts = 2;
+ }
+ else {
+ AICBT_INFO("%s: we don't support mutiple sco link for cvsd", __func__);
+ return;
+ }
+ } else{
+ if(check_select_msbc()) {
+ if(data->sco_num == 1)
+ new_alts = 1;
+ else {
+ AICBT_INFO("%s: we don't support mutiple sco link for msbc", __func__);
+ return;
+ }
+ } else {
+ new_alts = 2;
+ }
+ }
+ if (data->isoc_altsetting != new_alts) {
+
+ clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
+ mdelay(URB_CANCELING_DELAY_MS);
+ usb_kill_anchored_urbs(&data->isoc_anchor);
+
+ printk("%s set_isoc_interface in \r\n", __func__);
+ if (set_isoc_interface(hdev, new_alts) < 0)
+ return;
+
+ }
+
+ printk("%s set_isoc_interface out \r\n", __func__);
+
+ if (!test_and_set_bit(BTUSB_ISOC_RUNNING, &data->flags)) {
+ printk("%s btusb_submit_isoc_urb\r\n", __func__);
+ if (btusb_submit_isoc_urb(hdev, GFP_KERNEL) < 0)
+ clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
+ else
+ btusb_submit_isoc_urb(hdev, GFP_KERNEL);
+ }
+#ifdef CONFIG_SCO_OVER_HCI
+ if(test_bit(BTUSB_ISOC_RUNNING, &data->flags)) {
+ set_bit(USB_CAPTURE_RUNNING, &data->pSCOSnd->states);
+ set_bit(USB_PLAYBACK_RUNNING, &data->pSCOSnd->states);
+ }
+ if (test_bit(ALSA_PLAYBACK_RUNNING, &pSCOSnd->states)) {
+ schedule_work(&pSCOSnd->send_sco_work);
+ AICBT_INFO("%s: play_timer restart", __func__);
+ }
+#endif
+ } else {
+ clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
+#ifdef CONFIG_SCO_OVER_HCI
+ clear_bit(USB_CAPTURE_RUNNING, &data->pSCOSnd->states);
+ clear_bit(USB_PLAYBACK_RUNNING, &data->pSCOSnd->states);
+ //AIC_sco_card_t *pSCOSnd = data->pSCOSnd;
+ if (test_bit(ALSA_PLAYBACK_RUNNING, &pSCOSnd->states)) {
+ mod_timer(&snd_cap_timer.play_timer,jiffies + msecs_to_jiffies(30));
+ AICBT_INFO("%s: play_timer start", __func__);
+ }
+#endif
+ mdelay(URB_CANCELING_DELAY_MS);
+ usb_kill_anchored_urbs(&data->isoc_anchor);
+
+ set_isoc_interface(hdev, 0);
+ if (test_and_clear_bit(BTUSB_DID_ISO_RESUME, &data->flags))
+ usb_autopm_put_interface(data->isoc ? data->isoc : data->intf);
+ }
+}
+
+static void btusb_waker(struct work_struct *work)
+{
+ struct btusb_data *data = container_of(work, struct btusb_data, waker);
+ int err;
+
+ AICBT_DBG("%s", __func__);
+
+ err = usb_autopm_get_interface(data->intf);
+ if (err < 0)
+ return;
+
+ usb_autopm_put_interface(data->intf);
+}
+
+int bt_pm_notify(struct notifier_block *notifier, ulong pm_event, void *unused)
+{
+ struct btusb_data *data;
+ firmware_info *fw_info;
+ struct usb_device *udev;
+
+ AICBT_INFO("%s: pm event %ld", __func__, pm_event);
+
+ data = container_of(notifier, struct btusb_data, pm_notifier);
+ fw_info = data->fw_info;
+ udev = fw_info->udev;
+
+ switch (pm_event) {
+ case PM_SUSPEND_PREPARE:
+ case PM_HIBERNATION_PREPARE:
+#if 0
+ patch_entry->fw_len = load_firmware(fw_info, &patch_entry->fw_cache);
+ if (patch_entry->fw_len <= 0) {
+ /* We may encount failure in loading firmware, just give a warning */
+ AICBT_WARN("%s: Failed to load firmware", __func__);
+ }
+#endif
+ if (!device_may_wakeup(&udev->dev)) {
+#if (CONFIG_RESET_RESUME || CONFIG_BLUEDROID)
+ AICBT_INFO("%s:remote wakeup not supported, reset resume supported", __func__);
+#else
+ fw_info->intf->needs_binding = 1;
+ AICBT_INFO("%s:remote wakeup not supported, binding needed", __func__);
+#endif
+ }
+ break;
+
+ case PM_POST_SUSPEND:
+ case PM_POST_HIBERNATION:
+ case PM_POST_RESTORE:
+#if 0
+ /* Reclaim fw buffer when bt usb resumed */
+ if (patch_entry->fw_len > 0) {
+ kfree(patch_entry->fw_cache);
+ patch_entry->fw_cache = NULL;
+ patch_entry->fw_len = 0;
+ }
+#endif
+
+#if BTUSB_RPM
+ usb_disable_autosuspend(udev);
+ usb_enable_autosuspend(udev);
+ pm_runtime_set_autosuspend_delay(&(udev->dev), 2000);
+#endif
+ break;
+
+ default:
+ break;
+ }
+
+ return NOTIFY_DONE;
+}
+
+int bt_reboot_notify(struct notifier_block *notifier, ulong pm_event, void *unused)
+{
+ struct btusb_data *data;
+ firmware_info *fw_info;
+ struct usb_device *udev;
+
+ AICBT_INFO("%s: pm event %ld", __func__, pm_event);
+
+ data = container_of(notifier, struct btusb_data, reboot_notifier);
+ fw_info = data->fw_info;
+ udev = fw_info->udev;
+
+ switch (pm_event) {
+ case SYS_DOWN:
+ AICBT_DBG("%s:system down or restart", __func__);
+ break;
+
+ case SYS_HALT:
+ case SYS_POWER_OFF:
+#if SUSPNED_DW_FW
+ cancel_work_sync(&data->work);
+
+ btusb_stop_traffic(data);
+ mdelay(URB_CANCELING_DELAY_MS);
+ usb_kill_anchored_urbs(&data->tx_anchor);
+
+
+ if(fw_info_4_suspend) {
+ download_suspend_patch(fw_info_4_suspend,1);
+ }
+ else
+ AICBT_ERR("%s: Failed to download suspend fw", __func__);
+#endif
+
+#ifdef SET_WAKEUP_DEVICE
+ set_wakeup_device_from_conf(fw_info_4_suspend);
+#endif
+ AICBT_DBG("%s:system halt or power off", __func__);
+ break;
+
+ default:
+ break;
+ }
+
+ return NOTIFY_DONE;
+}
+
+
+#ifdef CONFIG_SCO_OVER_HCI
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0)
+void aic_snd_capture_timeout(ulong data)
+#else
+void aic_snd_capture_timeout(struct timer_list *t)
+#endif
+{
+ uint8_t null_data[255];
+ struct btusb_data *usb_data;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0)
+ usb_data = (struct btusb_data *)data;
+#else
+ usb_data = &snd_cap_timer.snd_usb_data;
+#endif
+ aic_copy_capture_data_to_alsa(usb_data, null_data, snd_cap_timer.snd_sco_length/2);
+ //printk("%s enter\r\n", __func__);
+ mod_timer(&snd_cap_timer.cap_timer,jiffies + msecs_to_jiffies(3));
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0)
+void aic_snd_play_timeout(ulong data)
+#else
+void aic_snd_play_timeout(struct timer_list *t)
+#endif
+{
+ AIC_sco_card_t *pSCOSnd;
+ struct snd_pcm_runtime *runtime;
+ snd_pcm_uframes_t period_size;
+ int count;
+ struct btusb_data *usb_data;
+ int sco_packet_bytes;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0)
+ usb_data = (struct btusb_data *)data;
+#else
+ usb_data = &snd_cap_timer.snd_usb_data;
+#endif
+ pSCOSnd = usb_data->pSCOSnd;
+
+ if(test_bit(USB_PLAYBACK_RUNNING, &pSCOSnd->states)) {
+ return;
+ }
+
+ if(!test_bit(ALSA_PLAYBACK_RUNNING, &pSCOSnd->states)) {
+ return;
+ }
+
+ runtime = pSCOSnd->playback.substream->runtime;
+ period_size = runtime->period_size;
+ sco_packet_bytes = pSCOSnd->playback.sco_packet_bytes;
+ count = frames_to_bytes(runtime, period_size)/sco_packet_bytes;
+
+ pSCOSnd->playback.buffer_pos += period_size;
+ if ( pSCOSnd->playback.buffer_pos >= runtime->buffer_size)
+ pSCOSnd->playback.buffer_pos -= runtime->buffer_size;
+
+ if(test_bit(ALSA_PLAYBACK_RUNNING, &pSCOSnd->states)) {
+ snd_pcm_period_elapsed(pSCOSnd->playback.substream);
+ }
+ //AICBT_DBG("%s,play_timer restart buffer_pos:%d sco_handle:%d sco_packet_bytes:%d count:%d", __FUNCTION__, pSCOSnd->playback.buffer_pos, pSCOSnd->usb_data->sco_handle,
+ //sco_packet_bytes, count);
+ mod_timer(&snd_cap_timer.play_timer,jiffies + msecs_to_jiffies(3*count));
+}
+
+static const struct snd_pcm_hardware snd_card_sco_capture_default =
+{
+ .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_NONINTERLEAVED |
+ SNDRV_PCM_ACCESS_RW_INTERLEAVED | SNDRV_PCM_INFO_FIFO_IN_FRAMES),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8,
+ .rates = (SNDRV_PCM_RATE_8000),
+ .rate_min = 8000,
+ .rate_max = 8000,
+ .channels_min = 1,
+ .channels_max = 1,
+ .buffer_bytes_max = 8 * 768,
+ .period_bytes_min = 48,
+ .period_bytes_max = 768,
+ .periods_min = 1,
+ .periods_max = 8,
+ .fifo_size = 8,
+
+};
+
+static int snd_sco_capture_pcm_open(struct snd_pcm_substream * substream)
+{
+ AIC_sco_card_t *pSCOSnd = substream->private_data;
+
+ AICBT_INFO("%s", __FUNCTION__);
+ pSCOSnd->capture.substream = substream;
+
+ memcpy(&substream->runtime->hw, &snd_card_sco_capture_default, sizeof(struct snd_pcm_hardware));
+ pSCOSnd->capture.buffer_pos = 0;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0)
+ init_timer(&snd_cap_timer.cap_timer);
+ snd_cap_timer.cap_timer.data = (unsigned long)pSCOSnd->usb_data;
+ snd_cap_timer.cap_timer.function = aic_snd_capture_timeout;
+#else
+ timer_setup(&snd_cap_timer.cap_timer, aic_snd_capture_timeout, 0);
+ snd_cap_timer.snd_usb_data = *(pSCOSnd->usb_data);
+#endif
+
+ if(check_controller_support_msbc(pSCOSnd->dev)) {
+ substream->runtime->hw.rates |= SNDRV_PCM_RATE_16000;
+ substream->runtime->hw.rate_max = 16000;
+ substream->runtime->hw.period_bytes_min = 96;
+ substream->runtime->hw.period_bytes_max = 16 * 96;
+ substream->runtime->hw.buffer_bytes_max = 8 * 16 * 96;
+ }
+ set_bit(ALSA_CAPTURE_OPEN, &pSCOSnd->states);
+ return 0;
+}
+
+static int snd_sco_capture_pcm_close(struct snd_pcm_substream *substream)
+{
+ AIC_sco_card_t *pSCOSnd = substream->private_data;
+
+ del_timer(&snd_cap_timer.cap_timer);
+ clear_bit(ALSA_CAPTURE_OPEN, &pSCOSnd->states);
+ return 0;
+}
+
+static int snd_sco_capture_ioctl(struct snd_pcm_substream *substream, unsigned int cmd, void *arg)
+{
+ AICBT_DBG("%s, cmd = %d", __FUNCTION__, cmd);
+ switch (cmd)
+ {
+ default:
+ return snd_pcm_lib_ioctl(substream, cmd, arg);
+ }
+ return 0;
+}
+
+static int snd_sco_capture_pcm_hw_params(struct snd_pcm_substream * substream, struct snd_pcm_hw_params * hw_params)
+{
+
+ int err;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ err = snd_pcm_lib_alloc_vmalloc_buffer(substream, params_buffer_bytes(hw_params));
+ AICBT_INFO("%s,err : %d, runtime state : %d", __FUNCTION__, err, runtime->status->state);
+ return err;
+}
+
+static int snd_sco_capture_pcm_hw_free(struct snd_pcm_substream * substream)
+{
+ AICBT_DBG("%s", __FUNCTION__);
+ return snd_pcm_lib_free_vmalloc_buffer(substream);;
+}
+
+static int snd_sco_capture_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ AIC_sco_card_t *pSCOSnd = substream->private_data;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ AICBT_INFO("%s %d\n", __FUNCTION__, (int)runtime->period_size);
+ if (test_bit(DISCONNECTED, &pSCOSnd->states))
+ return -ENODEV;
+ if (!test_bit(USB_CAPTURE_RUNNING, &pSCOSnd->states))
+ return -EIO;
+
+ if(runtime->rate == 8000) {
+ if(pSCOSnd->usb_data->isoc_altsetting != 2)
+ return -ENOEXEC;
+ pSCOSnd->capture.sco_packet_bytes = 48;
+ }
+ else if(runtime->rate == 16000 && check_controller_support_msbc(pSCOSnd->dev)) {
+ if(pSCOSnd->usb_data->isoc_altsetting != 4)
+ return -ENOEXEC;
+ pSCOSnd->capture.sco_packet_bytes = 96;
+ }
+ else if(pSCOSnd->usb_data->isoc_altsetting == 2) {
+ pSCOSnd->capture.sco_packet_bytes = 48;
+ }
+ else if(pSCOSnd->usb_data->isoc_altsetting == 1) {
+ pSCOSnd->capture.sco_packet_bytes = 24;
+ }
+ return 0;
+}
+
+static int snd_sco_capture_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ AIC_sco_card_t *pSCOSnd = substream->private_data;
+ AICBT_INFO("%s, cmd : %d", __FUNCTION__, cmd);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ if (!test_bit(USB_CAPTURE_RUNNING, &pSCOSnd->states))
+ return -EIO;
+ set_bit(ALSA_CAPTURE_RUNNING, &pSCOSnd->states);
+ return 0;
+ case SNDRV_PCM_TRIGGER_STOP:
+ clear_bit(ALSA_CAPTURE_RUNNING, &pSCOSnd->states);
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static snd_pcm_uframes_t snd_sco_capture_pcm_pointer(struct snd_pcm_substream *substream)
+{
+ AIC_sco_card_t *pSCOSnd = substream->private_data;
+
+ return pSCOSnd->capture.buffer_pos;
+}
+
+
+static struct snd_pcm_ops snd_sco_capture_pcm_ops = {
+ .open = snd_sco_capture_pcm_open,
+ .close = snd_sco_capture_pcm_close,
+ .ioctl = snd_sco_capture_ioctl,
+ .hw_params = snd_sco_capture_pcm_hw_params,
+ .hw_free = snd_sco_capture_pcm_hw_free,
+ .prepare = snd_sco_capture_pcm_prepare,
+ .trigger = snd_sco_capture_pcm_trigger,
+ .pointer = snd_sco_capture_pcm_pointer,
+};
+
+
+static const struct snd_pcm_hardware snd_card_sco_playback_default =
+{
+ .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_NONINTERLEAVED |
+ SNDRV_PCM_ACCESS_RW_INTERLEAVED | SNDRV_PCM_INFO_FIFO_IN_FRAMES),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = (SNDRV_PCM_RATE_8000),
+ .rate_min = 8000,
+ .rate_max = 8000,
+ .channels_min = 1,
+ .channels_max = 1,
+ .buffer_bytes_max = 8 * 768,
+ .period_bytes_min = 48,
+ .period_bytes_max = 768,
+ .periods_min = 1,
+ .periods_max = 8,
+ .fifo_size = 8,
+};
+
+static int snd_sco_playback_pcm_open(struct snd_pcm_substream * substream)
+{
+ AIC_sco_card_t *pSCOSnd = substream->private_data;
+ int err = 0;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0)
+ init_timer(&snd_cap_timer.play_timer);
+ snd_cap_timer.play_timer.data = (unsigned long)pSCOSnd->usb_data;
+ snd_cap_timer.play_timer.function = aic_snd_play_timeout;
+#else
+ timer_setup(&snd_cap_timer.play_timer, aic_snd_play_timeout, 0);
+ snd_cap_timer.snd_usb_data = *(pSCOSnd->usb_data);
+#endif
+ pSCOSnd->playback.buffer_pos = 0;
+
+ AICBT_INFO("%s, rate : %d", __FUNCTION__, substream->runtime->rate);
+ memcpy(&substream->runtime->hw, &snd_card_sco_playback_default, sizeof(struct snd_pcm_hardware));
+ if(check_controller_support_msbc(pSCOSnd->dev)) {
+ substream->runtime->hw.rates |= SNDRV_PCM_RATE_16000;
+ substream->runtime->hw.rate_max = 16000;
+ substream->runtime->hw.period_bytes_min = 96;
+ substream->runtime->hw.period_bytes_max = 16 * 96;
+ substream->runtime->hw.buffer_bytes_max = 8 * 16 * 96;
+ }
+ pSCOSnd->playback.substream = substream;
+ set_bit(ALSA_PLAYBACK_OPEN, &pSCOSnd->states);
+
+ return err;
+}
+
+static int snd_sco_playback_pcm_close(struct snd_pcm_substream *substream)
+{
+ AIC_sco_card_t *pSCOSnd = substream->private_data;
+
+ del_timer(&snd_cap_timer.play_timer);
+ AICBT_INFO("%s: play_timer delete", __func__);
+ clear_bit(ALSA_PLAYBACK_OPEN, &pSCOSnd->states);
+ cancel_work_sync(&pSCOSnd->send_sco_work);
+ return 0;
+}
+
+static int snd_sco_playback_ioctl(struct snd_pcm_substream *substream, unsigned int cmd, void *arg)
+{
+ AICBT_DBG("%s, cmd : %d", __FUNCTION__, cmd);
+ switch (cmd)
+ {
+ default:
+ return snd_pcm_lib_ioctl(substream, cmd, arg);
+ break;
+ }
+ return 0;
+}
+
+static int snd_sco_playback_pcm_hw_params(struct snd_pcm_substream * substream, struct snd_pcm_hw_params * hw_params)
+{
+ int err;
+ err = snd_pcm_lib_alloc_vmalloc_buffer(substream, params_buffer_bytes(hw_params));
+ return err;
+}
+
+static int snd_sco_palyback_pcm_hw_free(struct snd_pcm_substream * substream)
+{
+ AICBT_DBG("%s", __FUNCTION__);
+ return snd_pcm_lib_free_vmalloc_buffer(substream);
+}
+
+static int snd_sco_playback_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ AIC_sco_card_t *pSCOSnd = substream->private_data;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ AICBT_INFO("%s, bound_rate = %d", __FUNCTION__, runtime->rate);
+
+ if (test_bit(DISCONNECTED, &pSCOSnd->states))
+ return -ENODEV;
+ if (!test_bit(USB_PLAYBACK_RUNNING, &pSCOSnd->states))
+ return -EIO;
+
+ if(runtime->rate == 8000) {
+ if(pSCOSnd->usb_data->isoc_altsetting != 2)
+ return -ENOEXEC;
+ pSCOSnd->playback.sco_packet_bytes = 48;
+ }
+ else if(runtime->rate == 16000) {
+ if(pSCOSnd->usb_data->isoc_altsetting != 4)
+ return -ENOEXEC;
+ pSCOSnd->playback.sco_packet_bytes = 96;
+ }
+
+ return 0;
+}
+
+static int snd_sco_playback_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ AIC_sco_card_t *pSCOSnd = substream->private_data;
+
+ AICBT_INFO("%s, cmd = %d", __FUNCTION__, cmd);
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ if (!test_bit(USB_PLAYBACK_RUNNING, &pSCOSnd->states))
+ return -EIO;
+ set_bit(ALSA_PLAYBACK_RUNNING, &pSCOSnd->states);
+ schedule_work(&pSCOSnd->send_sco_work);
+#ifdef CONFIG_SCO_OVER_HCI
+ if (!test_bit(USB_PLAYBACK_RUNNING, &pSCOSnd->states)) {
+ AICBT_INFO("%s: play_timer cmd 1 start ", __func__);
+ mod_timer(&snd_cap_timer.play_timer,jiffies + msecs_to_jiffies(3));
+ }
+#endif
+ return 0;
+ case SNDRV_PCM_TRIGGER_STOP:
+ clear_bit(ALSA_PLAYBACK_RUNNING, &pSCOSnd->states);
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static snd_pcm_uframes_t snd_sco_playback_pcm_pointer(struct snd_pcm_substream *substream)
+{
+ AIC_sco_card_t *pSCOSnd = substream->private_data;
+
+ return pSCOSnd->playback.buffer_pos;
+}
+
+
+static struct snd_pcm_ops snd_sco_playback_pcm_ops = {
+ .open = snd_sco_playback_pcm_open,
+ .close = snd_sco_playback_pcm_close,
+ .ioctl = snd_sco_playback_ioctl,
+ .hw_params = snd_sco_playback_pcm_hw_params,
+ .hw_free = snd_sco_palyback_pcm_hw_free,
+ .prepare = snd_sco_playback_pcm_prepare,
+ .trigger = snd_sco_playback_pcm_trigger,
+ .pointer = snd_sco_playback_pcm_pointer,
+};
+
+
+static AIC_sco_card_t* btusb_snd_init(struct usb_interface *intf, const struct usb_device_id *id, struct btusb_data *data)
+{
+ struct snd_card *card;
+ AIC_sco_card_t *pSCOSnd;
+ int err=0;
+ AICBT_INFO("%s", __func__);
+ err = snd_card_new(&intf->dev,
+ -1, AIC_SCO_ID, THIS_MODULE,
+ sizeof(AIC_sco_card_t), &card);
+ if (err < 0) {
+ AICBT_ERR("%s: sco snd card create fail", __func__);
+ return NULL;
+ }
+ // private data
+ pSCOSnd = (AIC_sco_card_t *)card->private_data;
+ pSCOSnd->card = card;
+ pSCOSnd->dev = interface_to_usbdev(intf);
+ pSCOSnd->usb_data = data;
+
+ strcpy(card->driver, AIC_SCO_ID);
+ strcpy(card->shortname, "Aicsemi sco snd");
+ sprintf(card->longname, "Aicsemi sco over hci: VID:0x%04x, PID:0x%04x",
+ id->idVendor, pSCOSnd->dev->descriptor.idProduct);
+
+ err = snd_pcm_new(card, AIC_SCO_ID, 0, 1, 1, &pSCOSnd->pcm);
+ if (err < 0) {
+ AICBT_ERR("%s: sco snd card new pcm fail", __func__);
+ return NULL;
+ }
+ pSCOSnd->pcm->private_data = pSCOSnd;
+ sprintf(pSCOSnd->pcm->name, "sco_pcm:VID:0x%04x, PID:0x%04x",
+ id->idVendor, pSCOSnd->dev->descriptor.idProduct);
+
+ snd_pcm_set_ops(pSCOSnd->pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_sco_playback_pcm_ops);
+ snd_pcm_set_ops(pSCOSnd->pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_sco_capture_pcm_ops);
+
+ err = snd_card_register(card);
+ if (err < 0) {
+ AICBT_ERR("%s: sco snd card register card fail", __func__);
+ return NULL;
+ }
+
+ spin_lock_init(&pSCOSnd->capture_lock);
+ spin_lock_init(&pSCOSnd->playback_lock);
+ INIT_WORK(&pSCOSnd->send_sco_work, playback_work);
+ return pSCOSnd;
+}
+#endif
+
+static int aicwf_usb_chipmatch(u16 vid, u16 pid){
+
+ if(pid == USB_PRODUCT_ID_AIC8801){
+ g_chipid = PRODUCT_ID_AIC8801;
+ printk("%s USE AIC8801\r\n", __func__);
+ return 0;
+ }else if(pid == USB_PRODUCT_ID_AIC8800DC){
+ g_chipid = PRODUCT_ID_AIC8800DC;
+ printk("%s USE AIC8800DC\r\n", __func__);
+ return 0;
+ }else if(pid == USB_PRODUCT_ID_AIC8800D80){
+ g_chipid = PRODUCT_ID_AIC8800D80;
+ printk("%s USE AIC8800D80\r\n", __func__);
+ return 0;
+ }else{
+ return -1;
+ }
+}
+
+
+static int btusb_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+ struct usb_device *udev = interface_to_usbdev(intf);
+ struct usb_endpoint_descriptor *ep_desc;
+ u8 endpoint_num;
+ struct btusb_data *data;
+ struct hci_dev *hdev;
+ firmware_info *fw_info;
+ int i, err=0;
+
+ bt_support = 1;
+
+ AICBT_INFO("%s: usb_interface %p, bInterfaceNumber %d, idVendor 0x%04x, "
+ "idProduct 0x%04x", __func__, intf,
+ intf->cur_altsetting->desc.bInterfaceNumber,
+ id->idVendor, id->idProduct);
+
+ aicwf_usb_chipmatch(id->idVendor, id->idProduct);
+
+ /* interface numbers are hardcoded in the spec */
+ if (intf->cur_altsetting->desc.bInterfaceNumber != 0)
+ return -ENODEV;
+
+ AICBT_DBG("%s: can wakeup = %x, may wakeup = %x", __func__,
+ device_can_wakeup(&udev->dev), device_may_wakeup(&udev->dev));
+
+ data = aic_alloc(intf);
+ if (!data)
+ return -ENOMEM;
+
+ for (i = 0; i < intf->cur_altsetting->desc.bNumEndpoints; i++) {
+ ep_desc = &intf->cur_altsetting->endpoint[i].desc;
+
+ endpoint_num = usb_endpoint_num(ep_desc);
+ printk("endpoint num %d\n", endpoint_num);
+
+ if (!data->intr_ep && usb_endpoint_is_int_in(ep_desc)) {
+ data->intr_ep = ep_desc;
+ continue;
+ }
+
+ if (!data->bulk_tx_ep && usb_endpoint_is_bulk_out(ep_desc)) {
+ data->bulk_tx_ep = ep_desc;
+ continue;
+ }
+
+ if (!data->bulk_rx_ep && usb_endpoint_is_bulk_in(ep_desc)) {
+ data->bulk_rx_ep = ep_desc;
+ continue;
+ }
+ }
+
+ if (!data->intr_ep || !data->bulk_tx_ep || !data->bulk_rx_ep) {
+ aic_free(data);
+ return -ENODEV;
+ }
+
+ data->cmdreq_type = USB_TYPE_CLASS;
+
+ data->udev = udev;
+ data->intf = intf;
+
+ dlfw_dis_state = 0;
+ spin_lock_init(&queue_lock);
+ spin_lock_init(&dlfw_lock);
+ spin_lock_init(&data->lock);
+
+ INIT_WORK(&data->work, btusb_work);
+ INIT_WORK(&data->waker, btusb_waker);
+ spin_lock_init(&data->txlock);
+
+ init_usb_anchor(&data->tx_anchor);
+ init_usb_anchor(&data->intr_anchor);
+ init_usb_anchor(&data->bulk_anchor);
+ init_usb_anchor(&data->isoc_anchor);
+ init_usb_anchor(&data->deferred);
+
+#if (CONFIG_BLUEDROID == 0)
+#if HCI_VERSION_CODE >= KERNEL_VERSION(3, 18, 0)
+ spin_lock_init(&data->rxlock);
+ data->recv_bulk = btusb_recv_bulk;
+#endif
+#endif
+
+
+ fw_info = firmware_info_init(intf);
+ if (fw_info)
+ data->fw_info = fw_info;
+ else {
+ AICBT_WARN("%s: Failed to initialize fw info", __func__);
+ /* Skip download patch */
+ goto end;
+ }
+
+ AICBT_INFO("%s: download begining...", __func__);
+
+#if CONFIG_BLUEDROID
+ mutex_lock(&btchr_mutex);
+#endif
+ if(g_chipid == PRODUCT_ID_AIC8800DC){
+ err = download_patch(data->fw_info,1);
+ }
+
+#if CONFIG_BLUEDROID
+ mutex_unlock(&btchr_mutex);
+#endif
+
+ AICBT_INFO("%s: download ending...", __func__);
+ if (err < 0) {
+ return err;
+ }
+
+
+ hdev = hci_alloc_dev();
+ if (!hdev) {
+ aic_free(data);
+ data = NULL;
+ return -ENOMEM;
+ }
+
+ HDEV_BUS = HCI_USB;
+
+ data->hdev = hdev;
+
+ SET_HCIDEV_DEV(hdev, &intf->dev);
+
+ hdev->open = btusb_open;
+ hdev->close = btusb_close;
+ hdev->flush = btusb_flush;
+ hdev->send = btusb_send_frame;
+ hdev->notify = btusb_notify;
+#if (CONFIG_BLUEDROID == 0)
+#if LINUX_VERSION_CODE > KERNEL_VERSION(4, 0, 9)
+ hdev->shutdown = btusb_shutdown;
+#endif
+#endif //(CONFIG_BLUEDROIF == 0)
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(3, 4, 0)
+ hci_set_drvdata(hdev, data);
+#else
+ hdev->driver_data = data;
+ hdev->destruct = btusb_destruct;
+ hdev->owner = THIS_MODULE;
+#endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 7, 1)
+ if (!reset_on_close){
+ /* set_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks); */
+ AICBT_DBG("%s: Set HCI_QUIRK_RESET_ON_CLOSE", __func__);
+ }
+#endif
+
+ /* Interface numbers are hardcoded in the specification */
+ data->isoc = usb_ifnum_to_if(data->udev, 1);
+ if (data->isoc) {
+ err = usb_driver_claim_interface(&btusb_driver,
+ data->isoc, data);
+ if (err < 0) {
+ hci_free_dev(hdev);
+ hdev = NULL;
+ aic_free(data);
+ data = NULL;
+ return err;
+ }
+#ifdef CONFIG_SCO_OVER_HCI
+ data->pSCOSnd = btusb_snd_init(intf, id, data);
+#endif
+ }
+
+ err = hci_register_dev(hdev);
+ if (err < 0) {
+ hci_free_dev(hdev);
+ hdev = NULL;
+ aic_free(data);
+ data = NULL;
+ return err;
+ }
+
+ usb_set_intfdata(intf, data);
+
+//#ifdef CONFIG_HAS_EARLYSUSPEND
+#if 0
+ data->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN;
+ data->early_suspend.suspend = btusb_early_suspend;
+ data->early_suspend.resume = btusb_late_resume;
+ register_early_suspend(&data->early_suspend);
+#else
+ data->pm_notifier.notifier_call = bt_pm_notify;
+ data->reboot_notifier.notifier_call = bt_reboot_notify;
+ register_pm_notifier(&data->pm_notifier);
+ register_reboot_notifier(&data->reboot_notifier);
+#endif
+
+#if CONFIG_BLUEDROID
+ AICBT_INFO("%s: Check bt reset flag %d", __func__, bt_reset);
+ /* Report hci hardware error after everthing is ready,
+ * especially hci register is completed. Or, btchr_poll
+ * will get null hci dev when hotplug in.
+ */
+ if (bt_reset == 1) {
+ hci_hardware_error();
+ bt_reset = 0;
+ } else
+ bt_reset = 0; /* Clear and reset it anyway */
+#endif
+
+end:
+ return 0;
+}
+
+static void btusb_disconnect(struct usb_interface *intf)
+{
+ struct btusb_data *data;
+ struct hci_dev *hdev = NULL;
+#if CONFIG_BLUEDROID
+ wait_event_interruptible(bt_dlfw_wait, (check_set_dlfw_state_value(2) == 2));
+#endif
+
+ bt_support = 0;
+
+ AICBT_INFO("%s: usb_interface %p, bInterfaceNumber %d",
+ __func__, intf, intf->cur_altsetting->desc.bInterfaceNumber);
+
+ data = usb_get_intfdata(intf);
+
+ if (intf->cur_altsetting->desc.bInterfaceNumber != 0)
+ return;
+
+ if (data)
+ hdev = data->hdev;
+ else {
+ AICBT_WARN("%s: Failed to get bt usb data[Null]", __func__);
+ return;
+ }
+
+#ifdef CONFIG_SCO_OVER_HCI
+ if (intf->cur_altsetting->desc.bInterfaceNumber == 0) {
+ AIC_sco_card_t *pSCOSnd = data->pSCOSnd;
+ if(!pSCOSnd) {
+ AICBT_ERR("%s: sco private data is null", __func__);
+ return;
+ }
+ set_bit(DISCONNECTED, &pSCOSnd->states);
+ snd_card_disconnect(pSCOSnd->card);
+ snd_card_free_when_closed(pSCOSnd->card);
+ }
+#endif
+
+//#ifdef CONFIG_HAS_EARLYSUSPEND
+#if 0
+ unregister_early_suspend(&data->early_suspend);
+#else
+ unregister_pm_notifier(&data->pm_notifier);
+ unregister_reboot_notifier(&data->reboot_notifier);
+#endif
+
+ firmware_info_destroy(intf);
+
+#if CONFIG_BLUEDROID
+ if (test_bit(HCI_RUNNING, &hdev->flags)) {
+ AICBT_INFO("%s: Set BT reset flag", __func__);
+ bt_reset = 1;
+ }
+#endif
+
+ usb_set_intfdata(data->intf, NULL);
+
+ if (data->isoc)
+ usb_set_intfdata(data->isoc, NULL);
+
+ hci_unregister_dev(hdev);
+
+ if (intf == data->isoc)
+ usb_driver_release_interface(&btusb_driver, data->intf);
+ else if (data->isoc)
+ usb_driver_release_interface(&btusb_driver, data->isoc);
+
+#if !CONFIG_BLUEDROID
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(3, 4, 0)
+ __hci_dev_put(hdev);
+#endif
+#endif
+
+ hci_free_dev(hdev);
+ aic_free(data);
+ data = NULL;
+ set_dlfw_state_value(0);
+}
+
+#ifdef CONFIG_PM
+static int btusb_suspend(struct usb_interface *intf, pm_message_t message)
+{
+ struct btusb_data *data = usb_get_intfdata(intf);
+ //firmware_info *fw_info = data->fw_info;
+
+ AICBT_INFO("%s: event 0x%x, suspend count %d", __func__,
+ message.event, data->suspend_count);
+
+ if (intf->cur_altsetting->desc.bInterfaceNumber != 0)
+ return 0;
+#if 0
+ if (!test_bit(HCI_RUNNING, &data->hdev->flags))
+ set_bt_onoff(fw_info, 1);
+#endif
+ if (data->suspend_count++)
+ return 0;
+
+ spin_lock_irq(&data->txlock);
+ if (!((message.event & PM_EVENT_AUTO) && data->tx_in_flight)) {
+ set_bit(BTUSB_SUSPENDING, &data->flags);
+ spin_unlock_irq(&data->txlock);
+ } else {
+ spin_unlock_irq(&data->txlock);
+ data->suspend_count--;
+ AICBT_ERR("%s: Failed to enter suspend", __func__);
+ return -EBUSY;
+ }
+
+ cancel_work_sync(&data->work);
+
+ btusb_stop_traffic(data);
+ mdelay(URB_CANCELING_DELAY_MS);
+ usb_kill_anchored_urbs(&data->tx_anchor);
+
+ return 0;
+}
+
+static void play_deferred(struct btusb_data *data)
+{
+ struct urb *urb;
+ int err;
+
+ while ((urb = usb_get_from_anchor(&data->deferred))) {
+ usb_anchor_urb(urb, &data->tx_anchor);
+ err = usb_submit_urb(urb, GFP_ATOMIC);
+ if (err < 0) {
+ AICBT_ERR("%s: Failed to submit urb %p, err %d",
+ __func__, urb, err);
+ kfree(urb->setup_packet);
+ usb_unanchor_urb(urb);
+ } else {
+ usb_mark_last_busy(data->udev);
+ }
+ usb_free_urb(urb);
+
+ data->tx_in_flight++;
+ }
+ mdelay(URB_CANCELING_DELAY_MS);
+ usb_scuttle_anchored_urbs(&data->deferred);
+}
+
+static int btusb_resume(struct usb_interface *intf)
+{
+ struct btusb_data *data = usb_get_intfdata(intf);
+ struct hci_dev *hdev = data->hdev;
+ int err = 0;
+
+ AICBT_INFO("%s: Suspend count %d", __func__, data->suspend_count);
+
+ if (intf->cur_altsetting->desc.bInterfaceNumber != 0)
+ return 0;
+
+ if (--data->suspend_count)
+ return 0;
+
+ #if 0
+ /*check_fw_version to check the status of the BT Controller after USB Resume*/
+ err = check_fw_version(fw_info);
+ if (err !=0)
+ {
+ AICBT_INFO("%s: BT Controller Power OFF And Return hci_hardware_error:%d", __func__, err);
+ hci_hardware_error();
+ }
+ #endif
+
+ AICBT_INFO("%s g_chipid %x\n", __func__, g_chipid);
+ if(g_chipid == PRODUCT_ID_AIC8800DC){
+ if(data->fw_info){
+ err = download_patch(data->fw_info,1);
+ }else{
+ AICBT_WARN("%s: Failed to initialize fw info", __func__);
+ }
+ }
+
+ #if 1
+ if (test_bit(BTUSB_INTR_RUNNING, &data->flags)) {
+ err = btusb_submit_intr_urb(hdev, GFP_NOIO);
+ if (err < 0) {
+ clear_bit(BTUSB_INTR_RUNNING, &data->flags);
+ goto failed;
+ }
+ }
+ #endif
+
+ if (test_bit(BTUSB_BULK_RUNNING, &data->flags)) {
+ err = btusb_submit_bulk_urb(hdev, GFP_NOIO);
+ if (err < 0) {
+ clear_bit(BTUSB_BULK_RUNNING, &data->flags);
+ goto failed;
+ }
+
+ btusb_submit_bulk_urb(hdev, GFP_NOIO);
+ }
+
+ if (test_bit(BTUSB_ISOC_RUNNING, &data->flags)) {
+ if (btusb_submit_isoc_urb(hdev, GFP_NOIO) < 0)
+ clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
+ else
+ btusb_submit_isoc_urb(hdev, GFP_NOIO);
+ }
+
+ spin_lock_irq(&data->txlock);
+ play_deferred(data);
+ clear_bit(BTUSB_SUSPENDING, &data->flags);
+ spin_unlock_irq(&data->txlock);
+ schedule_work(&data->work);
+
+ return 0;
+
+failed:
+ mdelay(URB_CANCELING_DELAY_MS);
+ usb_scuttle_anchored_urbs(&data->deferred);
+ spin_lock_irq(&data->txlock);
+ clear_bit(BTUSB_SUSPENDING, &data->flags);
+ spin_unlock_irq(&data->txlock);
+
+ return err;
+}
+#endif
+
+static struct usb_driver btusb_driver = {
+ .name = "aic_btusb",
+ .probe = btusb_probe,
+ .disconnect = btusb_disconnect,
+#ifdef CONFIG_PM
+ .suspend = btusb_suspend,
+ .resume = btusb_resume,
+#if CONFIG_RESET_RESUME
+ .reset_resume = btusb_resume,
+#endif
+#endif
+ .id_table = btusb_table,
+ .supports_autosuspend = 1,
+#if LINUX_VERSION_CODE > KERNEL_VERSION(3, 7, 1)
+ .disable_hub_initiated_lpm = 1,
+#endif
+};
+
+static int __init btusb_init(void)
+{
+ int err;
+
+ AICBT_INFO("AICBT_RELEASE_NAME: %s",AICBT_RELEASE_NAME);
+ AICBT_INFO("AicSemi Bluetooth USB driver module init, version %s", VERSION);
+ AICBT_INFO("RELEASE DATE: 2023_0506_1635 \r\n");
+#if CONFIG_BLUEDROID
+ err = btchr_init();
+ if (err < 0) {
+ /* usb register will go on, even bt char register failed */
+ AICBT_ERR("Failed to register usb char device interfaces");
+ } else
+ bt_char_dev_registered = 1;
+#endif
+ err = usb_register(&btusb_driver);
+ if (err < 0)
+ AICBT_ERR("Failed to register aic bluetooth USB driver");
+ return err;
+}
+
+static void __exit btusb_exit(void)
+{
+ AICBT_INFO("AicSemi Bluetooth USB driver module exit");
+#if CONFIG_BLUEDROID
+ if (bt_char_dev_registered > 0)
+ btchr_exit();
+#endif
+ usb_deregister(&btusb_driver);
+}
+
+module_init(btusb_init);
+module_exit(btusb_exit);
+
+
+module_param(mp_drv_mode, int, 0644);
+MODULE_PARM_DESC(mp_drv_mode, "0: NORMAL; 1: MP MODE");
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)
+MODULE_IMPORT_NS(VFS_internal_I_am_really_a_filesystem_and_am_NOT_a_driver);
+#endif
+
+MODULE_AUTHOR("AicSemi Corporation");
+MODULE_DESCRIPTION("AicSemi Bluetooth USB driver version");
+MODULE_VERSION(VERSION);
+MODULE_LICENSE("GPL");
diff --git a/drivers/bluetooth/aic_btusb/aic_btusb.h b/drivers/bluetooth/aic_btusb/aic_btusb.h
new file mode 100644
index 000000000000..a7b7ab5032cb
--- /dev/null
+++ b/drivers/bluetooth/aic_btusb/aic_btusb.h
@@ -0,0 +1,753 @@
+/*
+ *
+ * Aic Bluetooth USB driver
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/skbuff.h>
+#include <linux/errno.h>
+#include <linux/usb.h>
+#include <linux/cdev.h>
+#include <linux/device.h>
+#include <linux/poll.h>
+
+#include <linux/version.h>
+#include <linux/pm_runtime.h>
+#include <linux/firmware.h>
+#include <linux/suspend.h>
+
+
+#ifdef CONFIG_PLATFORM_UBUNTU
+#define CONFIG_BLUEDROID 0 /* bleuz 0, bluedroid 1 */
+#else
+#define CONFIG_BLUEDROID 1 /* bleuz 0, bluedroid 1 */
+#endif
+
+
+//#define CONFIG_SCO_OVER_HCI
+#define CONFIG_USB_AIC_UART_SCO_DRIVER
+
+#ifdef CONFIG_SCO_OVER_HCI
+#include <linux/usb/audio.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+
+#define AIC_SCO_ID "snd_sco_aic"
+enum {
+ USB_CAPTURE_RUNNING,
+ USB_PLAYBACK_RUNNING,
+ ALSA_CAPTURE_OPEN,
+ ALSA_PLAYBACK_OPEN,
+ ALSA_CAPTURE_RUNNING,
+ ALSA_PLAYBACK_RUNNING,
+ CAPTURE_URB_COMPLETED,
+ PLAYBACK_URB_COMPLETED,
+ DISCONNECTED,
+};
+
+// AIC sound card
+typedef struct AIC_sco_card {
+ struct snd_card *card;
+ struct snd_pcm *pcm;
+ struct usb_device *dev;
+ struct btusb_data *usb_data;
+ unsigned long states;
+ struct aic_sco_stream {
+ struct snd_pcm_substream *substream;
+ unsigned int sco_packet_bytes;
+ snd_pcm_uframes_t buffer_pos;
+ } capture, playback;
+ spinlock_t capture_lock;
+ spinlock_t playback_lock;
+ struct work_struct send_sco_work;
+} AIC_sco_card_t;
+#endif
+/* Some Android system may use standard Linux kernel, while
+ * standard Linux may also implement early suspend feature.
+ * So exclude earysuspend.h from CONFIG_BLUEDROID.
+ */
+#ifdef CONFIG_HAS_EARLYSUSPEND
+#include <linux/earlysuspend.h>
+#endif
+
+#if CONFIG_BLUEDROID
+#else
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+#include <net/bluetooth/hci.h>
+#endif
+
+
+/***********************************
+** AicSemi - For aic_btusb driver **
+***********************************/
+#define URB_CANCELING_DELAY_MS 10
+/* when OS suspended, module is still powered,usb is not powered,
+ * this may set to 1, and must comply with special patch code.
+ */
+#define CONFIG_RESET_RESUME 1
+#define PRINT_CMD_EVENT 0
+#define PRINT_ACL_DATA 0
+#define PRINT_SCO_DATA 0
+
+#define AICBT_DBG_FLAG 0
+
+#if AICBT_DBG_FLAG
+#define AICBT_DBG(fmt, arg...) printk( "aic_btusb: " fmt "\n" , ## arg)
+#else
+#define AICBT_DBG(fmt, arg...)
+#endif
+
+#define AICBT_INFO(fmt, arg...) printk("aic_btusb: " fmt "\n" , ## arg)
+#define AICBT_WARN(fmt, arg...) printk("aic_btusb: " fmt "\n" , ## arg)
+#define AICBT_ERR(fmt, arg...) printk("aic_btusb: " fmt "\n" , ## arg)
+
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 33)
+#define HDEV_BUS hdev->bus
+#define USB_RPM 1
+#else
+#define HDEV_BUS hdev->type
+#define USB_RPM 0
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 38)
+#define NUM_REASSEMBLY 3
+#endif
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(3, 4, 0)
+#define GET_DRV_DATA(x) hci_get_drvdata(x)
+#else
+#define GET_DRV_DATA(x) x->driver_data
+#endif
+
+#define SCO_NUM hdev->conn_hash.sco_num
+
+
+#define BTUSB_RPM (0 * USB_RPM) /* 1 SS enable; 0 SS disable */
+#define BTUSB_WAKEUP_HOST 0 /* 1 enable; 0 disable */
+#define BTUSB_MAX_ISOC_FRAMES 48
+#define BTUSB_INTR_RUNNING 0
+#define BTUSB_BULK_RUNNING 1
+#define BTUSB_ISOC_RUNNING 2
+#define BTUSB_SUSPENDING 3
+#define BTUSB_DID_ISO_RESUME 4
+
+#define HCI_VENDOR_USB_DISC_HARDWARE_ERROR 0xFF
+
+#define HCI_CMD_READ_BD_ADDR 0x1009
+#define HCI_VENDOR_READ_LMP_VERISION 0x1001
+#define HCI_VENDOR_RESET 0x0C03
+
+#define DRV_NORMAL_MODE 0
+#define DRV_MP_MODE 1
+int mp_drv_mode = 0; /* 1 Mptool Fw; 0 Normal Fw */
+
+
+#if CONFIG_BLUEDROID
+#define QUEUE_SIZE 500
+
+/***************************************
+** AicSemi - Integrate from bluetooth.h **
+*****************************************/
+/* Reserv for core and drivers use */
+#define BT_SKB_RESERVE 8
+
+/* BD Address */
+typedef struct {
+ __u8 b[6];
+} __packed bdaddr_t;
+
+/* Skb helpers */
+struct bt_skb_cb {
+ __u8 pkt_type;
+ __u8 incoming;
+ __u16 expect;
+ __u16 tx_seq;
+ __u8 retries;
+ __u8 sar;
+ __u8 force_active;
+};
+
+#define bt_cb(skb) ((struct bt_skb_cb *)((skb)->cb))
+
+static inline struct sk_buff *bt_skb_alloc(unsigned int len, gfp_t how)
+{
+ struct sk_buff *skb;
+
+ if ((skb = alloc_skb(len + BT_SKB_RESERVE, how))) {
+ skb_reserve(skb, BT_SKB_RESERVE);
+ bt_cb(skb)->incoming = 0;
+ }
+ return skb;
+}
+/* AicSemi - Integrate from bluetooth.h end */
+
+/***********************************
+** AicSemi - Integrate from hci.h **
+***********************************/
+#define HCI_MAX_ACL_SIZE 1024
+#define HCI_MAX_SCO_SIZE 255
+#define HCI_MAX_EVENT_SIZE 260
+#define HCI_MAX_FRAME_SIZE (HCI_MAX_ACL_SIZE + 4)
+
+/* HCI bus types */
+#define HCI_VIRTUAL 0
+#define HCI_USB 1
+#define HCI_PCCARD 2
+#define HCI_UART 3
+#define HCI_RS232 4
+#define HCI_PCI 5
+#define HCI_SDIO 6
+
+/* HCI controller types */
+#define HCI_BREDR 0x00
+#define HCI_AMP 0x01
+
+/* HCI device flags */
+enum {
+ HCI_UP,
+ HCI_INIT,
+ HCI_RUNNING,
+
+ HCI_PSCAN,
+ HCI_ISCAN,
+ HCI_AUTH,
+ HCI_ENCRYPT,
+ HCI_INQUIRY,
+
+ HCI_RAW,
+
+ HCI_RESET,
+};
+
+/*
+ * BR/EDR and/or LE controller flags: the flags defined here should represent
+ * states from the controller.
+ */
+enum {
+ HCI_SETUP,
+ HCI_AUTO_OFF,
+ HCI_MGMT,
+ HCI_PAIRABLE,
+ HCI_SERVICE_CACHE,
+ HCI_LINK_KEYS,
+ HCI_DEBUG_KEYS,
+ HCI_UNREGISTER,
+
+ HCI_LE_SCAN,
+ HCI_SSP_ENABLED,
+ HCI_HS_ENABLED,
+ HCI_LE_ENABLED,
+ HCI_CONNECTABLE,
+ HCI_DISCOVERABLE,
+ HCI_LINK_SECURITY,
+ HCI_PENDING_CLASS,
+};
+
+/* HCI data types */
+#define HCI_COMMAND_PKT 0x01
+#define HCI_ACLDATA_PKT 0x02
+#define HCI_SCODATA_PKT 0x03
+#define HCI_EVENT_PKT 0x04
+#define HCI_VENDOR_PKT 0xff
+
+#define HCI_MAX_NAME_LENGTH 248
+#define HCI_MAX_EIR_LENGTH 240
+
+#define HCI_OP_READ_LOCAL_VERSION 0x1001
+struct hci_rp_read_local_version {
+ __u8 status;
+ __u8 hci_ver;
+ __le16 hci_rev;
+ __u8 lmp_ver;
+ __le16 manufacturer;
+ __le16 lmp_subver;
+} __packed;
+
+#define HCI_EV_CMD_COMPLETE 0x0e
+struct hci_ev_cmd_complete {
+ __u8 ncmd;
+ __le16 opcode;
+} __packed;
+
+/* ---- HCI Packet structures ---- */
+#define HCI_COMMAND_HDR_SIZE 3
+#define HCI_EVENT_HDR_SIZE 2
+#define HCI_ACL_HDR_SIZE 4
+#define HCI_SCO_HDR_SIZE 3
+
+struct hci_command_hdr {
+ __le16 opcode; /* OCF & OGF */
+ __u8 plen;
+} __packed;
+
+struct hci_event_hdr {
+ __u8 evt;
+ __u8 plen;
+} __packed;
+
+struct hci_acl_hdr {
+ __le16 handle; /* Handle & Flags(PB, BC) */
+ __le16 dlen;
+} __packed;
+
+struct hci_sco_hdr {
+ __le16 handle;
+ __u8 dlen;
+} __packed;
+
+static inline struct hci_event_hdr *hci_event_hdr(const struct sk_buff *skb)
+{
+ return (struct hci_event_hdr *) skb->data;
+}
+
+static inline struct hci_acl_hdr *hci_acl_hdr(const struct sk_buff *skb)
+{
+ return (struct hci_acl_hdr *) skb->data;
+}
+
+static inline struct hci_sco_hdr *hci_sco_hdr(const struct sk_buff *skb)
+{
+ return (struct hci_sco_hdr *) skb->data;
+}
+
+/* ---- HCI Ioctl requests structures ---- */
+struct hci_dev_stats {
+ __u32 err_rx;
+ __u32 err_tx;
+ __u32 cmd_tx;
+ __u32 evt_rx;
+ __u32 acl_tx;
+ __u32 acl_rx;
+ __u32 sco_tx;
+ __u32 sco_rx;
+ __u32 byte_rx;
+ __u32 byte_tx;
+};
+/* AicSemi - Integrate from hci.h end */
+
+/*****************************************
+** AicSemi - Integrate from hci_core.h **
+*****************************************/
+struct hci_conn_hash {
+ struct list_head list;
+ unsigned int acl_num;
+ unsigned int sco_num;
+ unsigned int le_num;
+};
+
+#define HCI_MAX_SHORT_NAME_LENGTH 10
+
+#define NUM_REASSEMBLY 4
+struct hci_dev {
+ struct mutex lock;
+
+ char name[8];
+ unsigned long flags;
+ __u16 id;
+ __u8 bus;
+ __u8 dev_type;
+
+ struct sk_buff *reassembly[NUM_REASSEMBLY];
+
+ struct hci_conn_hash conn_hash;
+
+ struct hci_dev_stats stat;
+
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(3, 4, 0)
+ atomic_t refcnt;
+ struct module *owner;
+ void *driver_data;
+#endif
+
+ atomic_t promisc;
+
+ struct device *parent;
+ struct device dev;
+
+ unsigned long dev_flags;
+
+ int (*open)(struct hci_dev *hdev);
+ int (*close)(struct hci_dev *hdev);
+ int (*flush)(struct hci_dev *hdev);
+ int (*send)(struct sk_buff *skb);
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(3, 4, 0)
+ void (*destruct)(struct hci_dev *hdev);
+#endif
+#if LINUX_VERSION_CODE > KERNEL_VERSION(3, 7, 1)
+ __u16 voice_setting;
+#endif
+ void (*notify)(struct hci_dev *hdev, unsigned int evt);
+ int (*ioctl)(struct hci_dev *hdev, unsigned int cmd, unsigned long arg);
+ u8 *align_data;
+};
+
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(3, 4, 0)
+static inline struct hci_dev *__hci_dev_hold(struct hci_dev *d)
+{
+ atomic_inc(&d->refcnt);
+ return d;
+}
+
+static inline void __hci_dev_put(struct hci_dev *d)
+{
+ if (atomic_dec_and_test(&d->refcnt))
+ d->destruct(d);
+}
+#endif
+
+static inline void *hci_get_drvdata(struct hci_dev *hdev)
+{
+ return dev_get_drvdata(&hdev->dev);
+}
+
+static inline void hci_set_drvdata(struct hci_dev *hdev, void *data)
+{
+ dev_set_drvdata(&hdev->dev, data);
+}
+
+#define SET_HCIDEV_DEV(hdev, pdev) ((hdev)->parent = (pdev))
+/* AicSemi - Integrate from hci_core.h end */
+
+/* ----- HCI Commands ---- */
+#define HCI_OP_INQUIRY 0x0401
+#define HCI_OP_INQUIRY_CANCEL 0x0402
+#define HCI_OP_EXIT_PERIODIC_INQ 0x0404
+#define HCI_OP_CREATE_CONN 0x0405
+#define HCI_OP_DISCONNECT 0x0406
+#define HCI_OP_ADD_SCO 0x0407
+#define HCI_OP_CREATE_CONN_CANCEL 0x0408
+#define HCI_OP_ACCEPT_CONN_REQ 0x0409
+#define HCI_OP_REJECT_CONN_REQ 0x040a
+#define HCI_OP_LINK_KEY_REPLY 0x040b
+#define HCI_OP_LINK_KEY_NEG_REPLY 0x040c
+#define HCI_OP_PIN_CODE_REPLY 0x040d
+#define HCI_OP_PIN_CODE_NEG_REPLY 0x040e
+#define HCI_OP_CHANGE_CONN_PTYPE 0x040f
+#define HCI_OP_AUTH_REQUESTED 0x0411
+#define HCI_OP_SET_CONN_ENCRYPT 0x0413
+#define HCI_OP_CHANGE_CONN_LINK_KEY 0x0415
+#define HCI_OP_REMOTE_NAME_REQ 0x0419
+#define HCI_OP_REMOTE_NAME_REQ_CANCEL 0x041a
+#define HCI_OP_READ_REMOTE_FEATURES 0x041b
+#define HCI_OP_READ_REMOTE_EXT_FEATURES 0x041c
+#define HCI_OP_READ_REMOTE_VERSION 0x041d
+#define HCI_OP_SETUP_SYNC_CONN 0x0428
+#define HCI_OP_ACCEPT_SYNC_CONN_REQ 0x0429
+#define HCI_OP_REJECT_SYNC_CONN_REQ 0x042a
+#define HCI_OP_SNIFF_MODE 0x0803
+#define HCI_OP_EXIT_SNIFF_MODE 0x0804
+#define HCI_OP_ROLE_DISCOVERY 0x0809
+#define HCI_OP_SWITCH_ROLE 0x080b
+#define HCI_OP_READ_LINK_POLICY 0x080c
+#define HCI_OP_WRITE_LINK_POLICY 0x080d
+#define HCI_OP_READ_DEF_LINK_POLICY 0x080e
+#define HCI_OP_WRITE_DEF_LINK_POLICY 0x080f
+#define HCI_OP_SNIFF_SUBRATE 0x0811
+#define HCI_OP_Write_Link_Policy_Settings 0x080d
+#define HCI_OP_SET_EVENT_MASK 0x0c01
+#define HCI_OP_RESET 0x0c03
+#define HCI_OP_SET_EVENT_FLT 0x0c05
+#define HCI_OP_Write_Extended_Inquiry_Response 0x0c52
+#define HCI_OP_Write_Simple_Pairing_Mode 0x0c56
+#define HCI_OP_Read_Buffer_Size 0x1005
+#define HCI_OP_Host_Buffer_Size 0x0c33
+#define HCI_OP_Read_Local_Version_Information 0x1001
+#define HCI_OP_Read_BD_ADDR 0x1009
+#define HCI_OP_Read_Local_Supported_Commands 0x1002
+#define HCI_OP_Write_Scan_Enable 0x0c1a
+#define HCI_OP_Write_Current_IAC_LAP 0x0c3a
+#define HCI_OP_Write_Inquiry_Scan_Activity 0x0c1e
+#define HCI_OP_Write_Class_of_Device 0x0c24
+#define HCI_OP_LE_Rand 0x2018
+#define HCI_OP_LE_Set_Random_Address 0x2005
+#define HCI_OP_LE_Set_Extended_Scan_Enable 0x2042
+#define HCI_OP_LE_Set_Extended_Scan_Parameters 0x2041
+#define HCI_OP_Set_Event_Filter 0x0c05
+#define HCI_OP_Write_Voice_Setting 0x0c26
+#define HCI_OP_Change_Local_Name 0x0c13
+#define HCI_OP_Read_Local_Name 0x0c14
+#define HCI_OP_Wirte_Page_Timeout 0x0c18
+#define HCI_OP_LE_Clear_Resolving_List 0x0c29
+#define HCI_OP_LE_Set_Addres_Resolution_Enable_Command 0x0c2e
+#define HCI_OP_Write_Inquiry_mode 0x0c45
+#define HCI_OP_Write_Page_Scan_Type 0x0c47
+#define HCI_OP_Write_Inquiry_Scan_Type 0x0c43
+
+#define HCI_OP_Delete_Stored_Link_Key 0x0c12
+#define HCI_OP_LE_Read_Local_Resolvable_Address 0x202d
+#define HCI_OP_LE_Extended_Create_Connection 0x2043
+#define HCI_OP_Read_Remote_Version_Information 0x041d
+#define HCI_OP_LE_Start_Encryption 0x2019
+#define HCI_OP_LE_Add_Device_to_Resolving_List 0x2027
+#define HCI_OP_LE_Set_Privacy_Mode 0x204e
+#define HCI_OP_LE_Connection_Update 0x2013
+
+/* ----- HCI events---- */
+#define HCI_OP_DISCONNECT 0x0406
+#define HCI_EV_INQUIRY_COMPLETE 0x01
+#define HCI_EV_INQUIRY_RESULT 0x02
+#define HCI_EV_CONN_COMPLETE 0x03
+#define HCI_EV_CONN_REQUEST 0x04
+#define HCI_EV_DISCONN_COMPLETE 0x05
+#define HCI_EV_AUTH_COMPLETE 0x06
+#define HCI_EV_REMOTE_NAME 0x07
+#define HCI_EV_ENCRYPT_CHANGE 0x08
+#define HCI_EV_CHANGE_LINK_KEY_COMPLETE 0x09
+
+#define HCI_EV_REMOTE_FEATURES 0x0b
+#define HCI_EV_REMOTE_VERSION 0x0c
+#define HCI_EV_QOS_SETUP_COMPLETE 0x0d
+#define HCI_EV_CMD_COMPLETE 0x0e
+#define HCI_EV_CMD_STATUS 0x0f
+
+#define HCI_EV_ROLE_CHANGE 0x12
+#define HCI_EV_NUM_COMP_PKTS 0x13
+#define HCI_EV_MODE_CHANGE 0x14
+#define HCI_EV_PIN_CODE_REQ 0x16
+#define HCI_EV_LINK_KEY_REQ 0x17
+#define HCI_EV_LINK_KEY_NOTIFY 0x18
+#define HCI_EV_CLOCK_OFFSET 0x1c
+#define HCI_EV_PKT_TYPE_CHANGE 0x1d
+#define HCI_EV_PSCAN_REP_MODE 0x20
+
+#define HCI_EV_INQUIRY_RESULT_WITH_RSSI 0x22
+#define HCI_EV_REMOTE_EXT_FEATURES 0x23
+#define HCI_EV_SYNC_CONN_COMPLETE 0x2c
+#define HCI_EV_SYNC_CONN_CHANGED 0x2d
+#define HCI_EV_SNIFF_SUBRATE 0x2e
+#define HCI_EV_EXTENDED_INQUIRY_RESULT 0x2f
+#define HCI_EV_IO_CAPA_REQUEST 0x31
+#define HCI_EV_SIMPLE_PAIR_COMPLETE 0x36
+#define HCI_EV_REMOTE_HOST_FEATURES 0x3d
+#define HCI_EV_LE_Meta 0x3e
+
+#define CONFIG_MAC_OFFSET_GEN_1_2 (0x3C) //MAC's OFFSET in config/efuse for aic generation 1~2 bluetooth chip
+#define CONFIG_MAC_OFFSET_GEN_3PLUS (0x44) //MAC's OFFSET in config/efuse for aic generation 3+ bluetooth chip
+
+
+typedef struct {
+ uint16_t vid;
+ uint16_t pid;
+ uint16_t lmp_sub_default;
+ uint16_t lmp_sub;
+ uint16_t eversion;
+ char *mp_patch_name;
+ char *patch_name;
+ char *config_name;
+ uint8_t *fw_cache;
+ int fw_len;
+ uint16_t mac_offset;
+ uint32_t max_patch_size;
+} patch_info;
+
+//Define ioctl cmd the same as HCIDEVUP in the kernel
+#define DOWN_FW_CFG _IOW('E', 176, int)
+//#ifdef CONFIG_SCO_OVER_HCI
+//#define SET_ISO_CFG _IOW('H', 202, int)
+//#else
+#define SET_ISO_CFG _IOW('E', 177, int)
+//#endif
+#define RESET_CONTROLLER _IOW('E', 178, int)
+#define DWFW_CMPLT _IOW('E', 179, int)
+
+#define GET_USB_INFO _IOR('E', 180, int)
+
+/* for altsettings*/
+#include <linux/fs.h>
+#define BDADDR_FILE "/data/misc/bluetooth/bdaddr"
+#define FACTORY_BT_BDADDR_STORAGE_LEN 17
+#if 0
+static inline int getmacaddr(uint8_t * vnd_local_bd_addr)
+{
+ struct file *bdaddr_file;
+ mm_segment_t oldfs;
+ char buf[FACTORY_BT_BDADDR_STORAGE_LEN];
+ int32_t i = 0;
+ memset(buf, 0, FACTORY_BT_BDADDR_STORAGE_LEN);
+ bdaddr_file = filp_open(BDADDR_FILE, O_RDONLY, 0);
+ if (IS_ERR(bdaddr_file)){
+ AICBT_INFO("No Mac Config for BT\n");
+ return -1;
+ }
+ oldfs = get_fs();
+ set_fs(KERNEL_DS);
+ bdaddr_file->f_op->llseek(bdaddr_file, 0, 0);
+ bdaddr_file->f_op->read(bdaddr_file, buf, FACTORY_BT_BDADDR_STORAGE_LEN, &bdaddr_file->f_pos);
+ for (i = 0; i < 6; i++) {
+ if(buf[3*i]>'9')
+ {
+ if(buf[3*i]>'Z')
+ buf[3*i] -=('a'-'A'); //change a to A
+ buf[3*i] -= ('A'-'9'-1);
+ }
+ if(buf[3*i+1]>'9')
+ {
+ if(buf[3*i+1]>'Z')
+ buf[3*i+1] -=('a'-'A'); //change a to A
+ buf[3*i+1] -= ('A'-'9'-1);
+ }
+ vnd_local_bd_addr[5-i] = ((uint8_t)buf[3*i]-'0')*16 + ((uint8_t)buf[3*i+1]-'0');
+ }
+ set_fs(oldfs);
+ filp_close(bdaddr_file, NULL);
+ return 0;
+}
+#endif
+
+#endif /* CONFIG_BLUEDROID */
+
+
+typedef struct {
+ struct usb_interface *intf;
+ struct usb_device *udev;
+ int pipe_in, pipe_out;
+ uint8_t *send_pkt;
+ uint8_t *rcv_pkt;
+ struct hci_command_hdr *cmd_hdr;
+ struct hci_event_hdr *evt_hdr;
+ struct hci_ev_cmd_complete *cmd_cmp;
+ uint8_t *req_para, *rsp_para;
+ uint8_t *fw_data;
+ int pkt_len;
+ int fw_len;
+} firmware_info;
+
+/*******************************
+** Reasil patch code
+********************************/
+#define CMD_CMP_EVT 0x0e
+#define RCV_PKT_LEN 64
+#define SEND_PKT_LEN 300
+#define MSG_TO 1000
+#define PATCH_SEG_MAX 252
+#define DATA_END 0x80
+#define DOWNLOAD_OPCODE 0xfc02
+#define HCI_VSC_UPDATE_PT_CMD 0xFC75
+#define BTOFF_OPCODE 0xfc28
+#define TRUE 1
+#define FALSE 0
+#define CMD_HDR_LEN sizeof(struct hci_command_hdr)
+#define EVT_HDR_LEN sizeof(struct hci_event_hdr)
+#define CMD_CMP_LEN sizeof(struct hci_ev_cmd_complete)
+#define MAX_PATCH_SIZE_24K (1024*24)
+#define MAX_PATCH_SIZE_40K (1024*40)
+
+
+#define FW_RAM_ADID_BASE_ADDR 0x101788
+#define FW_RAM_PATCH_BASE_ADDR 0x184000
+#define FW_ADID_BASE_NAME "fw_adid_8800dc.bin"
+#define FW_PATCH_TABLE_NAME "fw_patch_table_8800dc.bin"
+#define FW_PATCH_BASE_NAME "fw_patch_8800dc.bin"
+#define FW_PATCH_TABLE_NAME_U02 "fw_patch_table_8800dc_u02.bin"
+#define FW_PATCH_BASE_NAME_U02 "fw_patch_8800dc_u02.bin"
+#define FW_PATCH_TABLE_NAME_U02H "fw_patch_table_8800dc_u02h.bin"
+#define FW_PATCH_BASE_NAME_U02H "fw_patch_8800dc_u02h.bin"
+#define AICBT_PT_TAG "AICBT_PT_TAG"
+
+enum aicbt_patch_table_type {
+ AICBT_PT_NULL = 0x00,
+ AICBT_PT_TRAP,
+ AICBT_PT_B4,
+ AICBT_PT_BTMODE,
+ AICBT_PT_PWRON,
+ AICBT_PT_AF,
+ AICBT_PT_VER,
+ AICBT_PT_MAX,
+};
+
+#define HCI_VSC_FW_STATUS_GET_CMD 0xFC78
+
+struct fw_status {
+ u8 status;
+} __packed;
+
+#define HCI_PATCH_DATA_MAX_LEN 240
+#define HCI_VSC_MEM_WR_SIZE 240
+#define HCI_VSC_MEM_RD_SIZE 128
+#define HCI_VSC_UPDATE_PT_SIZE 249
+#define HCI_PT_MAX_LEN 31
+
+#define HCI_VSC_DBG_RD_MEM_CMD 0xFC01
+
+struct hci_dbg_rd_mem_cmd {
+ __le32 start_addr;
+ __u8 type;
+ __u8 length;
+}__attribute__ ((packed));
+
+struct hci_dbg_rd_mem_cmd_evt {
+ __u8 status;
+ __u8 length;
+ __u8 data[HCI_VSC_MEM_RD_SIZE];
+}__attribute__ ((packed));
+
+struct long_buffer_tag {
+ __u8 length;
+ __u8 data[HCI_VSC_MEM_WR_SIZE];
+};
+
+struct hci_dbg_wr_mem_cmd {
+ __le32 start_addr;
+ __u8 type;
+ __u8 length;
+ __u8 data[HCI_VSC_MEM_WR_SIZE];
+};
+
+struct aicbt_patch_table {
+ char *name;
+ uint32_t type;
+ uint32_t *data;
+ uint32_t len;
+ struct aicbt_patch_table *next;
+};
+
+struct aicbt_patch_table_cmd {
+ uint8_t patch_num;
+ uint32_t patch_table_addr[31];
+ uint32_t patch_table_data[31];
+}__attribute__ ((packed));
+
+
+enum aic_endpoit {
+ CTRL_EP = 0,
+ INTR_EP = 3,
+ BULK_EP = 1,
+ ISOC_EP = 4
+};
+
+/* #define HCI_VERSION_CODE KERNEL_VERSION(3, 14, 41) */
+#define HCI_VERSION_CODE LINUX_VERSION_CODE
+
+int aic_load_firmware(u8 ** fw_buf, const char *name, struct device *device);
+int aicbt_patch_table_free(struct aicbt_patch_table **head);
+int download_patch(firmware_info *fw_info, int cached);
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 38)
+#define NUM_REASSEMBLY 3
+#else
+#define NUM_REASSEMBLY 4
+#endif
+
diff --git a/drivers/bluetooth/aic_btusb/aic_btusb_external_featrue.c b/drivers/bluetooth/aic_btusb/aic_btusb_external_featrue.c
new file mode 100644
index 000000000000..c65148ac1197
--- /dev/null
+++ b/drivers/bluetooth/aic_btusb/aic_btusb_external_featrue.c
@@ -0,0 +1,126 @@
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/skbuff.h>
+#include <linux/usb.h>
+#include <linux/poll.h>
+#include <linux/cdev.h>
+#include <linux/device.h>
+
+
+
+#define IOCTL_CHAR_DEVICE_NAME "aic_btusb_ex_dev"
+
+#define SET_APCF_PARAMETER _IOR('E', 181, int)
+
+static dev_t ioctl_devid; /* bt char device number */
+static struct cdev ioctl_char_dev; /* bt character device structure */
+static struct class *ioctl_char_class; /* device class for usb char driver */
+
+extern struct file_operations ioctl_chrdev_ops;
+
+extern void btchr_external_write(char* data, int len);
+
+static long ioctl_ioctl(struct file *file_p,unsigned int cmd, unsigned long arg)
+{
+ char data[1024];
+ int ret = 0;
+
+ printk("%s enter\r\n", __func__);
+ memset(data, 0, 1024);
+ switch(cmd)
+ {
+ case SET_APCF_PARAMETER:
+ printk("set apcf parameter\r\n");
+ ret = copy_from_user(data, (int __user *)arg, 1024);
+ btchr_external_write(&data[1], (int)data[0]);
+ break;
+
+ default:
+ printk("unknow cmdr\r\n");
+ break;
+ }
+ return 0;
+}
+
+
+#ifdef CONFIG_COMPAT
+static long compat_ioctlchr_ioctl (struct file *filp, unsigned int cmd, unsigned long arg)
+{
+ return ioctl_ioctl(filp, cmd, (unsigned long) compat_ptr(arg));
+}
+#endif
+
+
+struct file_operations ioctl_chrdev_ops = {
+ unlocked_ioctl : ioctl_ioctl,
+#ifdef CONFIG_COMPAT
+ compat_ioctl : compat_ioctlchr_ioctl,
+#endif
+
+};
+
+static int __init init_extenal_ioctl(void){
+ int res = 0;
+ struct device *dev;
+
+ printk("%s enter\r\n", __func__);
+
+ ioctl_char_class = class_create(THIS_MODULE, IOCTL_CHAR_DEVICE_NAME);
+ if (IS_ERR(ioctl_char_class)) {
+ printk("Failed to create ioctl char class");
+ }
+
+ res = alloc_chrdev_region(&ioctl_devid, 0, 1, IOCTL_CHAR_DEVICE_NAME);
+ if (res < 0) {
+ printk("Failed to allocate ioctl char device");
+ goto err_alloc;
+ }
+
+ dev = device_create(ioctl_char_class, NULL, ioctl_devid, NULL, IOCTL_CHAR_DEVICE_NAME);
+ if (IS_ERR(dev)) {
+ printk("Failed to create ioctl char device");
+ res = PTR_ERR(dev);
+ goto err_create;
+ }
+
+ cdev_init(&ioctl_char_dev, &ioctl_chrdev_ops);
+ res = cdev_add(&ioctl_char_dev, ioctl_devid, 1);
+ if (res < 0) {
+ printk("Failed to add ioctl char device");
+ goto err_add;
+ }
+
+ return res;
+
+err_add:
+ device_destroy(ioctl_char_class, ioctl_devid);
+err_create:
+ unregister_chrdev_region(ioctl_devid, 1);
+err_alloc:
+ class_destroy(ioctl_char_class);
+
+ return res;
+
+}
+static void __exit deinit_extenal_ioctl(void){
+ printk("%s enter\r\n", __func__);
+ device_destroy(ioctl_char_class, ioctl_devid);
+ cdev_del(&ioctl_char_dev);
+ unregister_chrdev_region(ioctl_devid, 1);
+ class_destroy(ioctl_char_class);
+
+}
+
+module_init(init_extenal_ioctl);
+module_exit(deinit_extenal_ioctl);
+
+
+MODULE_AUTHOR("AicSemi Corporation");
+MODULE_DESCRIPTION("AicSemi Bluetooth USB driver version");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/bluetooth/aic_btusb/aic_btusb_external_featrue.h b/drivers/bluetooth/aic_btusb/aic_btusb_external_featrue.h
new file mode 100644
index 000000000000..3b7b81ce1ca0
--- /dev/null
+++ b/drivers/bluetooth/aic_btusb/aic_btusb_external_featrue.h
@@ -0,0 +1,3 @@
+
+void btchr_external_write(char* data, int len);
+
diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
index 869e535faefa..e29a69833f67 100644
--- a/drivers/gpu/drm/panel/Kconfig
+++ b/drivers/gpu/drm/panel/Kconfig
@@ -233,6 +233,16 @@ config DRM_PANEL_JADARD_JD9365DA_H3
WXGA MIPI DSI panel. The panel support TFT dot matrix LCD with
800RGBx1280 dots at maximum.
+config DRM_PANEL_STARFIVE_JADARD
+ tristate "STARFIVE JADARD WXGA DSI panel"
+ depends on OF
+ depends on DRM_MIPI_DSI
+ depends on BACKLIGHT_CLASS_DEVICE
+ help
+ Say Y here if you want to enable support for StarFive Jadard radxa
+ WXGA MIPI DSI panel. The panel support TFT dot matrix LCD with
+ 800RGBx1280 dots at maximum.
+
config DRM_PANEL_JDI_LT070ME05000
tristate "JDI LT070ME05000 WUXGA DSI panel"
depends on OF
diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
index 433e93d57949..85afe7fbf1ab 100644
--- a/drivers/gpu/drm/panel/Makefile
+++ b/drivers/gpu/drm/panel/Makefile
@@ -21,6 +21,7 @@ obj-$(CONFIG_DRM_PANEL_ILITEK_ILI9881C) += panel-ilitek-ili9881c.o
obj-$(CONFIG_DRM_PANEL_INNOLUX_EJ030NA) += panel-innolux-ej030na.o
obj-$(CONFIG_DRM_PANEL_INNOLUX_P079ZCA) += panel-innolux-p079zca.o
obj-$(CONFIG_DRM_PANEL_JADARD_JD9365DA_H3) += panel-jadard-jd9365da-h3.o
+obj-$(CONFIG_DRM_PANEL_STARFIVE_JADARD) += panel-starfive-jadard.o
obj-$(CONFIG_DRM_PANEL_JDI_LT070ME05000) += panel-jdi-lt070me05000.o
obj-$(CONFIG_DRM_PANEL_JDI_R63452) += panel-jdi-fhd-r63452.o
obj-$(CONFIG_DRM_PANEL_KHADAS_TS050) += panel-khadas-ts050.o
diff --git a/drivers/gpu/drm/panel/panel-starfive-jadard.c b/drivers/gpu/drm/panel/panel-starfive-jadard.c
new file mode 100644
index 000000000000..a86bd6dc72f8
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-starfive-jadard.c
@@ -0,0 +1,828 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2019 Radxa Limited
+ * Copyright (c) 2022 Edgeble AI Technologies Pvt. Ltd.
+ *
+ * Author:
+ * - Jagan Teki <jagan@amarulasolutions.com>
+ * - Stephen Chen <stephen@radxa.com>
+ */
+
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_modes.h>
+#include <drm/drm_panel.h>
+#include <drm/drm_print.h>
+
+#include <linux/gpio/consumer.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/regulator/consumer.h>
+
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/of_graph.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_device.h>
+#include <video/display_timing.h>
+#include <video/videomode.h>
+
+
+//accel sc7a20
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/common/st_sensors_i2c.h>
+#include <linux/iio/common/st_sensors.h>
+//#include "st_accel.h"
+
+#define DSI_DRIVER_NAME "starfive-dri"
+
+enum cmd_type {
+ CMD_TYPE_DCS,
+ CMD_TYPE_DELAY,
+};
+
+struct jadard_init_cmd {
+ enum cmd_type type;
+ const char *data;
+ size_t len;
+};
+
+#define _INIT_CMD_DCS(...) \
+ { \
+ .type = CMD_TYPE_DCS, \
+ .data = (char[]){__VA_ARGS__}, \
+ .len = sizeof((char[]){__VA_ARGS__}) \
+ } \
+
+#define _INIT_CMD_DELAY(...) \
+ { \
+ .type = CMD_TYPE_DELAY, \
+ .data = (char[]){__VA_ARGS__}, \
+ .len = sizeof((char[]){__VA_ARGS__}) \
+ } \
+
+struct jadard_panel_desc {
+ const struct drm_display_mode mode;
+ unsigned int lanes;
+ enum mipi_dsi_pixel_format format;
+ const struct jadard_init_cmd *init_cmds;
+ u32 num_init_cmds;
+ const struct display_timing *timings;
+ unsigned int num_timings;
+};
+
+struct jadard {
+ struct drm_panel panel;
+ struct mipi_dsi_device *dsi;
+ const struct jadard_panel_desc *desc;
+ struct i2c_client *client;
+
+ struct device *dev;
+
+ struct regulator *vdd;
+ struct regulator *vccio;
+ struct gpio_desc *reset;
+ struct gpio_desc *enable;
+ bool enable_initialized;
+ int choosemode;
+};
+
+static inline struct jadard *panel_to_jadard(struct drm_panel *panel)
+{
+ return container_of(panel, struct jadard, panel);
+}
+
+static int jadard_i2c_write(struct i2c_client *client, u8 reg, u8 val)
+{
+ struct i2c_msg msg;
+ u8 buf[2];
+ int ret;
+
+ buf[0] = reg;
+ buf[1] = val;
+ msg.addr = client->addr;
+ msg.flags = 0;
+ msg.buf = buf;
+ msg.len = 2;
+
+ ret = i2c_transfer(client->adapter, &msg, 1);
+ if (ret >= 0)
+ return 0;
+
+ return ret;
+}
+
+static int jadard_i2c_read(struct i2c_client *client, u8 reg, u8 *val)
+{
+ struct i2c_msg msg[2];
+ u8 buf[2];
+ int ret;
+
+ buf[0] = reg;
+ msg[0].addr = client->addr;
+ msg[0].flags = 0;
+ msg[0].buf = buf;
+ msg[0].len = 1;
+ msg[1].addr = client->addr;
+ msg[1].flags = I2C_M_RD;
+ msg[1].buf = val;
+ msg[1].len = 1;
+ ret = i2c_transfer(client->adapter, msg, 2);
+
+ if (ret >= 0)
+ return 0;
+
+ return ret;
+}
+
+static int jadard_enable(struct drm_panel *panel)
+{
+ struct device *dev = panel->dev;
+ struct jadard *jadard = panel_to_jadard(panel);
+ const struct jadard_panel_desc *desc = jadard->desc;
+ struct mipi_dsi_device *dsi = jadard->dsi;
+ unsigned int i;
+ int err;
+ if (jadard->enable_initialized == true)
+ return 0;
+
+ if (jadard->choosemode == 0) {//8inch
+
+ for (i = 0; i < desc->num_init_cmds; i++) {
+ const struct jadard_init_cmd *cmd = &desc->init_cmds[i];
+
+ switch (cmd->type) {
+ case CMD_TYPE_DELAY:
+ msleep(cmd->data[0]);
+ err = 0;
+ break;
+ case CMD_TYPE_DCS:
+ err = mipi_dsi_dcs_write(dsi, cmd->data[0],
+ cmd->len <= 1 ? NULL : &cmd->data[1],
+ cmd->len - 1);
+ break;
+ default:
+ err = -EINVAL;
+ }
+
+ if (err < 0) {
+ DRM_DEV_ERROR(dev, "failed to write CMD#0x%x\n", cmd->data[0]);
+ return err;
+ }
+
+ }
+
+ err = mipi_dsi_dcs_exit_sleep_mode(dsi);
+ if (err < 0)
+ DRM_DEV_ERROR(dev, "failed to exit sleep mode ret = %d\n", err);
+ msleep(120);
+
+ err = mipi_dsi_dcs_set_display_on(dsi);
+ if (err < 0)
+ DRM_DEV_ERROR(dev, "failed to set display on ret = %d\n", err);
+ }
+
+ jadard->enable_initialized = true ;
+
+ return 0;
+}
+
+static int jadard_disable(struct drm_panel *panel)
+{
+ struct device *dev = panel->dev;
+ struct jadard *jadard = panel_to_jadard(panel);
+ int ret;
+ if (jadard->choosemode == 0) {//8inch
+ ret = mipi_dsi_dcs_set_display_off(jadard->dsi);
+ if (ret < 0)
+ DRM_DEV_ERROR(dev, "failed to set display off: %d\n", ret);
+
+ ret = mipi_dsi_dcs_enter_sleep_mode(jadard->dsi);
+ if (ret < 0)
+ DRM_DEV_ERROR(dev, "failed to enter sleep mode: %d\n", ret);
+
+ }
+ jadard->enable_initialized = false;
+
+ return 0;
+}
+
+static int jadard_prepare(struct drm_panel *panel)
+{
+ struct device *dev = panel->dev;
+ struct jadard *jadard = panel_to_jadard(panel);
+ const struct jadard_panel_desc *desc = jadard->desc;
+ struct mipi_dsi_device *dsi = jadard->dsi;
+ unsigned int i;
+ int err;
+
+ if (jadard->enable_initialized == true)
+ return 0;
+
+ gpiod_direction_output(jadard->enable, 0);
+ gpiod_set_value(jadard->enable, 1);
+ mdelay(100);
+
+ gpiod_direction_output(jadard->reset, 0);
+ mdelay(100);
+ gpiod_set_value(jadard->reset, 1);
+ mdelay(100);
+ gpiod_set_value(jadard->reset, 0);
+ mdelay(100);
+ gpiod_set_value(jadard->reset, 1);
+ mdelay(150);
+
+ return 0;
+}
+
+static int jadard_unprepare(struct drm_panel *panel)
+{
+ struct jadard *jadard = panel_to_jadard(panel);
+
+ gpiod_set_value(jadard->reset, 1);
+ msleep(120);
+#if 0
+ regulator_disable(jadard->vdd);
+ regulator_disable(jadard->vccio);
+#endif
+ return 0;
+}
+
+static int jadard_get_modes(struct drm_panel *panel,
+ struct drm_connector *connector)
+{
+ struct jadard *jadard = panel_to_jadard(panel);
+ const struct drm_display_mode *desc_mode = &jadard->desc->mode;
+ struct drm_display_mode *mode;
+
+ mode = drm_mode_duplicate(connector->dev, desc_mode);
+ if (!mode) {
+ DRM_DEV_ERROR(&jadard->dsi->dev, "failed to add mode %ux%ux@%u\n",
+ desc_mode->hdisplay, desc_mode->vdisplay,
+ drm_mode_vrefresh(desc_mode));
+ return -ENOMEM;
+ }
+
+ drm_mode_set_name(mode);
+ drm_mode_probed_add(connector, mode);
+
+ connector->display_info.width_mm = mode->width_mm;
+ connector->display_info.height_mm = mode->height_mm;
+
+ return 1;
+}
+
+static int seiko_panel_get_timings(struct drm_panel *panel,
+ unsigned int num_timings,
+ struct display_timing *timings)
+{
+ struct jadard *jadard = panel_to_jadard(panel);
+ unsigned int i;
+
+ if (jadard->desc->num_timings < num_timings)
+ num_timings = jadard->desc->num_timings;
+
+ if (timings)
+ for (i = 0; i < num_timings; i++)
+ timings[i] = jadard->desc->timings[i];
+
+ return jadard->desc->num_timings;
+}
+
+static const struct drm_panel_funcs jadard_funcs = {
+ .disable = jadard_disable,
+ .unprepare = jadard_unprepare,
+ .prepare = jadard_prepare,
+ .enable = jadard_enable,
+ .get_modes = jadard_get_modes,
+ .get_timings = seiko_panel_get_timings,
+};
+
+static const struct jadard_init_cmd cz101b4001_init_cmds[] = {
+ _INIT_CMD_DCS(0x01),
+ _INIT_CMD_DELAY(100),
+ _INIT_CMD_DCS(0xE0, 0x00),
+ _INIT_CMD_DCS(0xE1, 0x93),
+ _INIT_CMD_DCS(0xE2, 0x65),
+ _INIT_CMD_DCS(0xE3, 0xF8),
+ _INIT_CMD_DCS(0x80, 0x03),
+ _INIT_CMD_DCS(0xE0, 0x01),
+ _INIT_CMD_DCS(0x00, 0x00),
+ _INIT_CMD_DCS(0x01, 0x7E),
+ _INIT_CMD_DCS(0x03, 0x00),
+ _INIT_CMD_DCS(0x04, 0x65),
+ _INIT_CMD_DCS(0x0C, 0x74),
+ _INIT_CMD_DCS(0x17, 0x00),
+ _INIT_CMD_DCS(0x18, 0xB7),
+ _INIT_CMD_DCS(0x19, 0x00),
+ _INIT_CMD_DCS(0x1A, 0x00),
+ _INIT_CMD_DCS(0x1B, 0xB7),
+ _INIT_CMD_DCS(0x1C, 0x00),
+ _INIT_CMD_DCS(0x24, 0xFE),
+ _INIT_CMD_DCS(0x37, 0x19),
+ _INIT_CMD_DCS(0x38, 0x05),
+ _INIT_CMD_DCS(0x39, 0x00),
+ _INIT_CMD_DCS(0x3A, 0x01),
+ _INIT_CMD_DCS(0x3B, 0x01),
+ _INIT_CMD_DCS(0x3C, 0x70),
+ _INIT_CMD_DCS(0x3D, 0xFF),
+ _INIT_CMD_DCS(0x3E, 0xFF),
+ _INIT_CMD_DCS(0x3F, 0xFF),
+ _INIT_CMD_DCS(0x40, 0x06),
+ _INIT_CMD_DCS(0x41, 0xA0),
+ _INIT_CMD_DCS(0x43, 0x1E),
+ _INIT_CMD_DCS(0x44, 0x0F),
+ _INIT_CMD_DCS(0x45, 0x28),
+ _INIT_CMD_DCS(0x4B, 0x04),
+ _INIT_CMD_DCS(0x55, 0x02),
+ _INIT_CMD_DCS(0x56, 0x01),
+ _INIT_CMD_DCS(0x57, 0xA9),
+ _INIT_CMD_DCS(0x58, 0x0A),
+ _INIT_CMD_DCS(0x59, 0x0A),
+ _INIT_CMD_DCS(0x5A, 0x37),
+ _INIT_CMD_DCS(0x5B, 0x19),
+ _INIT_CMD_DCS(0x5D, 0x78),
+ _INIT_CMD_DCS(0x5E, 0x63),
+ _INIT_CMD_DCS(0x5F, 0x54),
+ _INIT_CMD_DCS(0x60, 0x49),
+ _INIT_CMD_DCS(0x61, 0x45),
+ _INIT_CMD_DCS(0x62, 0x38),
+ _INIT_CMD_DCS(0x63, 0x3D),
+ _INIT_CMD_DCS(0x64, 0x28),
+ _INIT_CMD_DCS(0x65, 0x43),
+ _INIT_CMD_DCS(0x66, 0x41),
+ _INIT_CMD_DCS(0x67, 0x43),
+ _INIT_CMD_DCS(0x68, 0x62),
+ _INIT_CMD_DCS(0x69, 0x50),
+ _INIT_CMD_DCS(0x6A, 0x57),
+ _INIT_CMD_DCS(0x6B, 0x49),
+ _INIT_CMD_DCS(0x6C, 0x44),
+ _INIT_CMD_DCS(0x6D, 0x37),
+ _INIT_CMD_DCS(0x6E, 0x23),
+ _INIT_CMD_DCS(0x6F, 0x10),
+ _INIT_CMD_DCS(0x70, 0x78),
+ _INIT_CMD_DCS(0x71, 0x63),
+ _INIT_CMD_DCS(0x72, 0x54),
+ _INIT_CMD_DCS(0x73, 0x49),
+ _INIT_CMD_DCS(0x74, 0x45),
+ _INIT_CMD_DCS(0x75, 0x38),
+ _INIT_CMD_DCS(0x76, 0x3D),
+ _INIT_CMD_DCS(0x77, 0x28),
+ _INIT_CMD_DCS(0x78, 0x43),
+ _INIT_CMD_DCS(0x79, 0x41),
+ _INIT_CMD_DCS(0x7A, 0x43),
+ _INIT_CMD_DCS(0x7B, 0x62),
+ _INIT_CMD_DCS(0x7C, 0x50),
+ _INIT_CMD_DCS(0x7D, 0x57),
+ _INIT_CMD_DCS(0x7E, 0x49),
+ _INIT_CMD_DCS(0x7F, 0x44),
+ _INIT_CMD_DCS(0x80, 0x37),
+ _INIT_CMD_DCS(0x81, 0x23),
+ _INIT_CMD_DCS(0x82, 0x10),
+ _INIT_CMD_DCS(0xE0, 0x02),
+ _INIT_CMD_DCS(0x00, 0x47),
+ _INIT_CMD_DCS(0x01, 0x47),
+ _INIT_CMD_DCS(0x02, 0x45),
+ _INIT_CMD_DCS(0x03, 0x45),
+ _INIT_CMD_DCS(0x04, 0x4B),
+ _INIT_CMD_DCS(0x05, 0x4B),
+ _INIT_CMD_DCS(0x06, 0x49),
+ _INIT_CMD_DCS(0x07, 0x49),
+ _INIT_CMD_DCS(0x08, 0x41),
+ _INIT_CMD_DCS(0x09, 0x1F),
+ _INIT_CMD_DCS(0x0A, 0x1F),
+ _INIT_CMD_DCS(0x0B, 0x1F),
+ _INIT_CMD_DCS(0x0C, 0x1F),
+ _INIT_CMD_DCS(0x0D, 0x1F),
+ _INIT_CMD_DCS(0x0E, 0x1F),
+ _INIT_CMD_DCS(0x0F, 0x5F),
+ _INIT_CMD_DCS(0x10, 0x5F),
+ _INIT_CMD_DCS(0x11, 0x57),
+ _INIT_CMD_DCS(0x12, 0x77),
+ _INIT_CMD_DCS(0x13, 0x35),
+ _INIT_CMD_DCS(0x14, 0x1F),
+ _INIT_CMD_DCS(0x15, 0x1F),
+ _INIT_CMD_DCS(0x16, 0x46),
+ _INIT_CMD_DCS(0x17, 0x46),
+ _INIT_CMD_DCS(0x18, 0x44),
+ _INIT_CMD_DCS(0x19, 0x44),
+ _INIT_CMD_DCS(0x1A, 0x4A),
+ _INIT_CMD_DCS(0x1B, 0x4A),
+ _INIT_CMD_DCS(0x1C, 0x48),
+ _INIT_CMD_DCS(0x1D, 0x48),
+ _INIT_CMD_DCS(0x1E, 0x40),
+ _INIT_CMD_DCS(0x1F, 0x1F),
+ _INIT_CMD_DCS(0x20, 0x1F),
+ _INIT_CMD_DCS(0x21, 0x1F),
+ _INIT_CMD_DCS(0x22, 0x1F),
+ _INIT_CMD_DCS(0x23, 0x1F),
+ _INIT_CMD_DCS(0x24, 0x1F),
+ _INIT_CMD_DCS(0x25, 0x5F),
+ _INIT_CMD_DCS(0x26, 0x5F),
+ _INIT_CMD_DCS(0x27, 0x57),
+ _INIT_CMD_DCS(0x28, 0x77),
+ _INIT_CMD_DCS(0x29, 0x35),
+ _INIT_CMD_DCS(0x2A, 0x1F),
+ _INIT_CMD_DCS(0x2B, 0x1F),
+ _INIT_CMD_DCS(0x58, 0x40),
+ _INIT_CMD_DCS(0x59, 0x00),
+ _INIT_CMD_DCS(0x5A, 0x00),
+ _INIT_CMD_DCS(0x5B, 0x10),
+ _INIT_CMD_DCS(0x5C, 0x06),
+ _INIT_CMD_DCS(0x5D, 0x40),
+ _INIT_CMD_DCS(0x5E, 0x01),
+ _INIT_CMD_DCS(0x5F, 0x02),
+ _INIT_CMD_DCS(0x60, 0x30),
+ _INIT_CMD_DCS(0x61, 0x01),
+ _INIT_CMD_DCS(0x62, 0x02),
+ _INIT_CMD_DCS(0x63, 0x03),
+ _INIT_CMD_DCS(0x64, 0x6B),
+ _INIT_CMD_DCS(0x65, 0x05),
+ _INIT_CMD_DCS(0x66, 0x0C),
+ _INIT_CMD_DCS(0x67, 0x73),
+ _INIT_CMD_DCS(0x68, 0x09),
+ _INIT_CMD_DCS(0x69, 0x03),
+ _INIT_CMD_DCS(0x6A, 0x56),
+ _INIT_CMD_DCS(0x6B, 0x08),
+ _INIT_CMD_DCS(0x6C, 0x00),
+ _INIT_CMD_DCS(0x6D, 0x04),
+ _INIT_CMD_DCS(0x6E, 0x04),
+ _INIT_CMD_DCS(0x6F, 0x88),
+ _INIT_CMD_DCS(0x70, 0x00),
+ _INIT_CMD_DCS(0x71, 0x00),
+ _INIT_CMD_DCS(0x72, 0x06),
+ _INIT_CMD_DCS(0x73, 0x7B),
+ _INIT_CMD_DCS(0x74, 0x00),
+ _INIT_CMD_DCS(0x75, 0xF8),
+ _INIT_CMD_DCS(0x76, 0x00),
+ _INIT_CMD_DCS(0x77, 0xD5),
+ _INIT_CMD_DCS(0x78, 0x2E),
+ _INIT_CMD_DCS(0x79, 0x12),
+ _INIT_CMD_DCS(0x7A, 0x03),
+ _INIT_CMD_DCS(0x7B, 0x00),
+ _INIT_CMD_DCS(0x7C, 0x00),
+ _INIT_CMD_DCS(0x7D, 0x03),
+ _INIT_CMD_DCS(0x7E, 0x7B),
+ _INIT_CMD_DCS(0xE0, 0x04),
+ _INIT_CMD_DCS(0x00, 0x0E),
+ _INIT_CMD_DCS(0x02, 0xB3),
+ _INIT_CMD_DCS(0x09, 0x60),
+ _INIT_CMD_DCS(0x0E, 0x2A),
+ _INIT_CMD_DCS(0x36, 0x59),
+ _INIT_CMD_DCS(0xE0, 0x00),
+
+ _INIT_CMD_DELAY(120),
+};
+
+static const struct display_timing jadard_timing[] = {
+ {
+ .pixelclock = { 79200000, 79200000, 79200000 },
+ .hactive = { 800, 800, 800 },
+ .hfront_porch = { 356, 356, 356 },
+ .hback_porch = { 134, 134, 134 },
+ .hsync_len = { 7, 7, 7 },
+ .vactive = { 1280, 1280, 1280 },
+ .vfront_porch = { 84, 84, 84 },
+ .vback_porch = { 20, 20, 20 },
+ .vsync_len = { 9, 9, 9 },
+ .flags = DISPLAY_FLAGS_DE_LOW,
+ },
+ {
+ .pixelclock = { 148500000, 148500000, 148500000 },
+ .hactive = { 1200, 1200, 1200 },
+ .hfront_porch = { 246, 246, 246 },
+ .hback_porch = { 5, 5, 5 },
+ .hsync_len = { 5, 5, 5 },
+ .vactive = { 1920, 1920, 1920 },
+ .vfront_porch = { 84, 84, 84 },
+ .vback_porch = { 20, 20, 20 },
+ .vsync_len = { 16, 16, 16 },
+ .flags = DISPLAY_FLAGS_DE_LOW,
+ },
+ {}
+};
+
+static const struct jadard_panel_desc cz101b4001_desc[] = {
+ {
+ .mode = {
+ .clock = 79200,
+
+ .hdisplay = 800,
+ .hsync_start = 800 + 180,
+ .hsync_end = 800 + 180 + 15,
+ .htotal = 800 + 180 + 15 + 45,
+
+ .vdisplay = 1280,
+ .vsync_start = 1280 + 84,
+ .vsync_end = 1280 + 84 + 20,
+ .vtotal = 1280 + 84 + 20 + 7,
+
+ .width_mm = 62,
+ .height_mm = 110,
+ .type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
+ },
+ .lanes = 4,
+ .format = MIPI_DSI_FMT_RGB888,
+ .init_cmds = cz101b4001_init_cmds,
+ .num_init_cmds = ARRAY_SIZE(cz101b4001_init_cmds),
+ .timings = &jadard_timing[0],
+ .num_timings = 1,
+ },
+ {
+ .mode = {
+ .clock = 148500,
+
+ .hdisplay = 1200,
+ .hsync_start = 1200 + 246,
+ .hsync_end = 1200 + 246 + 5,
+ .htotal = 1200 + 246 + 5 + 5,
+
+ .vdisplay = 1920,
+ .vsync_start = 1920 + 84,
+ .vsync_end = 1920 + 84 + 20,
+ .vtotal = 1920 + 84 + 20 + 16,
+
+ .width_mm = 62,
+ .height_mm = 110,
+ .type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
+ },
+ .lanes = 4,
+ .format = MIPI_DSI_FMT_RGB888,
+ .init_cmds = cz101b4001_init_cmds,//no
+ .num_init_cmds = ARRAY_SIZE(cz101b4001_init_cmds),//no
+ //.timings = &starfive_timing,
+ .timings = &jadard_timing[1],
+ .num_timings = 1,
+ },
+ {}
+};
+
+static int panel_probe(struct i2c_client *client)//, const struct i2c_device_id *id
+{
+ u8 reg_value = 0;
+ struct jadard *jd_panel;
+ const struct jadard_panel_desc *desc;
+
+ struct device_node *endpoint, *dsi_host_node;
+ struct mipi_dsi_host *host;
+ struct device *dev = &client->dev;
+
+ const struct st_sensor_settings *settings;
+ struct st_sensor_data *adata;
+ struct iio_dev *indio_dev;
+ int err; u8 mode = 1; int ret = 0;
+
+
+ struct mipi_dsi_device_info info = {
+ .type = DSI_DRIVER_NAME,
+ .channel = 1, //0,
+ .node = NULL,
+ };
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ dev_warn(&client->dev,
+ "I2C adapter doesn't support I2C_FUNC_SMBUS_BYTE\n");
+ return -EIO;
+ }
+
+ jd_panel = devm_kzalloc(&client->dev, sizeof(struct jadard), GFP_KERNEL);
+ if (!jd_panel)
+ return -ENOMEM;
+
+ desc = &cz101b4001_desc[0];//use 8inch parameter to pre config dsi and phy
+
+ jd_panel->client = client;
+ i2c_set_clientdata(client, jd_panel);
+
+ jd_panel->enable_initialized = false;
+
+ jd_panel->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(jd_panel->reset)) {
+ DRM_DEV_ERROR(dev, "failed to get our reset GPIO\n");
+ return PTR_ERR(jd_panel->reset);
+ }
+
+ jd_panel->enable = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW);
+ if (IS_ERR(jd_panel->enable)) {
+ DRM_DEV_ERROR(dev, "failed to get our enable GPIO\n");
+ return PTR_ERR(jd_panel->enable);
+ }
+
+ /*use i2c read to detect whether the panel has connected */
+ ret = jadard_i2c_read(client, 0x00, &reg_value);
+ if (ret < 0) {
+ dev_info(dev, "no 4lane connect!!!!\n");
+ return -ENODEV;
+ }
+ dev_info(dev, "==4lane panel!!! maybe 8inch==\n");
+ jd_panel->choosemode = 0;
+ endpoint = of_graph_get_next_endpoint(dev->of_node, NULL);
+ if (!endpoint)
+ return -ENODEV;
+
+ dsi_host_node = of_graph_get_remote_port_parent(endpoint);
+ if (!dsi_host_node)
+ goto error;
+
+ host = of_find_mipi_dsi_host_by_node(dsi_host_node);
+ of_node_put(dsi_host_node);
+ if (!host) {
+ of_node_put(endpoint);
+ return -EPROBE_DEFER;
+ }
+
+ drm_panel_init(&jd_panel->panel, dev, &jadard_funcs,
+ DRM_MODE_CONNECTOR_DSI);
+
+ drm_panel_add(&jd_panel->panel);
+
+ info.node = of_node_get(of_graph_get_remote_port(endpoint));
+ if (!info.node)
+ goto error;
+
+ of_node_put(endpoint);
+ jd_panel->desc = desc;
+
+ jd_panel->dsi = mipi_dsi_device_register_full(host, &info);
+ if (IS_ERR(jd_panel->dsi)) {
+ dev_err(dev, "DSI device registration failed: %ld\n",
+ PTR_ERR(jd_panel->dsi));
+ return PTR_ERR(jd_panel->dsi);
+ }
+
+ mipi_dsi_set_drvdata(jd_panel->dsi, jd_panel);
+
+ //radxa 10inch connect detect
+ gpiod_direction_output(jd_panel->enable, 0);
+ gpiod_set_value(jd_panel->enable, 1);
+ mdelay(100);
+
+ gpiod_direction_output(jd_panel->reset, 0);
+ mdelay(100);
+ gpiod_set_value(jd_panel->reset, 1);
+ mdelay(100);
+ gpiod_set_value(jd_panel->reset, 0);
+ mdelay(100);
+ gpiod_set_value(jd_panel->reset, 1);
+ mdelay(150);
+
+ jd_panel->dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
+ if(jd_panel->dsi) {
+ //use this command to detect the connected status
+ err = mipi_dsi_dcs_get_power_mode(jd_panel->dsi, &mode);
+ dev_info(dev, "dsi command return %d, mode %d\n", err, mode);
+ if (err == -EIO) {
+ dev_info(dev, "raxda 10 inch detected\n");
+ jd_panel->choosemode = 1;
+ desc = &cz101b4001_desc[1];//choose 1200x1920 mode
+ jd_panel->desc = desc;
+ jd_panel->dsi->hs_rate = 980000000;//after this, dsi and phy will config again
+ } else {
+ dev_info(dev, "4lane is radxa 8inch\n");
+ jd_panel->dsi->hs_rate = 490000000;
+ }
+ }
+ jd_panel->dsi->mode_flags |= MIPI_DSI_MODE_LPM;
+
+ //acceleroter SC7A20
+ dev_info(dev, "probe sc7a20 begin\n");
+ settings = st_accel_get_settings("sc7a20");
+ if (!settings) {
+ dev_err(&client->dev, "device name %s not recognized.\n",
+ client->name);
+ return -ENODEV;
+ }
+
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*adata));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ adata = iio_priv(indio_dev);
+ adata->sensor_settings = (struct st_sensor_settings *)settings;
+
+ ret = st_sensors_i2c_configure(indio_dev, client);
+ if (ret < 0)
+ return ret;
+
+ ret = st_sensors_power_enable(indio_dev);
+ if (ret)
+ return ret;
+ st_accel_common_probe(indio_dev);
+ dev_info(dev, "probe sc7a20 end\n");
+
+ return 0;
+error:
+ of_node_put(endpoint);
+ return -ENODEV;
+no_panel:
+ mipi_dsi_device_unregister(jd_panel->dsi);
+ //drm_panel_remove(&jd_panel->panel);
+ //mipi_dsi_detach(jd_panel->dsi);
+
+ return -ENODEV;
+
+
+}
+
+static void panel_remove(struct i2c_client *client)
+{
+ struct jadard *jd_panel = i2c_get_clientdata(client);
+
+ mipi_dsi_detach(jd_panel->dsi);
+ drm_panel_remove(&jd_panel->panel);
+ mipi_dsi_device_unregister(jd_panel->dsi);
+}
+
+static const struct i2c_device_id panel_id[] = {
+ { "starfive_jadard", 0 },
+ { }
+};
+
+static const struct of_device_id panel_dt_ids[] = {
+ { .compatible = "starfive_jadard", .data = &cz101b4001_desc},
+ { /* sentinel */ }
+};
+
+static struct i2c_driver panel_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "starfive_jadard",
+ .of_match_table = panel_dt_ids,
+ },
+ .probe = panel_probe,
+ .remove = panel_remove,
+ .id_table = panel_id,
+};
+
+static int jadard_dsi_probe(struct mipi_dsi_device *dsi)
+{
+ struct device *dev = &dsi->dev;
+ struct jadard *jadard = mipi_dsi_get_drvdata(dsi);
+
+ int ret;
+
+ dsi->mode_flags = MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE ;
+ dsi->format = MIPI_DSI_FMT_RGB888;
+ dsi->lanes = 4;
+ dsi->channel = 1;
+ dsi->hs_rate = 490000000;
+
+ ret = mipi_dsi_attach(dsi);
+ if (ret < 0) {
+ return ret;
+ }
+
+ return 0;
+}
+
+static void jadard_dsi_remove(struct mipi_dsi_device *dsi)
+{
+ struct jadard *jadard = mipi_dsi_get_drvdata(dsi);
+
+ mipi_dsi_detach(dsi);
+ drm_panel_remove(&jadard->panel);
+}
+
+static const struct of_device_id jadard_of_match[] = {
+ { .compatible = "starfive-dri-panel-1", .data = &cz101b4001_desc },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, jadard_of_match);
+
+static struct mipi_dsi_driver jadard_mipi_driver = {
+ .probe = jadard_dsi_probe,
+ .remove = jadard_dsi_remove,
+ .driver.name = DSI_DRIVER_NAME,
+};
+//module_mipi_dsi_driver(jadard_driver);
+
+
+static int __init init_panel(void)
+{
+ int err;
+
+ mipi_dsi_driver_register(&jadard_mipi_driver);
+ err = i2c_add_driver(&panel_driver);
+
+ return err;
+
+}
+module_init(init_panel);
+
+static void __exit exit_panel(void)
+{
+ i2c_del_driver(&panel_driver);
+ mipi_dsi_driver_unregister(&jadard_mipi_driver);
+}
+module_exit(exit_panel);
+
+
+MODULE_AUTHOR("Jagan Teki <jagan@edgeble.ai>");
+MODULE_AUTHOR("Stephen Chen <stephen@radxa.com>");
+MODULE_DESCRIPTION("Jadard JD9365DA-H3 WUXGA DSI panel");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/gpu/drm/verisilicon/Kconfig b/drivers/gpu/drm/verisilicon/Kconfig
index ad737f780401..8f0a04bd0fc0 100644
--- a/drivers/gpu/drm/verisilicon/Kconfig
+++ b/drivers/gpu/drm/verisilicon/Kconfig
@@ -7,7 +7,7 @@ config DRM_VERISILICON
select DRM_GEM_DMA_HELPER
select CMA
select DMA_CMA
- select SOC_STARFIVE_EVB_VOUT
+ select SOC_STARFIVE_VF2_VOUT
help
Choose this option if you have a VeriSilicon soc chipset.
This driver provides VeriSilicon kernel mode
diff --git a/drivers/gpu/drm/verisilicon/starfive_drm_dsi.c b/drivers/gpu/drm/verisilicon/starfive_drm_dsi.c
index 104fe10624f3..8f8235cef2d0 100644
--- a/drivers/gpu/drm/verisilicon/starfive_drm_dsi.c
+++ b/drivers/gpu/drm/verisilicon/starfive_drm_dsi.c
@@ -868,13 +868,13 @@ static int cdns_dsi_bridge_attach(struct drm_bridge *bridge,
struct cdns_dsi *dsi = input_to_dsi(input);
struct cdns_dsi_output *output = &dsi->output;
- dev_info(dsi->base.dev, "===>cdns_dsi_bridge_attach begin\n");
+ dev_info(dsi->base.dev, "cdns_dsi_bridge_attach begin\n");
if (!drm_core_check_feature(bridge->dev, DRIVER_ATOMIC)) {
dev_err(dsi->base.dev,
"cdns-dsi driver is only compatible with DRM devices supporting atomic updates");
return -EOPNOTSUPP;
}
- dev_info(dsi->base.dev, "===>cdns_dsi_bridge_attach end\n");
+ dev_info(dsi->base.dev, "cdns_dsi_bridge_attach end\n");
return drm_bridge_attach(bridge->encoder, output->bridge, bridge,
flags);
}
@@ -887,8 +887,7 @@ cdns_dsi_bridge_mode_valid(struct drm_bridge *bridge,
struct cdns_dsi_input *input = bridge_to_cdns_dsi_input(bridge);
struct cdns_dsi *dsi = input_to_dsi(input);
struct cdns_dsi_output *output = &dsi->output;
- struct cdns_dsi_cfg dsi_cfg;
- int bpp, ret;
+ int bpp;
/*
* VFP_DSI should be less than VFP_DPI and VFP_DSI should be at
@@ -906,10 +905,6 @@ cdns_dsi_bridge_mode_valid(struct drm_bridge *bridge,
if ((mode->hdisplay * bpp) % 32)
return MODE_H_ILLEGAL;
- ret = cdns_dsi_check_conf(dsi, mode, &dsi_cfg, true);
- //if (ret)
- // return MODE_BAD;
-
return MODE_OK;
}
@@ -953,7 +948,6 @@ static void cdns_dsi_bridge_disable(struct drm_bridge *bridge)
struct cdns_dsi *dsi = input_to_dsi(input);
u32 val;
- dsi->link_initialized = false;
val = readl(dsi->regs + MCTL_MAIN_DATA_CTL);
val &= ~(IF_VID_SELECT_MASK | IF_VID_MODE | VID_EN | HOST_EOT_GEN |
DISP_EOT_GEN);
@@ -962,24 +956,15 @@ static void cdns_dsi_bridge_disable(struct drm_bridge *bridge)
val = readl(dsi->regs + MCTL_MAIN_EN) & ~IF_EN(input->id);
writel(val, dsi->regs + MCTL_MAIN_EN);
pm_runtime_put(dsi->base.dev);
- sys_mipi_dsi_set_ppi_txbyte_hs(0, dsi);
- phy_power_off(dsi->dphy);
- phy_exit(dsi->dphy);
-
}
static void cdns_dsi_hs_init(struct cdns_dsi *dsi)
{
struct cdns_dsi_output *output = &dsi->output;
- //u32 dpi_fifo_int = 0;
int ret;
- /*
- * Power all internal DPHY blocks down and maintain their reset line
- * asserted before changing the DPHY config.
- */
- writel(DPHY_CMN_PSO | DPHY_PLL_PSO | DPHY_ALL_D_PDN | DPHY_C_PDN |
- DPHY_CMN_PDN | DPHY_PLL_PDN,
- dsi->regs + MCTL_DPHY_CFG0);
+
+ if (dsi->link_initialized)
+ return;
phy_init(dsi->dphy);
phy_set_mode(dsi->dphy, PHY_MODE_MIPI_DPHY);
@@ -988,33 +973,19 @@ static void cdns_dsi_hs_init(struct cdns_dsi *dsi)
ret = sys_mipi_dsi_set_ppi_txbyte_hs(1, dsi);
- writel(PLL_LOCKED, dsi->regs + MCTL_MAIN_STS_CLR);
- writel(DPHY_CMN_PSO | DPHY_ALL_D_PDN | DPHY_C_PDN | DPHY_CMN_PDN,
- dsi->regs + MCTL_DPHY_CFG0);
- mdelay(100);
- /* De-assert data and clock reset lines. */
- writel(DPHY_CMN_PSO | DPHY_ALL_D_PDN | DPHY_C_PDN | DPHY_CMN_PDN |
- DPHY_D_RSTB(output->dev->lanes) | DPHY_C_RSTB,
- dsi->regs + MCTL_DPHY_CFG0);
-
-/*
- dpi_fifo_int = readl(dsi->regs + DPI_IRQ_CLR);
- if (dpi_fifo_int)
- writel(1, dsi->regs + DPI_IRQ_CLR);
-*/
}
static void cdns_dsi_init_link(struct cdns_dsi *dsi)
{
struct cdns_dsi_output *output = &dsi->output;
- unsigned long sysclk_period, ulpout;
+ unsigned long ulpout;
u32 val;
int i;
if (dsi->link_initialized)
return;
+ cdns_dsi_hs_init(dsi);
- //val = 0;
val = WAIT_BURST_TIME(0xf);
for (i = 1; i < output->dev->lanes; i++)
val |= DATA_LANE_EN(i);
@@ -1024,24 +995,63 @@ static void cdns_dsi_init_link(struct cdns_dsi *dsi)
writel(val, dsi->regs + MCTL_MAIN_PHY_CTL);
- /* ULPOUT should be set to 1ms and is expressed in sysclk cycles.*/
- sysclk_period = NSEC_PER_SEC / clk_get_rate(dsi->dsi_sys_clk);
- ulpout = DIV_ROUND_UP(NSEC_PER_MSEC, sysclk_period);
+ ulpout = DIV_ROUND_UP(clk_get_rate(dsi->dsi_sys_clk), MSEC_PER_SEC);
+
writel(CLK_LANE_ULPOUT_TIME(ulpout) | DATA_LANE_ULPOUT_TIME(ulpout),
dsi->regs + MCTL_ULPOUT_TIME);
writel(LINK_EN, dsi->regs + MCTL_MAIN_DATA_CTL);
- val = CLK_LANE_EN | PLL_START;
+ if (output->dev->mode_flags & MIPI_DSI_MODE_LPM){
+ val = CLK_LANE_EN | CLK_FORCE_STOP;
+ } else {
+ val = CLK_LANE_EN;
+ }
for (i = 0; i < output->dev->lanes; i++)
val |= DATA_LANE_START(i);
writel(val, dsi->regs + MCTL_MAIN_EN);
+ udelay(20);
dsi->link_initialized = true;
}
+static void start_clane_hs(struct cdns_dsi *dsi)
+{
+ // struct cdns_dsi *dsi = (struct cdns_dsi *)handle->priv;
+ struct cdns_dsi_output *output = &dsi->output;
+
+ unsigned long ulpout;
+ u32 val;
+ int i;
+
+ val = WAIT_BURST_TIME(0xf);
+ for (i = 1; i < output->dev->lanes; i++)
+ val |= DATA_LANE_EN(i);
+
+ if (!(output->dev->mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS))
+ val |= CLK_CONTINUOUS;
+
+ writel(val, dsi->regs + MCTL_MAIN_PHY_CTL);
+
+ ulpout = DIV_ROUND_UP(clk_get_rate(dsi->dsi_sys_clk), MSEC_PER_SEC);
+ writel(CLK_LANE_ULPOUT_TIME(ulpout) | DATA_LANE_ULPOUT_TIME(ulpout),
+ dsi->regs + MCTL_ULPOUT_TIME);
+
+ writel(LINK_EN, dsi->regs + MCTL_MAIN_DATA_CTL);
+
+ val = CLK_LANE_EN; // | CLK_FORCE_STOP; // | PLL_START; unused bit
+ for (i = 0; i < output->dev->lanes; i++)
+ val |= DATA_LANE_START(i);
+
+ writel(val, dsi->regs + MCTL_MAIN_EN); // start hs clk lane
+
+ // clane output DDR(Double Data Rate) clock = half of dphy hs_rate, because hs is double edge sampling
+
+ dsi->link_initialized = true;
+}
+
static void cdns_dsi_bridge_enable(struct drm_bridge *bridge)
{
struct cdns_dsi_input *input = bridge_to_cdns_dsi_input(bridge);
@@ -1064,10 +1074,15 @@ static void cdns_dsi_bridge_enable(struct drm_bridge *bridge)
//WARN_ON_ONCE(cdns_dsi_check_conf(dsi, mode, &dsi_cfg, false));//original //7110 mode illegal,need confirm //cannot disable
cdns_dsi_check_conf(dsi, mode, &dsi_cfg, false);
-
- cdns_dsi_hs_init(dsi);
+ //cdns_dsi_hs_init(dsi);
cdns_dsi_init_link(dsi);
+ if (output->panel)
+ drm_panel_prepare(output->panel);
+
+ if (output->panel)
+ drm_panel_enable(output->panel);
+
writel(HBP_LEN(dsi_cfg.hbp) | HSA_LEN(dsi_cfg.hsa),
dsi->regs + VID_HSIZE1);
writel(HFP_LEN(dsi_cfg.hfp) | HACT_LEN(dsi_cfg.hact),
@@ -1103,10 +1118,11 @@ static void cdns_dsi_bridge_enable(struct drm_bridge *bridge)
tx_byte_period = DIV_ROUND_DOWN_ULL((u64)NSEC_PER_SEC * 8,
phy_cfg->hs_clk_rate);
reg_wakeup = (phy_cfg->hs_prepare + phy_cfg->hs_zero) / tx_byte_period;
+
writel(REG_WAKEUP_TIME(reg_wakeup) | REG_LINE_DURATION(tmp),
dsi->regs + VID_DPHY_TIME);
- vrefresh = drm_mode_vrefresh(mode);//display_timing_vrefresh(dpi);
+ vrefresh = drm_mode_vrefresh(mode)-1;//display_timing_vrefresh(dpi);
tmp = NSEC_PER_SEC / vrefresh;
tmp /= tx_byte_period;
@@ -1163,6 +1179,9 @@ static void cdns_dsi_bridge_enable(struct drm_bridge *bridge)
writel(tmp, dsi->regs + VID_MAIN_CTL);
}
+ //to restart the clane and dlane
+ start_clane_hs(dsi);
+
tmp = readl(dsi->regs + MCTL_MAIN_DATA_CTL);
tmp &= ~(IF_VID_SELECT_MASK | HOST_EOT_GEN | IF_VID_MODE);
@@ -1201,6 +1220,7 @@ static int cdns_dsi_attach(struct mipi_dsi_host *host,
* same host. In order to support that we'd need the DRM bridge
* framework to allow dynamic reconfiguration of the bridge chain.
*/
+
if (output->dev)
return -EBUSY;
@@ -1241,12 +1261,15 @@ static int cdns_dsi_attach(struct mipi_dsi_host *host,
output->dev = dev;
output->bridge = bridge;
output->panel = panel;
+ output->phy_opts.mipi_dphy.hs_clk_rate = dev->hs_rate;
/*
* The DSI output has been properly configured, we can now safely
* register the input to the bridge framework so that it can take place
* in a display pipeline.
*/
+
+ dev_info(host->dev, "hs_rate.%ld, channel = %d\n" ,dev->hs_rate,dev->channel);
drm_bridge_add(&input->bridge);
return 0;
}
@@ -1261,6 +1284,7 @@ static int cdns_dsi_detach(struct mipi_dsi_host *host,
drm_bridge_remove(&input->bridge);
if (output->panel)
drm_panel_bridge_remove(output->bridge);
+ output->dev = NULL;
return 0;
}
@@ -1271,6 +1295,9 @@ static irqreturn_t cdns_dsi_interrupt(int irq, void *data)
irqreturn_t ret = IRQ_NONE;
u32 flag, ctl;
+ if (dsi->link_initialized == false)
+ ret = IRQ_HANDLED;
+
flag = readl(dsi->regs + DIRECT_CMD_STS_FLAG);
if (flag) {
ctl = readl(dsi->regs + DIRECT_CMD_STS_CTL);
@@ -1287,16 +1314,11 @@ static ssize_t cdns_dsi_transfer(struct mipi_dsi_host *host,
const struct mipi_dsi_msg *msg)
{
struct cdns_dsi *dsi = to_cdns_dsi(host);
- u32 cmd, val, wait = WRITE_COMPLETED, ctl = 0;
+ u32 cmd, sts, val, wait = WRITE_COMPLETED, ctl = 0;
struct mipi_dsi_packet packet;
int ret, i, tx_len, rx_len;
- u32 stat = 0;
- int timeout = 100;
- int stat_88;
- int stat_188;
- int stat_88_ack_val;
- ret = pm_runtime_get_sync(host->dev);
+ ret = pm_runtime_resume_and_get(host->dev);
if (ret < 0)
return ret;
@@ -1311,23 +1333,23 @@ static ssize_t cdns_dsi_transfer(struct mipi_dsi_host *host,
/* For read operations, the maximum TX len is 2. */
if (rx_len && tx_len > 2) {
- ret = -EOPNOTSUPP;
+ ret = -ENOTSUPP;
goto out;
}
/* TX len is limited by the CMD FIFO depth. */
if (tx_len > dsi->direct_cmd_fifo_depth) {
- ret = -EOPNOTSUPP;
+ ret = -ENOTSUPP;
goto out;
}
/* RX len is limited by the RX FIFO depth. */
if (rx_len > dsi->rx_fifo_depth) {
- ret = -EOPNOTSUPP;
+ ret = -ENOTSUPP;
goto out;
}
- cmd = CMD_SIZE(tx_len) | CMD_VCHAN_ID(msg->channel) |
+ cmd = CMD_SIZE(tx_len) | CMD_VCHAN_ID(0) |
CMD_DATATYPE(msg->type);
if (msg->flags & MIPI_DSI_MSG_USE_LPM)
@@ -1346,11 +1368,10 @@ static ssize_t cdns_dsi_transfer(struct mipi_dsi_host *host,
ctl = BTA_EN;
}
- /* Clear status flags before sending the command. */
+ writel(readl(dsi->regs + MCTL_MAIN_DATA_CTL) | ctl,
+ dsi->regs + MCTL_MAIN_DATA_CTL);
- iowrite32(wait, dsi->regs + DIRECT_CMD_STS_CLR);
- iowrite32(wait, dsi->regs + DIRECT_CMD_STS_CTL);
- iowrite32(cmd, dsi->regs + DIRECT_CMD_MAIN_SETTINGS);
+ writel(cmd, dsi->regs + DIRECT_CMD_MAIN_SETTINGS);
for (i = 0; i < tx_len; i += 4) {
const u8 *buf = msg->tx_buf;
@@ -1359,24 +1380,46 @@ static ssize_t cdns_dsi_transfer(struct mipi_dsi_host *host,
val = 0;
for (j = 0; j < 4 && j + i < tx_len; j++)
val |= (u32)buf[i + j] << (8 * j);
- iowrite32(val, dsi->regs + DIRECT_CMD_WRDATA);
+
+ writel(val, dsi->regs + DIRECT_CMD_WRDATA);
}
- iowrite32(0, dsi->regs + DIRECT_CMD_SEND);
- do {
- stat = readl(dsi->regs + DIRECT_CMD_STS);
- if ((stat & 0x02) == 0x02)
- break;
- mdelay(10);
- } while (--timeout);
- if (!timeout)
- DRM_DEBUG("timeout!\n");
+ /* Clear status flags before sending the command. */
+ writel(wait, dsi->regs + DIRECT_CMD_STS_CLR);
+ writel(wait, dsi->regs + DIRECT_CMD_STS_CTL);
+ reinit_completion(&dsi->direct_cmd_comp);
+ writel(0, dsi->regs + DIRECT_CMD_SEND);
- stat_88 = readl(dsi->regs + DIRECT_CMD_STS);
- stat_188 = readl(dsi->regs + MCTL_DPHY_ERR_FLAG);
- stat_88_ack_val = stat_88 >> 16;
- if (stat_188 || stat_88_ack_val)
- dev_dbg(host->dev, "stat: [188h] %08x, [88h] %08x\r\n", stat_188, stat_88);
+ wait_for_completion_timeout(&dsi->direct_cmd_comp,
+ msecs_to_jiffies(1000));
+
+ sts = readl(dsi->regs + DIRECT_CMD_STS);
+ writel(wait, dsi->regs + DIRECT_CMD_STS_CLR);
+ writel(0, dsi->regs + DIRECT_CMD_STS_CTL);
+
+ writel(readl(dsi->regs + MCTL_MAIN_DATA_CTL) & ~ctl,
+ dsi->regs + MCTL_MAIN_DATA_CTL);
+
+ /* We did not receive the events we were waiting for. */
+ if (!(sts & wait)) {
+ ret = -ETIMEDOUT;
+ goto out;
+ }
+
+ /* 'READ' or 'WRITE with ACK' failed. */
+ if (sts & (READ_COMPLETED_WITH_ERR | ACK_WITH_ERR_RCVD)) {
+ ret = -EIO;
+ goto out;
+ }
+
+ for (i = 0; i < rx_len; i += 4) {
+ u8 *buf = msg->rx_buf;
+ int j;
+
+ val = readl(dsi->regs + DIRECT_CMD_RDDATA);
+ for (j = 0; j < 4 && j + i < rx_len; j++)
+ buf[i + j] = val >> (8 * j);
+ }
out:
pm_runtime_put(host->dev);
@@ -1413,8 +1456,8 @@ static int cdns_dsi_runtime_resume(struct device *dev)
dev_err(dsi->base.dev, "failed to enable clock\n");
return ret;
}
- enable_irq(dsi->irq);
+ enable_irq(dsi->irq);
ret = cdns_dsi_resets_deassert(dsi, dsi->base.dev);
if (ret < 0) {
dev_err(dsi->base.dev, "failed to deassert reset\n");
@@ -1428,6 +1471,13 @@ static int cdns_dsi_runtime_suspend(struct device *dev)
{
struct cdns_dsi *dsi = dev_get_drvdata(dev);
int ret;
+ disable_irq(dsi->irq);
+
+ if (dsi->link_initialized == true) {
+ sys_mipi_dsi_set_ppi_txbyte_hs(0, dsi);
+ phy_power_off(dsi->dphy);
+ phy_exit(dsi->dphy);
+ }
ret = cdns_dsi_resets_assert(dsi, dsi->base.dev);
if (ret < 0)
@@ -1436,7 +1486,7 @@ static int cdns_dsi_runtime_suspend(struct device *dev)
cdns_dsi_clock_disable(dsi);
dsi->link_initialized = false;
- disable_irq(dsi->irq);
+
return 0;
}
diff --git a/drivers/gpu/drm/verisilicon/vs_dc.c b/drivers/gpu/drm/verisilicon/vs_dc.c
index 5cd68a5c8af2..9a74f2458028 100644
--- a/drivers/gpu/drm/verisilicon/vs_dc.c
+++ b/drivers/gpu/drm/verisilicon/vs_dc.c
@@ -659,7 +659,7 @@ static int dc_init(struct device *dev)
to set value*/
clk_set_rate(dc->dc8200_pix0, 1000);
clk_set_parent(dc->dc8200_clk_pix1, dc->hdmitx0_pixelclk);
- clk_set_parent(dc->vout_top_lcd, dc->dc8200_clk_pix0);
+ clk_set_parent(dc->vout_top_lcd, dc->dc8200_clk_pix0_out);
clk_set_parent(dc->dc8200_clk_pix0, dc->dc8200_pix0);
return 0;
@@ -749,7 +749,7 @@ static void vs_dc_enable(struct device *dev, struct drm_crtc *crtc)
{
clk_set_rate(dc->dc8200_pix0, mode->clock * 1000);
clk_set_parent(dc->dc8200_clk_pix1, dc->dc8200_pix0);
- clk_set_parent(dc->vout_top_lcd, dc->dc8200_clk_pix1);
+ clk_set_parent(dc->vout_top_lcd, dc->dc8200_clk_pix1_out);
}else{
clk_set_parent(dc->dc8200_clk_pix0, dc->hdmitx0_pixelclk);
}
@@ -800,7 +800,7 @@ static void vs_dc_disable(struct device *dev, struct drm_crtc *crtc)
clk_set_rate(dc->dc8200_pix0, 1000);
/*reset the parent pixclk channel*/
clk_set_parent(dc->dc8200_clk_pix1, dc->hdmitx0_pixelclk);
- clk_set_parent(dc->vout_top_lcd, dc->dc8200_clk_pix0);
+ clk_set_parent(dc->vout_top_lcd, dc->dc8200_clk_pix0_out);
clk_set_parent(dc->dc8200_clk_pix0, dc->dc8200_pix0);
}
diff --git a/drivers/gpu/drm/verisilicon/vs_dc.h b/drivers/gpu/drm/verisilicon/vs_dc.h
index 299b7cea6782..8e18bd7e7f5b 100644
--- a/drivers/gpu/drm/verisilicon/vs_dc.h
+++ b/drivers/gpu/drm/verisilicon/vs_dc.h
@@ -65,6 +65,8 @@ struct vs_dc {
struct clk *vout_top_lcd;
struct clk *hdmitx0_pixelclk;
struct clk *dc8200_pix0;
+ struct clk *dc8200_clk_pix0_out;
+ struct clk *dc8200_clk_pix1_out;
struct reset_control *vout_resets;
struct reset_control *dc8200_rst_axi;
struct reset_control *dc8200_rst_core;
diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c
index b068ff8afbc9..b4d91d394c1b 100644
--- a/drivers/input/touchscreen/goodix.c
+++ b/drivers/input/touchscreen/goodix.c
@@ -515,12 +515,12 @@ static irqreturn_t goodix_ts_irq_handler(int irq, void *dev_id)
static void goodix_free_irq(struct goodix_ts_data *ts)
{
- devm_free_irq(&ts->client->dev, ts->client->irq, ts);
+ devm_free_irq(&ts->client->dev, gpiod_to_irq(ts->gpiod_int), ts);
}
static int goodix_request_irq(struct goodix_ts_data *ts)
{
- return devm_request_threaded_irq(&ts->client->dev, ts->client->irq,
+ return devm_request_threaded_irq(&ts->client->dev, gpiod_to_irq(ts->gpiod_int),
NULL, goodix_ts_irq_handler,
ts->irq_flags, ts->client->name, ts);
}
diff --git a/drivers/media/platform/starfive/v4l2_driver/Readme.txt b/drivers/media/platform/starfive/v4l2_driver/Readme.txt
index fc3dad63d10f..b291f1cb3a7b 100644
--- a/drivers/media/platform/starfive/v4l2_driver/Readme.txt
+++ b/drivers/media/platform/starfive/v4l2_driver/Readme.txt
@@ -4,8 +4,7 @@
ensure linux/arch/riscv/configs/starfive_jh7110_defconfig:
CONFIG_VIDEO_STF_VIN=y
-CONFIG_VIN_SENSOR_SC2235=y
-CONFIG_VIN_SENSOR_OV4689=y
+CONFIG_VIN_SENSOR_IMX219=y
-Only support the lane0/lane5 of dphy as clock lane, lane1/lane2/lane3/lane4
+Only support the lane4/lane5 of dphy as clock lane, lane0/lane1/lane2/lane3
as data lane.
diff --git a/drivers/media/platform/starfive/v4l2_driver/sc2235.c b/drivers/media/platform/starfive/v4l2_driver/sc2235.c
index 0e6e9502f7f5..ffc548ec8c47 100644
--- a/drivers/media/platform/starfive/v4l2_driver/sc2235.c
+++ b/drivers/media/platform/starfive/v4l2_driver/sc2235.c
@@ -1904,7 +1904,7 @@ static struct i2c_driver sc2235_i2c_driver = {
.pm = &sc2235_pm_ops,
},
.id_table = sc2235_id,
- .probe = sc2235_probe,
+ .probe_new = sc2235_probe,
.remove = sc2235_remove,
};
diff --git a/drivers/media/platform/starfive/v4l2_driver/stf_common.h b/drivers/media/platform/starfive/v4l2_driver/stf_common.h
index aafc821b0283..efd2678467af 100644
--- a/drivers/media/platform/starfive/v4l2_driver/stf_common.h
+++ b/drivers/media/platform/starfive/v4l2_driver/stf_common.h
@@ -11,7 +11,7 @@
// #define STF_DEBUG
-// #define USE_CSIDPHY_ONE_CLK_MODE 1
+#define USE_CSIDPHY_ONE_CLK_MODE 1
enum {
ST_DVP = 0x0001,
diff --git a/drivers/media/platform/starfive/v4l2_driver/stf_csi.c b/drivers/media/platform/starfive/v4l2_driver/stf_csi.c
index d204cd111140..c25730f3406b 100644
--- a/drivers/media/platform/starfive/v4l2_driver/stf_csi.c
+++ b/drivers/media/platform/starfive/v4l2_driver/stf_csi.c
@@ -411,11 +411,7 @@ int stf_csi_register(struct stf_csi_dev *csi_dev, struct v4l2_device *v4l2_dev)
struct media_pad *pads = csi_dev->pads;
int ret;
- csi_dev->mipirx_1p8 = devm_regulator_get(dev, "mipirx_1p8");
- if (IS_ERR(csi_dev->mipirx_1p8))
- return PTR_ERR(csi_dev->mipirx_1p8);
-
- csi_dev->mipirx_0p9 = devm_regulator_get(dev, "mipirx_0p9");
+ csi_dev->mipirx_0p9 = devm_regulator_get(dev, "mipi_0p9");
if (IS_ERR(csi_dev->mipirx_0p9))
return PTR_ERR(csi_dev->mipirx_0p9);
diff --git a/drivers/media/platform/starfive/v4l2_driver/stf_csi.h b/drivers/media/platform/starfive/v4l2_driver/stf_csi.h
index f55b67b7ddba..690d2b5fe2e9 100644
--- a/drivers/media/platform/starfive/v4l2_driver/stf_csi.h
+++ b/drivers/media/platform/starfive/v4l2_driver/stf_csi.h
@@ -47,7 +47,6 @@ struct stf_csi_dev {
struct csi_hw_ops *hw_ops;
struct mutex stream_lock;
int stream_count;
- struct regulator *mipirx_1p8;
struct regulator *mipirx_0p9;
};
diff --git a/drivers/media/platform/starfive/v4l2_driver/stf_csi_hw_ops.c b/drivers/media/platform/starfive/v4l2_driver/stf_csi_hw_ops.c
index ff8c2f33ad7a..8141feccbbff 100644
--- a/drivers/media/platform/starfive/v4l2_driver/stf_csi_hw_ops.c
+++ b/drivers/media/platform/starfive/v4l2_driver/stf_csi_hw_ops.c
@@ -42,32 +42,18 @@ static int stf_csi_power_on(struct stf_csi_dev *csi_dev, u8 on)
int ret;
if (on) {
- ret = regulator_enable(csi_dev->mipirx_1p8);
- if (ret) {
- st_err(ST_CSI, "Cannot enable mipirx_1p8 regulator\n");
- goto err_1p8;
- }
-
ret = regulator_enable(csi_dev->mipirx_0p9);
if (ret) {
st_err(ST_CSI, "Cannot enable mipirx_0p9 regulator\n");
- goto err_0p9;
+ return ret;
}
- } else {
- regulator_disable(csi_dev->mipirx_1p8);
+ } else
regulator_disable(csi_dev->mipirx_0p9);
- }
regmap_update_bits(stfcamss->stf_aon_syscon, stfcamss->aon_gp_reg,
BIT(31), BIT(31));
return 0;
-
-err_0p9:
- regulator_disable(csi_dev->mipirx_1p8);
-err_1p8:
- return ret;
-
}
static int stf_csi_clk_enable(struct stf_csi_dev *csi_dev)
diff --git a/drivers/media/platform/starfive/v4l2_driver/stf_csiphy_hw_ops.c b/drivers/media/platform/starfive/v4l2_driver/stf_csiphy_hw_ops.c
index 50beebab436f..2b8f15a04be6 100644
--- a/drivers/media/platform/starfive/v4l2_driver/stf_csiphy_hw_ops.c
+++ b/drivers/media/platform/starfive/v4l2_driver/stf_csiphy_hw_ops.c
@@ -67,12 +67,12 @@ int try_cfg(struct csi2phy_cfg2 *cfg, struct csi2phy_cfg *cfg0,
{
int i = 0;
- cfg->clock_lane = 0;
+ cfg->clock_lane = 4;
cfg->clock1_lane = 5;
- cfg->data_lanes[0] = 1;
- cfg->data_lanes[1] = 2;
- cfg->data_lanes[2] = 3;
- cfg->data_lanes[3] = 4;
+ cfg->data_lanes[0] = 0;
+ cfg->data_lanes[1] = 1;
+ cfg->data_lanes[2] = 2;
+ cfg->data_lanes[3] = 3;
if (cfg0 && cfg1) {
st_debug(ST_CSIPHY, "CSIPHY use 2 clk mode\n");
@@ -101,7 +101,6 @@ int try_cfg(struct csi2phy_cfg2 *cfg, struct csi2phy_cfg *cfg0,
st_debug(ST_CSIPHY, "CSIPHY cfg0 use 1 clk mode\n");
cfg->num_clks = 1;
cfg->num_data_lanes = cfg0->num_data_lanes;
- cfg->clock_lane = cfg->clock1_lane = cfg0->clock_lane;
cfg->lane_polarities[0] = cfg->lane_polarities[1] =
cfg0->lane_polarities[0];
for (i = 0; i < cfg0->num_data_lanes; i++) {
@@ -112,7 +111,6 @@ int try_cfg(struct csi2phy_cfg2 *cfg, struct csi2phy_cfg *cfg0,
st_debug(ST_CSIPHY, "CSIPHY cfg1 use 1 clk mode\n");
cfg->num_clks = 1;
cfg->num_data_lanes = cfg1->num_data_lanes;
- cfg->clock_lane = cfg->clock1_lane = cfg1->clock_lane;
cfg->lane_polarities[0] = cfg->lane_polarities[1] =
cfg1->lane_polarities[0];
for (i = 0; i < cfg1->num_data_lanes; i++) {
diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig
index 7555af5195ec..392212d81c1e 100644
--- a/drivers/net/wireless/Kconfig
+++ b/drivers/net/wireless/Kconfig
@@ -37,6 +37,8 @@ source "drivers/net/wireless/st/Kconfig"
source "drivers/net/wireless/ti/Kconfig"
source "drivers/net/wireless/zydas/Kconfig"
source "drivers/net/wireless/quantenna/Kconfig"
+source "drivers/net/wireless/eswin/Kconfig"
+source "drivers/net/wireless/aic8800/Kconfig"
source "drivers/net/wireless/legacy/Kconfig"
diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile
index 4d7374d567d1..051fe4793340 100644
--- a/drivers/net/wireless/Makefile
+++ b/drivers/net/wireless/Makefile
@@ -25,3 +25,7 @@ obj-$(CONFIG_WLAN_VENDOR_ZYDAS) += zydas/
obj-$(CONFIG_WLAN) += legacy/
obj-$(CONFIG_WLAN) += virtual/
+
+obj-$(CONFIG_USB_WIFI_ECR6600U) += eswin/
+obj-$(CONFIG_AIC_WLAN_SUPPORT) += aic8800/
+
diff --git a/drivers/net/wireless/aic8800/Kconfig b/drivers/net/wireless/aic8800/Kconfig
new file mode 100755
index 000000000000..44970d119b6b
--- /dev/null
+++ b/drivers/net/wireless/aic8800/Kconfig
@@ -0,0 +1,10 @@
+config AIC_WLAN_SUPPORT
+ bool "AIC wireless Support"
+ default n
+ help
+ This is support for aic wireless chip.
+
+if AIC_WLAN_SUPPORT
+source "drivers/net/wireless/aic8800/aic8800_fdrv/Kconfig"
+source "drivers/net/wireless/aic8800/aic_load_fw/Kconfig"
+endif
diff --git a/drivers/net/wireless/aic8800/Makefile b/drivers/net/wireless/aic8800/Makefile
new file mode 100755
index 000000000000..3d06d677af5e
--- /dev/null
+++ b/drivers/net/wireless/aic8800/Makefile
@@ -0,0 +1,71 @@
+CONFIG_AIC_LOADFW_SUPPORT := m
+CONFIG_AIC8800_WLAN_SUPPORT := m
+
+
+obj-$(CONFIG_AIC_LOADFW_SUPPORT) += aic_load_fw/
+obj-$(CONFIG_AIC8800_WLAN_SUPPORT) += aic8800_fdrv/
+
+
+# Platform support list
+CONFIG_PLATFORM_ROCKCHIP ?= n
+CONFIG_PLATFORM_ALLWINNER ?= n
+CONFIG_PLATFORM_AMLOGIC ?= n
+CONFIG_PLATFORM_UBUNTU ?= y
+
+ifeq ($(CONFIG_PLATFORM_ROCKCHIP), y)
+#KDIR := /home/yaya/E/Rockchip/3229/Android7/RK3229_ANDROID7.1_v1.01_20170914/rk3229_Android7.1_v1.01_xml0914/kernel
+#ARCH ?= arm
+#CROSS_COMPILE ?= /home/yaya/E/Rockchip/3229/Android7/RK3229_ANDROID7.1_v1.01_20170914/rk3229_Android7.1_v1.01_xml0914/prebuilts/gcc/linux-x86/arm/arm-eabi-4.6/bin/arm-eabi-
+KDIR := /home/yaya/E/Rockchip/3229/Android9/rk3229_android9.0_box/kernel
+ARCH ?= arm
+CROSS_COMPILE ?= /home/yaya/E/Rockchip/3229/Android9/rk3229_android9.0_box/prebuilts/gcc/linux-x86/arm/gcc-linaro-6.3.1-2017.05-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf-
+#KDIR := /home/yaya/E/Rockchip/3399/rk3399-android-10/kernel
+#ARCH ?= arm64
+#CROSS_COMPILE ?= /home/yaya/E/Rockchip/3399/rk3399-android-10/prebuilts/gcc/linux-x86/aarch64/gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-
+ccflags-y += -DANDROID_PLATFORM
+endif
+
+ifeq ($(CONFIG_PLATFORM_ALLWINNER), y)
+KDIR := /home/yaya/E/Allwinner/R818/R818/AndroidQ/lichee/kernel/linux-4.9
+ARCH ?= arm64
+CROSS_COMPILE ?= /home/yaya/E/Allwinner/R818/R818/AndroidQ/lichee/out/gcc-linaro-5.3.1-2016.05-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-
+ccflags-y += -DANDROID_PLATFORM
+endif
+
+ifeq ($(CONFIG_PLATFORM_AMLOGIC), y)
+ccflags-y += -DANDROID_PLATFORM
+ARCH := arm
+CROSS_COMPILE := /home/yaya/D/Workspace/CyberQuantum/JinHaoYue/amls905x3/SDK/20191101-0tt-asop/android9.0/prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.9/bin/arm-linux-androidkernel-
+KDIR := /home/yaya/D/Workspace/CyberQuantum/JinHaoYue/amls905x3/SDK/20191101-0tt-asop/android9.0/out/target/product/u202/obj/KERNEL_OBJ/
+
+endif
+
+ifeq ($(CONFIG_PLATFORM_UBUNTU), y)
+KDIR := /lib/modules/$(shell uname -r)/build
+PWD := $(shell pwd)
+KVER := $(shell uname -r)
+MODDESTDIR := /lib/modules/$(KVER)/kernel/drivers/net/wireless/aic8800
+ARCH ?= x86_64
+CROSS_COMPILE ?=
+endif
+
+
+all: modules
+modules:
+ make -C $(KDIR) M=$(PWD) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) modules
+
+install:
+ mkdir -p $(MODDESTDIR)
+ install -p -m 644 aic_load_fw/aic_load_fw.ko $(MODDESTDIR)/
+ install -p -m 644 aic8800_fdrv/aic8800_fdrv.ko $(MODDESTDIR)/
+ /sbin/depmod -a ${KVER}
+
+uninstall:
+ rm -rfv $(MODDESTDIR)/aic_load_fw.ko
+ rm -rfv $(MODDESTDIR)/aic8800_fdrv.ko
+ /sbin/depmod -a ${KVER}
+
+clean:
+ cd aic_load_fw/;make clean;cd ..
+ cd aic8800_fdrv/;make clean;cd ..
+ rm -rf modules.order Module.symvers .tmp_versions/
diff --git a/drivers/net/wireless/aic8800/aic8800_fdrv/Kconfig b/drivers/net/wireless/aic8800/aic8800_fdrv/Kconfig
new file mode 100644
index 000000000000..92d598681693
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic8800_fdrv/Kconfig
@@ -0,0 +1,36 @@
+config AIC8800_WLAN_SUPPORT
+ tristate "AIC8800 wlan Support"
+ help
+ This is support for aic wifi driver.
+
+#choice
+# depends on AIC8800_WLAN_SUPPORT
+# prompt "Select 8800 support platform"
+#
+# config PLATFORM_ROCHCHIP
+# bool "PLATFORM_ROCHCHIP"
+# depends on AIC8800_WLAN_SUPPORT
+#
+# config PLATFORM_ALLWINNER
+# bool "PLATFORM_ALLWINNER"
+# depends on AIC8800_WLAN_SUPPORT
+#
+# config PLATFORM_AMLOGIC
+# bool "PLATFORM_AMLOGIC"
+# depends on AIC8800_WLAN_SUPPORT
+#
+# config PLATFORM_UBUNTU
+# bool "PLATFORM_UBUNTU"
+# depends on AIC8800_WLAN_SUPPORT
+#
+#endchoice
+#
+#config USE_5G
+# bool "AIC8800 force enable 5G"
+# ---help---
+# This is parameter that force enable 5G.
+#
+#config HE_FOR_OLD_KERNEL
+# bool "AIC8800 support AX for old kernel"
+# ---help---
+# This is parameter that enable AX for old kernel.
diff --git a/drivers/net/wireless/aic8800/aic8800_fdrv/Makefile b/drivers/net/wireless/aic8800/aic8800_fdrv/Makefile
new file mode 100644
index 000000000000..28643107cd27
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic8800_fdrv/Makefile
@@ -0,0 +1,373 @@
+#EXTRA_CFLAGS += $(USER_EXTRA_CFLAGS)
+#EXTRA_CFLAGS += -Wno-implicit-fallthrough
+
+RWNX_VERS_NUM := 6.4.3.0
+
+CONFIG_AIC8800_WLAN_SUPPORT = m
+MODULE_NAME = aic8800_fdrv
+CONFIG_COUNTRY_CODE = "00"
+
+# Support of bootrom start
+CONFIG_START_FROM_BOOTROM = y
+
+# Support of pmic setting, new version bootrom avaliable
+CONFIG_PMIC_SETTING ?=n
+
+# Select 8800DC/DW DCDC_VRF mode, check your board
+CONFIG_VRF_DCDC_MODE = y
+
+# ROM patch enabled option
+CONFIG_ROM_PATCH_EN ?=y
+
+#
+# WAITING FOR KCONFIG {
+#
+CONFIG_RWNX_SOFTMAC ?= n
+CONFIG_RWNX_FULLMAC ?= y
+CONFIG_RWNX_FHOST ?= n
+
+#
+# DEBUG OPTIONS
+CONFIG_RWNX_UM_HELPER_DFLT ?= "/dini/dini_bin/rwnx_umh.sh"
+CONFIG_AIC_FW_PATH = "/vendor/etc/firmware"
+export CONFIG_AIC_FW_PATH
+
+#
+# FW ARCH:
+CONFIG_RWNX_SDM ?= n
+CONFIG_RWNX_TL4 ?= n
+
+# IPC version
+CONFIG_RWNX_OLD_IPC ?= n
+
+# Support of P2P DebugFS for enabling/disabling NoA and OppPS
+CONFIG_RWNX_P2P_DEBUGFS ?= y
+
+#
+# } // WAITING FOR KCONFIG
+#
+
+# Enable A-MSDU support (need FW support)
+## Select this if FW is compiled with AMSDU support
+CONFIG_RWNX_SPLIT_TX_BUF ?= n
+## Select this TO send AMSDU
+CONFIG_RWNX_AMSDUS_TX ?= n
+
+# Enable BFMER support (need FW support)
+CONFIG_RWNX_BFMER ?= n
+
+CONFIG_SDIO_SUPPORT =n
+CONFIG_USB_SUPPORT =y
+CONFIG_RX_REORDER ?=y
+CONFIG_ARP_OFFLOAD =y
+CONFIG_USE_5G ?= n
+CONFIG_RADAR_OR_IR_DETECT =n
+CONFIG_DOWNLOAD_FW =n
+CONFIG_RFTEST=y
+CONFIG_USB_BT=y
+CONFIG_MAC_RANDOM_IF_NO_MAC_IN_EFUSE = y
+CONFIG_WPA3_FOR_OLD_KERNEL ?= n
+CONFIG_HE_FOR_OLD_KERNEL ?= n
+CONFIG_VHT_FOR_OLD_KERNEL ?= n
+# CONFIG_COEX = n for BT_ONLY, CONFIG_COEX =y for combo and sw
+CONFIG_COEX = y
+CONFIG_ALIGN_8BYTES = n
+CONFIG_TXRX_THREAD_PRIO = n
+CONFIG_USB_ALIGN_DATA = n
+CONFIG_RX_TASKLET = n
+CONFIG_TX_TASKLET = n
+CONFIG_RX_NETIF_RECV_SKB = y
+CONFIG_BR_SUPPORT = n
+CONFIG_USB_MSG_OUT_EP = y
+CONFIG_USB_MSG_IN_EP = y
+
+#DCDW support tx aggr, D80 support both
+CONFIG_USB_RX_AGGR = n
+CONFIG_USB_TX_AGGR = n
+
+CONFIG_USB_NO_TRANS_DMA_MAP = n
+CONFIG_GPIO_WAKEUP = n
+CONFIG_CREATE_TRACE_POINTS = n
+CONFIG_SUPPORT_REALTIME_CHANGE_MAC = y
+CONFIG_USE_USB_ZERO_PACKET = y
+CONFIG_DEBUG_FS = n
+CONFIG_STA_SCAN_WHEN_P2P_WORKING = y
+CONFIG_SET_VENDOR_EXTENSION_IE = n
+CONFIG_VENDOR_GPIO = n
+CONFIG_FWLOG_EN = n
+CONFIG_FOR_IPCAM = n
+CONFIG_5M10M = n
+# Need to set fw path in BOARD_KERNEL_CMDLINE
+CONFIG_USE_FW_REQUEST = n
+CONFIG_USE_P2P0 = n
+CONFIG_ONE_TXQ = n
+CONFIG_PER_STA_FC = n
+CONFIG_PREALLOC_RX_SKB = n
+CONFIG_PREALLOC_TXQ = n
+CONFIG_USE_WIRELESS_EXT = n
+CONFIG_DPD = n
+CONFIG_GKI = n
+CONFIG_SCHED_SCAN = n
+
+# Support of MU-MIMO transmission (need FW support)
+ifeq ($(CONFIG_RWNX_BFMER), y)
+CONFIG_RWNX_MUMIMO_TX ?= n
+else
+CONFIG_RWNX_MUMIMO_TX = n
+endif
+
+# Enable handling of radar event
+CONFIG_RWNX_RADAR ?= y
+
+# Enable HW queue for Broadcast/Multicast traffic (need FW support)
+CONFIG_RWNX_BCMC ?= y
+
+# Enable Monitor+Data interface support (need FW support)
+CONFIG_RWNX_MON_DATA =n
+CONFIG_RWNX_MON_XMIT ?= n
+CONFIG_RWNX_MON_RXFILTER ?= n
+
+# extra DEBUG config
+CONFIG_RWNX_SW_PROFILING ?= n
+CONFIG_RWNX_DBG ?= y
+BR_NAME = br0
+
+obj-$(CONFIG_AIC8800_WLAN_SUPPORT) := $(MODULE_NAME).o
+$(MODULE_NAME)-y := \
+ rwnx_msg_tx.o \
+ rwnx_msg_rx.o \
+ rwnx_utils.o \
+ rwnx_cmds.o \
+ rwnx_irqs.o \
+ rwnx_cfgfile.o \
+ rwnx_strs.o \
+ rwnx_rx.o \
+ rwnx_tx.o \
+ rwnx_txq.o \
+ rwnx_main.o \
+ rwnx_mod_params.o \
+ rwnx_mesh.o \
+ rwnx_platform.o \
+ rwnx_pci.o \
+ rwnx_dini.o \
+ rwnx_v7.o \
+ ipc_host.o \
+ rwnx_tdls.o \
+ regdb.o \
+ md5.o \
+ aic_vendor.o \
+ aicwf_compat_8800dc.o \
+ aicwf_compat_8800d80.o \
+
+$(MODULE_NAME)-$(CONFIG_BR_SUPPORT) += aic_br_ext.o
+$(MODULE_NAME)-$(CONFIG_RWNX_RADAR) += rwnx_radar.o
+$(MODULE_NAME)-$(CONFIG_DEBUG_FS) += rwnx_debugfs.o
+$(MODULE_NAME)-$(CONFIG_DEBUG_FS) += rwnx_fw_trace.o
+$(MODULE_NAME)-$(CONFIG_NL80211_TESTMODE) += rwnx_testmode.o
+$(MODULE_NAME)-$(CONFIG_RWNX_BFMER) += rwnx_bfmer.o
+$(MODULE_NAME)-$(CONFIG_RWNX_MUMIMO_TX) += rwnx_mu_group.o
+$(MODULE_NAME)-$(CONFIG_SDIO_SUPPORT) += sdio_host.o
+$(MODULE_NAME)-$(CONFIG_SDIO_SUPPORT) += aicwf_txrxif.o
+$(MODULE_NAME)-$(CONFIG_SDIO_SUPPORT) += aicwf_sdio.o
+
+$(MODULE_NAME)-$(CONFIG_USB_SUPPORT) += usb_host.o
+$(MODULE_NAME)-$(CONFIG_USB_SUPPORT) += aicwf_txrxif.o
+$(MODULE_NAME)-$(CONFIG_USB_SUPPORT) += aicwf_usb.o
+$(MODULE_NAME)-$(CONFIG_USE_WIRELESS_EXT) += aicwf_wext_linux.o
+$(MODULE_NAME)-$(CONFIG_GKI) += rwnx_gki.o
+
+ccflags-$(CONFIG_DEBUG_FS) += -DCONFIG_RWNX_DEBUGFS
+ccflags-$(CONFIG_DEBUG_FS) += -DCONFIG_RWNX_UM_HELPER_DFLT=\"$(CONFIG_RWNX_UM_HELPER_DFLT)\"
+ccflags-$(CONFIG_RWNX_P2P_DEBUGFS) += -DCONFIG_RWNX_P2P_DEBUGFS
+
+# FW VARS
+ccflags-y += -DNX_VIRT_DEV_MAX=4
+
+#for 8800D and DCDW u01
+#ccflags-y += -DNX_REMOTE_STA_MAX=8
+
+#for 8800DCDW u02
+ccflags-y += -DNX_REMOTE_STA_MAX_FOR_OLD_IC=8
+ccflags-y += -DNX_REMOTE_STA_MAX=32
+
+ccflags-y += -DNX_MU_GROUP_MAX=62
+ccflags-y += -DNX_TXDESC_CNT=64
+ccflags-y += -DNX_TX_MAX_RATES=4
+ccflags-y += -DNX_CHAN_CTXT_CNT=3
+
+# FW ARCH:
+ccflags-$(CONFIG_RWNX_SDM) += -DCONFIG_RWNX_SDM
+ccflags-$(CONFIG_RWNX_TL4) += -DCONFIG_RWNX_TL4
+ccflags-$(CONFIG_RWNX_OLD_IPC) += -DCONFIG_RWNX_OLD_IPC
+ccflags-$(CONFIG_START_FROM_BOOTROM) += -DCONFIG_START_FROM_BOOTROM
+ccflags-$(CONFIG_PMIC_SETTING) += -DCONFIG_PMIC_SETTING
+ccflags-$(CONFIG_VRF_DCDC_MODE) += -DCONFIG_VRF_DCDC_MODE
+ccflags-$(CONFIG_ROM_PATCH_EN) += -DCONFIG_ROM_PATCH_EN
+ccflags-$(CONFIG_WPA3_FOR_OLD_KERNEL) += -DCONFIG_WPA3_FOR_OLD_KERNEL
+ccflags-$(CONFIG_HE_FOR_OLD_KERNEL) += -DCONFIG_HE_FOR_OLD_KERNEL
+ccflags-$(CONFIG_VHT_FOR_OLD_KERNEL) += -DCONFIG_VHT_FOR_OLD_KERNEL
+ccflags-$(CONFIG_COEX) += -DCONFIG_COEX
+
+ccflags-y += -DCONFIG_RWNX_FULLMAC
+ccflags-y += -I$(src)/.
+ccflags-$(CONFIG_RWNX_RADAR) += -DCONFIG_RWNX_RADAR
+ccflags-$(CONFIG_RWNX_MON_DATA) += -DCONFIG_RWNX_MON_DATA
+ccflags-$(CONFIG_RWNX_MON_XMIT) += -DCONFIG_RWNX_MON_XMIT
+ccflags-$(CONFIG_RWNX_MON_RXFILTER) += -DCONFIG_RWNX_MON_RXFILTER
+ccflags-$(CONFIG_RWNX_BFMER) += -DCONFIG_RWNX_BFMER
+ccflags-$(CONFIG_RWNX_SPLIT_TX_BUF) += -DCONFIG_RWNX_SPLIT_TX_BUF
+ifeq ($(CONFIG_RWNX_SPLIT_TX_BUF), y)
+ccflags-$(CONFIG_RWNX_AMSDUS_TX) += -DCONFIG_RWNX_AMSDUS_TX
+endif
+ccflags-$(CONFIG_RWNX_DBG) += -DCONFIG_RWNX_DBG
+ccflags-$(CONFIG_RWNX_SW_PROFILING) += -DCONFIG_RWNX_SW_PROFILING
+ccflags-$(CONFIG_RWNX_MUMIMO_TX) += -DCONFIG_RWNX_MUMIMO_TX
+ccflags-$(CONFIG_RFTEST) += -DCONFIG_RFTEST
+
+
+ifeq ($(CONFIG_SDIO_SUPPORT), y)
+ccflags-y += -DAICWF_SDIO_SUPPORT
+endif
+
+ifeq ($(CONFIG_USB_SUPPORT), y)
+ccflags-y += -DAICWF_USB_SUPPORT
+endif
+ifeq ($(CONFIG_BR_SUPPORT), y)
+ccflags-y += -DCONFIG_BR_SUPPORT
+ccflags-y += '-DCONFIG_BR_SUPPORT_BRNAME="'$(BR_NAME)'"'
+endif
+
+ifeq ($(CONFIG_RWNX_MUMIMO_TX), y)
+ccflags-y += -DCONFIG_USER_MAX=2
+else
+ccflags-y += -DCONFIG_USER_MAX=1
+endif
+
+ifeq ($(CONFIG_RWNX_BCMC), y)
+ccflags-y += -DNX_TXQ_CNT=5
+else
+ccflags-y += -DNX_TXQ_CNT=4
+endif
+
+# For old kernel (<=3.19)
+ifeq ($(shell test $(VERSION) -lt 4 -a "$(CONFIG_VENDOR_RWNX)" = y ; echo $$?),0)
+ccflags-y += -DCONFIG_VENDOR_RWNX_VHT_NO80
+endif
+
+ccflags-$(CONFIG_RX_REORDER) += -DAICWF_RX_REORDER
+ccflags-$(CONFIG_ARP_OFFLOAD) += -DAICWF_ARP_OFFLOAD
+ccflags-$(CONFIG_USE_5G) += -DUSE_5G
+ccflags-$(CONFIG_RADAR_OR_IR_DETECT) += -DRADAR_OR_IR_DETECT
+ccflags-$(CONFIG_DOWNLOAD_FW) += -DCONFIG_DOWNLOAD_FW
+ccflags-$(CONFIG_USB_BT) += -DCONFIG_USB_BT
+ccflags-$(CONFIG_ALIGN_8BYTES) += -DCONFIG_ALIGN_8BYTES
+ccflags-$(CONFIG_TXRX_THREAD_PRIO) += -DCONFIG_TXRX_THREAD_PRIO
+ccflags-$(CONFIG_USB_ALIGN_DATA) += -DCONFIG_USB_ALIGN_DATA
+ccflags-$(CONFIG_MAC_RANDOM_IF_NO_MAC_IN_EFUSE) += -DCONFIG_MAC_RANDOM_IF_NO_MAC_IN_EFUSE
+ccflags-$(CONFIG_VENDOR_GPIO) += -DCONFIG_VENDOR_GPIO
+ccflags-y += -DDEFAULT_COUNTRY_CODE=""\$(CONFIG_COUNTRY_CODE)"\"
+ccflags-$(CONFIG_RX_NETIF_RECV_SKB) += -DCONFIG_RX_NETIF_RECV_SKB
+ccflags-$(CONFIG_USB_MSG_OUT_EP) += -DCONFIG_USB_MSG_OUT_EP
+ccflags-$(CONFIG_USB_MSG_IN_EP) += -DCONFIG_USB_MSG_IN_EP
+ccflags-$(CONFIG_USB_RX_AGGR) += -DCONFIG_USB_RX_AGGR
+ccflags-$(CONFIG_USB_TX_AGGR) += -DCONFIG_USB_TX_AGGR
+ccflags-$(CONFIG_USB_NO_TRANS_DMA_MAP) += -DCONFIG_USB_NO_TRANS_DMA_MAP
+ccflags-$(CONFIG_GPIO_WAKEUP) += -DCONFIG_GPIO_WAKEUP
+ccflags-$(CONFIG_CREATE_TRACE_POINTS) += -DCREATE_TRACE_POINTS
+ccflags-$(CONFIG_RX_TASKLET) += -DCONFIG_RX_TASKLET
+ccflags-$(CONFIG_TX_TASKLET) += -DCONFIG_TX_TASKLET
+ccflags-$(CONFIG_USE_USB_ZERO_PACKET) += -DCONFIG_USE_USB_ZERO_PACKET
+ccflags-$(CONFIG_STA_SCAN_WHEN_P2P_WORKING) += -DCONFIG_STA_SCAN_WHEN_P2P_WORKING
+ccflags-$(CONFIG_SUPPORT_REALTIME_CHANGE_MAC) += -DCONFIG_SUPPORT_REALTIME_CHANGE_MAC
+ccflags-$(CONFIG_SET_VENDOR_EXTENSION_IE) += -DCONFIG_SET_VENDOR_EXTENSION_IE
+ccflags-$(CONFIG_FWLOG_EN) += -DCONFIG_FWLOG_EN
+ccflags-$(CONFIG_FOR_IPCAM) += -DCONFIG_FOR_IPCAM
+ccflags-$(CONFIG_5M10M) += -DCONFIG_5M10M
+ccflags-$(CONFIG_USE_FW_REQUEST) += -DCONFIG_USE_FW_REQUEST
+ccflags-$(CONFIG_USE_P2P0) += -DCONFIG_USE_P2P0
+ccflags-$(CONFIG_ONE_TXQ) += -DCONFIG_ONE_TXQ
+ccflags-$(CONFIG_PER_STA_FC) += -DCONFIG_PER_STA_FC
+ccflags-$(CONFIG_PREALLOC_RX_SKB) += -DCONFIG_PREALLOC_RX_SKB
+ccflags-$(CONFIG_PREALLOC_TXQ) += -DCONFIG_PREALLOC_TXQ
+ccflags-$(CONFIG_USE_WIRELESS_EXT) += -DCONFIG_USE_WIRELESS_EXT
+ccflags-$(CONFIG_DPD) += -DCONFIG_DPD
+ccflags-$(CONFIG_GKI) += -DCONFIG_GKI
+ccflags-$(CONFIG_SCHED_SCAN) += -DCONFIG_SCHED_SCAN
+
+# Platform support list
+CONFIG_PLATFORM_ROCKCHIP ?= y
+CONFIG_PLATFORM_ALLWINNER ?= n
+CONFIG_PLATFORM_AMLOGIC ?= n
+CONFIG_PLATFORM_UBUNTU ?= n
+
+ifeq ($(CONFIG_PLATFORM_ROCKCHIP), y)
+#KDIR ?= /home/yaya/E/Rockchip/3288/Android5/kernel/
+#ARCH ?= arm
+#CROSS_COMPILE ?= /home/yaya/D/Workspace/CyberQuantum/JinHaoYue/rk3288/prebuilts/gcc/linux-x86/arm/arm-eabi-4.6/bin/arm-eabi-
+#KDIR ?= /home/yaya/E/Rockchip/3229/Android7/RK3229_ANDROID7.1_v1.01_20170914/rk3229_Android7.1_v1.01_xml0914/kernel
+#ARCH ?= arm
+#CROSS_COMPILE ?= /home/yaya/E/Rockchip/3229/Android7/RK3229_ANDROID7.1_v1.01_20170914/rk3229_Android7.1_v1.01_xml0914/prebuilts/gcc/linux-x86/arm/arm-eabi-4.6/bin/arm-eabi-
+#KDIR ?= /home/yaya/E/Rockchip/3229/Android9/rk3229_android9.0_box/kernel
+#ARCH ?= arm
+#CROSS_COMPILE ?= /home/yaya/E/Rockchip/3229/Android9/rk3229_android9.0_box/prebuilts/gcc/linux-x86/arm/gcc-linaro-6.3.1-2017.05-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf-
+KDIR ?= /home/yaya/E/Rockchip/3566/firefly/Android11.0/Firefly-RK356X_Android11.0_git_20210824/RK356X_Android11.0/kernel
+ARCH ?= arm64
+CROSS_COMPILE ?= /home/yaya/E/Rockchip/3566/Android11/rk3566_rk3568_android11_oranth/prebuilts/gcc/linux-x86/aarch64/gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-
+#ARCH ?= arm
+#CROSS_COMPILE ?=/home/yaya/D/Workspace/CyberQuantum/JinHaoYue/rk3288/prebuilts/gcc/linux-x86/arm/arm-eabi-4.6/bin/arm-eabi-
+#KDIR ?=/home/yaya/D/Workspace/CyberQuantum/JinHaoYue/rk3288/kernel
+#KDIR := /home/yaya/E/Rockchip/3566/Android/kernel
+#ARCH ?= arm64
+#CROSS_COMPILE ?= /home/yaya/E/Rockchip/3566/Android/prebuilts/gcc/linux-x86/aarch64/gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-
+#KDIR ?= /home/yaya/E/Rockchip/3566/Android11/rk3566_rk3568_android11_oranth/kernel
+#ARCH ?= arm64
+#CROSS_COMPILE ?= /home/yaya/E/Rockchip/3566/Android11/rk3566_rk3568_android11_oranth/prebuilts/gcc/linux-x86/aarch64/gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-
+#KDIR ?= /home/yaya/D/samba1/aiden/SDK/Rockchip/3588/Android12/RK3588_Android12.0/kernel-5.10
+#ARCH ?= arm64
+#CROSS_COMPILE ?= /home/yaya/D/samba1/aiden/SDK/Rockchip/3588/Android12/RK3588_Android12.0/prebuilts/gcc/linux-x86/aarch64/gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-
+ccflags-y += -DANDROID_PLATFORM
+ccflags-y += -DCONFIG_PLATFORM_ROCKCHIP
+endif
+
+ifeq ($(CONFIG_PLATFORM_ALLWINNER), y)
+KDIR ?= /home/yaya/E/Allwinner/R818/R818/AndroidQ/lichee/kernel/linux-4.9
+ARCH ?= arm64
+CROSS_COMPILE ?= /home/yaya/E/Allwinner/R818/R818/AndroidQ/lichee/out/gcc-linaro-5.3.1-2016.05-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-
+ccflags-y += -DANDROID_PLATFORM
+endif
+
+ifeq ($(CONFIG_PLATFORM_AMLOGIC), y)
+ccflags-y += -DANDROID_PLATFORM
+ARCH ?= arm
+CROSS_COMPILE ?= /home/yaya/D/Workspace/CyberQuantum/JinHaoYue/amls905x3/SDK/20191101-0tt-asop/android9.0/prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.9/bin/arm-linux-androidkernel-
+KDIR ?= /home/yaya/D/Workspace/CyberQuantum/JinHaoYue/amls905x3/SDK/20191101-0tt-asop/android9.0/out/target/product/u202/obj/KERNEL_OBJ/
+
+endif
+
+ifeq ($(CONFIG_PLATFORM_UBUNTU), y)
+KDIR ?= /lib/modules/$(shell uname -r)/build
+#KDIR ?= ~/D/Workspace/CyberQuantum/Linux/linux-4.15/
+PWD ?= $(shell pwd)
+KVER ?= $(shell uname -r)
+MODDESTDIR ?= /lib/modules/$(KVER)/kernel/drivers/net/wireless/aic8800
+ARCH ?= x86_64
+CROSS_COMPILE ?=
+endif
+
+
+all: modules
+modules:
+# make -C $(KDIR) M=$(PWD) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) LLVM=1 LLVM_IAS=1 modules
+ make -C $(KDIR) M=$(PWD) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) modules
+install:
+ mkdir -p $(MODDESTDIR)
+ install -p -m 644 $(MODULE_NAME).ko $(MODDESTDIR)
+ /sbin/depmod -a ${KVER}
+
+uninstall:
+ rm -rfv $(MODDESTDIR)/$(MODULE_NAME).ko
+ /sbin/depmod -a ${KVER}
+
+clean:
+ rm -rf *.o *.ko *.o.* *.mod.* modules.* Module.* .a* .o* .*.o.* *.mod .tmp* .cache.mk .modules.order.cmd .Module.symvers.cmd
+
diff --git a/drivers/net/wireless/aic8800/aic8800_fdrv/aic_br_ext.c b/drivers/net/wireless/aic8800/aic8800_fdrv/aic_br_ext.c
new file mode 100644
index 000000000000..dd038e39f3fe
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic8800_fdrv/aic_br_ext.c
@@ -0,0 +1,1569 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2017 Realtek Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ *****************************************************************************/
+#define _AIC_BR_EXT_C_
+
+#ifdef __KERNEL__
+ #include <linux/if_arp.h>
+ #include <net/ip.h>
+ #include <net/ipx.h>
+ #include <linux/atalk.h>
+ #include <linux/udp.h>
+ #include <linux/if_pppox.h>
+ #include "rwnx_defs.h"
+#endif
+
+#ifdef CL_IPV6_PASS
+ #ifdef __KERNEL__
+ #include <linux/ipv6.h>
+ #include <linux/icmpv6.h>
+ #include <net/ndisc.h>
+ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24))
+ #include <net/ip6_checksum.h>
+ #else
+ #include <net/checksum.h>
+ #endif
+ #endif
+#endif
+
+#ifdef CONFIG_BR_SUPPORT
+
+/* #define BR_SUPPORT_DEBUG */
+
+#define NAT25_IPV4 01
+#define NAT25_IPV6 02
+#define NAT25_IPX 03
+#define NAT25_APPLE 04
+#define NAT25_PPPOE 05
+
+#define RTL_RELAY_TAG_LEN (ETH_ALEN)
+#define TAG_HDR_LEN 4
+
+#define MAGIC_CODE 0x8186
+#define MAGIC_CODE_LEN 2
+#define WAIT_TIME_PPPOE 5 /* waiting time for pppoe server in sec */
+
+/*-----------------------------------------------------------------
+ How database records network address:
+ 0 1 2 3 4 5 6 7 8 9 10
+ |----|----|----|----|----|----|----|----|----|----|----|
+ IPv4 |type| | IP addr |
+ IPX |type| Net addr | Node addr |
+ IPX |type| Net addr |Sckt addr|
+ Apple |type| Network |node|
+ PPPoE |type| SID | AC MAC |
+-----------------------------------------------------------------*/
+
+
+/* Find a tag in pppoe frame and return the pointer */
+static __inline__ unsigned char *__nat25_find_pppoe_tag(struct pppoe_hdr *ph, unsigned short type)
+{
+ unsigned char *cur_ptr, *start_ptr;
+ unsigned short tagLen, tagType;
+
+ start_ptr = cur_ptr = (unsigned char *)ph->tag;
+ while ((cur_ptr - start_ptr) < ntohs(ph->length)) {
+ /* prevent un-alignment access */
+ tagType = (unsigned short)((cur_ptr[0] << 8) + cur_ptr[1]);
+ tagLen = (unsigned short)((cur_ptr[2] << 8) + cur_ptr[3]);
+ if (tagType == type)
+ return cur_ptr;
+ cur_ptr = cur_ptr + TAG_HDR_LEN + tagLen;
+ }
+ return 0;
+}
+
+
+static __inline__ int __nat25_add_pppoe_tag(struct sk_buff *skb, struct pppoe_tag *tag)
+{
+ struct pppoe_hdr *ph = (struct pppoe_hdr *)(skb->data + ETH_HLEN);
+ int data_len;
+
+ data_len = tag->tag_len + TAG_HDR_LEN;
+ if (skb_tailroom(skb) < data_len) {
+ printk("skb_tailroom() failed in add SID tag!\n");
+ return -1;
+ }
+
+ skb_put(skb, data_len);
+ /* have a room for new tag */
+ memmove(((unsigned char *)ph->tag + data_len), (unsigned char *)ph->tag, ntohs(ph->length));
+ ph->length = htons(ntohs(ph->length) + data_len);
+ memcpy((unsigned char *)ph->tag, tag, data_len);
+ return data_len;
+}
+
+static int skb_pull_and_merge(struct sk_buff *skb, unsigned char *src, int len)
+{
+ int tail_len;
+ unsigned long end, tail;
+
+ if ((src + len) > skb_tail_pointer(skb) || skb->len < len)
+ return -1;
+
+ tail = (unsigned long)skb_tail_pointer(skb);
+ end = (unsigned long)src + len;
+ if (tail < end)
+ return -1;
+
+ tail_len = (int)(tail - end);
+ if (tail_len > 0)
+ memmove(src, src + len, tail_len);
+
+ skb_trim(skb, skb->len - len);
+ return 0;
+}
+
+static __inline__ unsigned long __nat25_timeout(struct rwnx_vif *vif)
+{
+ unsigned long timeout;
+
+ timeout = jiffies - NAT25_AGEING_TIME * HZ;
+
+ return timeout;
+}
+
+
+static __inline__ int __nat25_has_expired(struct rwnx_vif *vif,
+ struct nat25_network_db_entry *fdb)
+{
+ if (time_before_eq(fdb->ageing_timer, __nat25_timeout(vif)))
+ return 1;
+
+ return 0;
+}
+
+
+static __inline__ void __nat25_generate_ipv4_network_addr(unsigned char *networkAddr,
+ unsigned int *ipAddr)
+{
+ memset(networkAddr, 0, MAX_NETWORK_ADDR_LEN);
+
+ networkAddr[0] = NAT25_IPV4;
+ memcpy(networkAddr + 7, (unsigned char *)ipAddr, 4);
+}
+
+
+static __inline__ void __nat25_generate_ipx_network_addr_with_node(unsigned char *networkAddr,
+ unsigned int *ipxNetAddr, unsigned char *ipxNodeAddr)
+{
+ memset(networkAddr, 0, MAX_NETWORK_ADDR_LEN);
+
+ networkAddr[0] = NAT25_IPX;
+ memcpy(networkAddr + 1, (unsigned char *)ipxNetAddr, 4);
+ memcpy(networkAddr + 5, ipxNodeAddr, 6);
+}
+
+
+static __inline__ void __nat25_generate_ipx_network_addr_with_socket(unsigned char *networkAddr,
+ unsigned int *ipxNetAddr, unsigned short *ipxSocketAddr)
+{
+ memset(networkAddr, 0, MAX_NETWORK_ADDR_LEN);
+
+ networkAddr[0] = NAT25_IPX;
+ memcpy(networkAddr + 1, (unsigned char *)ipxNetAddr, 4);
+ memcpy(networkAddr + 5, (unsigned char *)ipxSocketAddr, 2);
+}
+
+
+static __inline__ void __nat25_generate_apple_network_addr(unsigned char *networkAddr,
+ unsigned short *network, unsigned char *node)
+{
+ memset(networkAddr, 0, MAX_NETWORK_ADDR_LEN);
+
+ networkAddr[0] = NAT25_APPLE;
+ memcpy(networkAddr + 1, (unsigned char *)network, 2);
+ networkAddr[3] = *node;
+}
+
+
+static __inline__ void __nat25_generate_pppoe_network_addr(unsigned char *networkAddr,
+ unsigned char *ac_mac, unsigned short *sid)
+{
+ memset(networkAddr, 0, MAX_NETWORK_ADDR_LEN);
+
+ networkAddr[0] = NAT25_PPPOE;
+ memcpy(networkAddr + 1, (unsigned char *)sid, 2);
+ memcpy(networkAddr + 3, (unsigned char *)ac_mac, 6);
+}
+
+
+#ifdef CL_IPV6_PASS
+static void __nat25_generate_ipv6_network_addr(unsigned char *networkAddr,
+ unsigned int *ipAddr)
+{
+ memset(networkAddr, 0, MAX_NETWORK_ADDR_LEN);
+
+ networkAddr[0] = NAT25_IPV6;
+ memcpy(networkAddr + 1, (unsigned char *)ipAddr, 16);
+}
+
+
+static unsigned char *scan_tlv(unsigned char *data, int len, unsigned char tag, unsigned char len8b)
+{
+ while (len > 0) {
+ if (*data == tag && *(data + 1) == len8b && len >= len8b * 8)
+ return data + 2;
+
+ len -= (*(data + 1)) * 8;
+ data += (*(data + 1)) * 8;
+ }
+ return NULL;
+}
+
+
+static int update_nd_link_layer_addr(unsigned char *data, int len, unsigned char *replace_mac)
+{
+ struct icmp6hdr *icmphdr = (struct icmp6hdr *)data;
+ unsigned char *mac;
+
+ if (icmphdr->icmp6_type == NDISC_ROUTER_SOLICITATION) {
+ if (len >= 8) {
+ mac = scan_tlv(&data[8], len - 8, 1, 1);
+ if (mac) {
+ printk("Router Solicitation, replace MAC From: %02x:%02x:%02x:%02x:%02x:%02x, To: %02x:%02x:%02x:%02x:%02x:%02x\n",
+ mac[0], mac[1], mac[2], mac[3], mac[4], mac[5],
+ replace_mac[0], replace_mac[1], replace_mac[2], replace_mac[3], replace_mac[4], replace_mac[5]);
+ memcpy(mac, replace_mac, 6);
+ return 1;
+ }
+ }
+ } else if (icmphdr->icmp6_type == NDISC_ROUTER_ADVERTISEMENT) {
+ if (len >= 16) {
+ mac = scan_tlv(&data[16], len - 16, 1, 1);
+ if (mac) {
+ printk("Router Advertisement, replace MAC From: %02x:%02x:%02x:%02x:%02x:%02x, To: %02x:%02x:%02x:%02x:%02x:%02x\n",
+ mac[0], mac[1], mac[2], mac[3], mac[4], mac[5],
+ replace_mac[0], replace_mac[1], replace_mac[2], replace_mac[3], replace_mac[4], replace_mac[5]);
+ memcpy(mac, replace_mac, 6);
+ return 1;
+ }
+ }
+ } else if (icmphdr->icmp6_type == NDISC_NEIGHBOUR_SOLICITATION) {
+ if (len >= 24) {
+ mac = scan_tlv(&data[24], len - 24, 1, 1);
+ if (mac) {
+ printk("Neighbor Solicitation, replace MAC From: %02x:%02x:%02x:%02x:%02x:%02x, To: %02x:%02x:%02x:%02x:%02x:%02x\n",
+ mac[0], mac[1], mac[2], mac[3], mac[4], mac[5],
+ replace_mac[0], replace_mac[1], replace_mac[2], replace_mac[3], replace_mac[4], replace_mac[5]);
+ memcpy(mac, replace_mac, 6);
+ return 1;
+ }
+ }
+ } else if (icmphdr->icmp6_type == NDISC_NEIGHBOUR_ADVERTISEMENT) {
+ if (len >= 24) {
+ mac = scan_tlv(&data[24], len - 24, 2, 1);
+ if (mac) {
+ printk("Neighbor Advertisement, replace MAC From: %02x:%02x:%02x:%02x:%02x:%02x, To: %02x:%02x:%02x:%02x:%02x:%02x\n",
+ mac[0], mac[1], mac[2], mac[3], mac[4], mac[5],
+ replace_mac[0], replace_mac[1], replace_mac[2], replace_mac[3], replace_mac[4], replace_mac[5]);
+ memcpy(mac, replace_mac, 6);
+ return 1;
+ }
+ }
+ } else if (icmphdr->icmp6_type == NDISC_REDIRECT) {
+ if (len >= 40) {
+ mac = scan_tlv(&data[40], len - 40, 2, 1);
+ if (mac) {
+ printk("Redirect, replace MAC From: %02x:%02x:%02x:%02x:%02x:%02x, To: %02x:%02x:%02x:%02x:%02x:%02x\n",
+ mac[0], mac[1], mac[2], mac[3], mac[4], mac[5],
+ replace_mac[0], replace_mac[1], replace_mac[2], replace_mac[3], replace_mac[4], replace_mac[5]);
+ memcpy(mac, replace_mac, 6);
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+#ifdef SUPPORT_RX_UNI2MCAST
+static void convert_ipv6_mac_to_mc(struct sk_buff *skb)
+{
+ struct ipv6hdr *iph = (struct ipv6hdr *)(skb->data + ETH_HLEN);
+ unsigned char *dst_mac = skb->data;
+
+ /* dst_mac[0] = 0xff; */
+ /* dst_mac[1] = 0xff; */
+ /*modified by qinjunjie,ipv6 multicast address ix 0x33-33-xx-xx-xx-xx*/
+ dst_mac[0] = 0x33;
+ dst_mac[1] = 0x33;
+ memcpy(&dst_mac[2], &iph->daddr.s6_addr32[3], 4);
+#if defined(__LINUX_2_6__)
+ /*modified by qinjunjie,warning:should not remove next line*/
+ skb->pkt_type = PACKET_MULTICAST;
+#endif
+}
+#endif /* CL_IPV6_PASS */
+#endif /* SUPPORT_RX_UNI2MCAST */
+
+
+static __inline__ int __nat25_network_hash(unsigned char *networkAddr)
+{
+ if (networkAddr[0] == NAT25_IPV4) {
+ unsigned long x;
+
+ x = networkAddr[7] ^ networkAddr[8] ^ networkAddr[9] ^ networkAddr[10];
+
+ return x & (NAT25_HASH_SIZE - 1);
+ } else if (networkAddr[0] == NAT25_IPX) {
+ unsigned long x;
+
+ x = networkAddr[1] ^ networkAddr[2] ^ networkAddr[3] ^ networkAddr[4] ^ networkAddr[5] ^
+ networkAddr[6] ^ networkAddr[7] ^ networkAddr[8] ^ networkAddr[9] ^ networkAddr[10];
+
+ return x & (NAT25_HASH_SIZE - 1);
+ } else if (networkAddr[0] == NAT25_APPLE) {
+ unsigned long x;
+
+ x = networkAddr[1] ^ networkAddr[2] ^ networkAddr[3];
+
+ return x & (NAT25_HASH_SIZE - 1);
+ } else if (networkAddr[0] == NAT25_PPPOE) {
+ unsigned long x;
+
+ x = networkAddr[0] ^ networkAddr[1] ^ networkAddr[2] ^ networkAddr[3] ^ networkAddr[4] ^ networkAddr[5] ^ networkAddr[6] ^ networkAddr[7] ^ networkAddr[8];
+
+ return x & (NAT25_HASH_SIZE - 1);
+ }
+#ifdef CL_IPV6_PASS
+ else if (networkAddr[0] == NAT25_IPV6) {
+ unsigned long x;
+
+ x = networkAddr[1] ^ networkAddr[2] ^ networkAddr[3] ^ networkAddr[4] ^ networkAddr[5] ^
+ networkAddr[6] ^ networkAddr[7] ^ networkAddr[8] ^ networkAddr[9] ^ networkAddr[10] ^
+ networkAddr[11] ^ networkAddr[12] ^ networkAddr[13] ^ networkAddr[14] ^ networkAddr[15] ^
+ networkAddr[16];
+
+ return x & (NAT25_HASH_SIZE - 1);
+ }
+#endif
+ else {
+ unsigned long x = 0;
+ int i;
+
+ for (i = 0; i < MAX_NETWORK_ADDR_LEN; i++)
+ x ^= networkAddr[i];
+
+ return x & (NAT25_HASH_SIZE - 1);
+ }
+}
+
+
+static __inline__ void __network_hash_link(struct rwnx_vif *vif,
+ struct nat25_network_db_entry *ent, int hash)
+{
+ /* Caller must _enter_critical_bh already! */
+ /* _irqL irqL; */
+ /* _enter_critical_bh(&priv->br_ext_lock, &irqL); */
+
+ ent->next_hash = vif->nethash[hash];
+ if (ent->next_hash != NULL)
+ ent->next_hash->pprev_hash = &ent->next_hash;
+ vif->nethash[hash] = ent;
+ ent->pprev_hash = &vif->nethash[hash];
+
+ /* _exit_critical_bh(&priv->br_ext_lock, &irqL); */
+}
+
+
+static __inline__ void __network_hash_unlink(struct nat25_network_db_entry *ent)
+{
+ /* Caller must _enter_critical_bh already! */
+ /* _irqL irqL; */
+ /* _enter_critical_bh(&priv->br_ext_lock, &irqL); */
+
+ *(ent->pprev_hash) = ent->next_hash;
+ if (ent->next_hash != NULL)
+ ent->next_hash->pprev_hash = ent->pprev_hash;
+ ent->next_hash = NULL;
+ ent->pprev_hash = NULL;
+
+ /* _exit_critical_bh(&priv->br_ext_lock, &irqL); */
+}
+
+
+static int __nat25_db_network_lookup_and_replace(struct rwnx_vif *vif,
+ struct sk_buff *skb, unsigned char *networkAddr)
+{
+ struct nat25_network_db_entry *db;
+ spin_lock_bh(&vif->br_ext_lock);
+
+ db = vif->nethash[__nat25_network_hash(networkAddr)];
+ while (db != NULL) {
+ if (!memcmp(db->networkAddr, networkAddr, MAX_NETWORK_ADDR_LEN)) {
+ if (!__nat25_has_expired(vif, db)) {
+ /* replace the destination mac address */
+ memcpy(skb->data, db->macAddr, ETH_ALEN);
+ atomic_inc(&db->use_count);
+
+#ifdef CL_IPV6_PASS
+ printk("NAT25: Lookup M:%02x%02x%02x%02x%02x%02x N:%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x"
+ "%02x%02x%02x%02x%02x%02x\n",
+ db->macAddr[0],
+ db->macAddr[1],
+ db->macAddr[2],
+ db->macAddr[3],
+ db->macAddr[4],
+ db->macAddr[5],
+ db->networkAddr[0],
+ db->networkAddr[1],
+ db->networkAddr[2],
+ db->networkAddr[3],
+ db->networkAddr[4],
+ db->networkAddr[5],
+ db->networkAddr[6],
+ db->networkAddr[7],
+ db->networkAddr[8],
+ db->networkAddr[9],
+ db->networkAddr[10],
+ db->networkAddr[11],
+ db->networkAddr[12],
+ db->networkAddr[13],
+ db->networkAddr[14],
+ db->networkAddr[15],
+ db->networkAddr[16]);
+#else
+ printk("NAT25: Lookup M:%02x%02x%02x%02x%02x%02x N:%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n",
+ db->macAddr[0],
+ db->macAddr[1],
+ db->macAddr[2],
+ db->macAddr[3],
+ db->macAddr[4],
+ db->macAddr[5],
+ db->networkAddr[0],
+ db->networkAddr[1],
+ db->networkAddr[2],
+ db->networkAddr[3],
+ db->networkAddr[4],
+ db->networkAddr[5],
+ db->networkAddr[6],
+ db->networkAddr[7],
+ db->networkAddr[8],
+ db->networkAddr[9],
+ db->networkAddr[10]);
+#endif
+ }
+ spin_unlock_bh(&vif->br_ext_lock);
+ return 1;
+ }
+
+ db = db->next_hash;
+ }
+
+ spin_unlock_bh(&vif->br_ext_lock);
+ return 0;
+}
+
+
+static void __nat25_db_network_insert(struct rwnx_vif *vif,
+ unsigned char *macAddr, unsigned char *networkAddr)
+{
+ struct nat25_network_db_entry *db;
+ int hash;
+ spin_lock_bh(&vif->br_ext_lock);
+
+ hash = __nat25_network_hash(networkAddr);
+ db = vif->nethash[hash];
+
+ while (db != NULL) {
+ if (!memcmp(db->networkAddr, networkAddr, MAX_NETWORK_ADDR_LEN)) {
+ memcpy(db->macAddr, macAddr, ETH_ALEN);
+ db->ageing_timer = jiffies;
+ spin_unlock_bh(&vif->br_ext_lock);
+ return;
+ }
+
+ db = db->next_hash;
+ }
+
+ db = (struct nat25_network_db_entry *)kmalloc(sizeof(*db), in_interrupt() ? GFP_ATOMIC : GFP_KERNEL);
+ if (db == NULL) {
+ spin_unlock_bh(&vif->br_ext_lock);
+ return;
+ }
+
+ memcpy(db->networkAddr, networkAddr, MAX_NETWORK_ADDR_LEN);
+ memcpy(db->macAddr, macAddr, ETH_ALEN);
+ atomic_set(&db->use_count, 1);
+ db->ageing_timer = jiffies;
+
+ __network_hash_link(vif, db, hash);
+
+ spin_unlock_bh(&vif->br_ext_lock);
+}
+
+
+static void __nat25_db_print(struct rwnx_vif *vif)
+{
+ spin_lock_bh(&vif->br_ext_lock);
+
+#ifdef BR_SUPPORT_DEBUG
+ static int counter = 0;
+ int i, j;
+ struct nat25_network_db_entry *db;
+
+ counter++;
+ if ((counter % 16) != 0)
+ return;
+
+ for (i = 0, j = 0; i < NAT25_HASH_SIZE; i++) {
+ db = vif->nethash[i];
+
+ while (db != NULL) {
+#ifdef CL_IPV6_PASS
+ printk("NAT25: DB(%d) H(%02d) C(%d) M:%02x%02x%02x%02x%02x%02x N:%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x"
+ "%02x%02x%02x%02x%02x%02x\n",
+ j,
+ i,
+ atomic_read(&db->use_count),
+ db->macAddr[0],
+ db->macAddr[1],
+ db->macAddr[2],
+ db->macAddr[3],
+ db->macAddr[4],
+ db->macAddr[5],
+ db->networkAddr[0],
+ db->networkAddr[1],
+ db->networkAddr[2],
+ db->networkAddr[3],
+ db->networkAddr[4],
+ db->networkAddr[5],
+ db->networkAddr[6],
+ db->networkAddr[7],
+ db->networkAddr[8],
+ db->networkAddr[9],
+ db->networkAddr[10],
+ db->networkAddr[11],
+ db->networkAddr[12],
+ db->networkAddr[13],
+ db->networkAddr[14],
+ db->networkAddr[15],
+ db->networkAddr[16]);
+#else
+ printk("NAT25: DB(%d) H(%02d) C(%d) M:%02x%02x%02x%02x%02x%02x N:%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n",
+ j,
+ i,
+ atomic_read(&db->use_count),
+ db->macAddr[0],
+ db->macAddr[1],
+ db->macAddr[2],
+ db->macAddr[3],
+ db->macAddr[4],
+ db->macAddr[5],
+ db->networkAddr[0],
+ db->networkAddr[1],
+ db->networkAddr[2],
+ db->networkAddr[3],
+ db->networkAddr[4],
+ db->networkAddr[5],
+ db->networkAddr[6],
+ db->networkAddr[7],
+ db->networkAddr[8],
+ db->networkAddr[9],
+ db->networkAddr[10]);
+#endif
+ j++;
+
+ db = db->next_hash;
+ }
+ }
+#endif
+
+ spin_unlock_bh(&vif->br_ext_lock);
+}
+
+
+
+
+/*
+ * NAT2.5 interface
+ */
+
+void nat25_db_cleanup(struct rwnx_vif *vif)
+{
+ int i;
+ spin_lock_bh(&vif->br_ext_lock);
+
+ for (i = 0; i < NAT25_HASH_SIZE; i++) {
+ struct nat25_network_db_entry *f;
+ f = vif->nethash[i];
+ while (f != NULL) {
+ struct nat25_network_db_entry *g;
+
+ g = f->next_hash;
+ if (vif->scdb_entry == f) {
+ memset(vif->scdb_mac, 0, ETH_ALEN);
+ memset(vif->scdb_ip, 0, 4);
+ vif->scdb_entry = NULL;
+ }
+ __network_hash_unlink(f);
+ kfree(f);
+
+ f = g;
+ }
+ }
+
+ spin_unlock_bh(&vif->br_ext_lock);
+}
+
+
+void nat25_db_expire(struct rwnx_vif *vif)
+{
+ int i;
+ spin_lock_bh(&vif->br_ext_lock);
+
+ /* if(!priv->ethBrExtInfo.nat25_disable) */
+ {
+ for (i = 0; i < NAT25_HASH_SIZE; i++) {
+ struct nat25_network_db_entry *f;
+ f = vif->nethash[i];
+
+ while (f != NULL) {
+ struct nat25_network_db_entry *g;
+ g = f->next_hash;
+
+ if (__nat25_has_expired(vif, f)) {
+ if (atomic_dec_and_test(&f->use_count)) {
+#ifdef BR_SUPPORT_DEBUG
+#ifdef CL_IPV6_PASS
+ panic_printk("NAT25 Expire H(%02d) M:%02x%02x%02x%02x%02x%02x N:%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x"
+ "%02x%02x%02x%02x%02x%02x\n",
+ i,
+ f->macAddr[0],
+ f->macAddr[1],
+ f->macAddr[2],
+ f->macAddr[3],
+ f->macAddr[4],
+ f->macAddr[5],
+ f->networkAddr[0],
+ f->networkAddr[1],
+ f->networkAddr[2],
+ f->networkAddr[3],
+ f->networkAddr[4],
+ f->networkAddr[5],
+ f->networkAddr[6],
+ f->networkAddr[7],
+ f->networkAddr[8],
+ f->networkAddr[9],
+ f->networkAddr[10],
+ f->networkAddr[11],
+ f->networkAddr[12],
+ f->networkAddr[13],
+ f->networkAddr[14],
+ f->networkAddr[15],
+ f->networkAddr[16]);
+#else
+
+ panic_printk("NAT25 Expire H(%02d) M:%02x%02x%02x%02x%02x%02x N:%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n",
+ i,
+ f->macAddr[0],
+ f->macAddr[1],
+ f->macAddr[2],
+ f->macAddr[3],
+ f->macAddr[4],
+ f->macAddr[5],
+ f->networkAddr[0],
+ f->networkAddr[1],
+ f->networkAddr[2],
+ f->networkAddr[3],
+ f->networkAddr[4],
+ f->networkAddr[5],
+ f->networkAddr[6],
+ f->networkAddr[7],
+ f->networkAddr[8],
+ f->networkAddr[9],
+ f->networkAddr[10]);
+#endif
+#endif
+ if (vif->scdb_entry == f) {
+ memset(vif->scdb_mac, 0, ETH_ALEN);
+ memset(vif->scdb_ip, 0, 4);
+ vif->scdb_entry = NULL;
+ }
+ __network_hash_unlink(f);
+ kfree(f);
+ }
+ }
+
+ f = g;
+ }
+ }
+ }
+
+ spin_unlock_bh(&vif->br_ext_lock);
+}
+
+
+#ifdef SUPPORT_TX_MCAST2UNI
+static int checkIPMcAndReplace(struct rwnx_vif *vif, struct sk_buff *skb, unsigned int *dst_ip)
+{
+ struct stat_info *pstat;
+ struct list_head *phead, *plist;
+ int i;
+
+ phead = &vif->asoc_list;
+ plist = phead->next;
+
+ while (plist != phead) {
+ pstat = list_entry(plist, struct stat_info, asoc_list);
+ plist = plist->next;
+
+ if (pstat->ipmc_num == 0)
+ continue;
+
+ for (i = 0; i < MAX_IP_MC_ENTRY; i++) {
+ if (pstat->ipmc[i].used && !memcmp(&pstat->ipmc[i].mcmac[3], ((unsigned char *)dst_ip) + 1, 3)) {
+ memcpy(skb->data, pstat->ipmc[i].mcmac, ETH_ALEN);
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+#endif
+
+int nat25_db_handle(struct rwnx_vif *vif, struct sk_buff *skb, int method)
+{
+ unsigned short protocol;
+ unsigned char networkAddr[MAX_NETWORK_ADDR_LEN];
+
+ if (skb == NULL)
+ return -1;
+
+ if ((method <= NAT25_MIN) || (method >= NAT25_MAX))
+ return -1;
+
+ protocol = *((unsigned short *)(skb->data + 2 * ETH_ALEN));
+
+ /*---------------------------------------------------*/
+ /* Handle IP frame */
+ /*---------------------------------------------------*/
+ if (protocol == __constant_htons(ETH_P_IP)) {
+ struct iphdr *iph = (struct iphdr *)(skb->data + ETH_HLEN);
+
+ if (((unsigned char *)(iph) + (iph->ihl << 2)) >= (skb->data + ETH_HLEN + skb->len)) {
+ printk("NAT25: malformed IP packet !\n");
+ return -1;
+ }
+
+ switch (method) {
+ case NAT25_CHECK:
+ return -1;
+
+ case NAT25_INSERT: {
+ /* some muticast with source IP is all zero, maybe other case is illegal */
+ /* in class A, B, C, host address is all zero or all one is illegal */
+ if (iph->saddr == 0)
+ return 0;
+ printk("NAT25: Insert IP, SA=%08x, DA=%08x\n", iph->saddr, iph->daddr);
+ __nat25_generate_ipv4_network_addr(networkAddr, &iph->saddr);
+ /* record source IP address and , source mac address into db */
+ __nat25_db_network_insert(vif, skb->data + ETH_ALEN, networkAddr);
+
+ __nat25_db_print(vif);
+ }
+ return 0;
+
+ case NAT25_LOOKUP: {
+ printk("NAT25: Lookup IP, SA=%08x, DA=%08x\n", iph->saddr, iph->daddr);
+#ifdef SUPPORT_TX_MCAST2UNI
+ if (vif->pshare->rf_ft_var.mc2u_disable ||
+ ((((OPMODE & (WIFI_STATION_STATE | WIFI_ASOC_STATE))
+ == (WIFI_STATION_STATE | WIFI_ASOC_STATE)) &&
+ !checkIPMcAndReplace(vif, skb, &iph->daddr)) ||
+ (OPMODE & WIFI_ADHOC_STATE)))
+#endif
+ {
+ __nat25_generate_ipv4_network_addr(networkAddr, &iph->daddr);
+
+ if (!__nat25_db_network_lookup_and_replace(vif, skb, networkAddr)) {
+ if (*((unsigned char *)&iph->daddr + 3) == 0xff) {
+ /* L2 is unicast but L3 is broadcast, make L2 bacome broadcast */
+ printk("NAT25: Set DA as boardcast\n");
+ memset(skb->data, 0xff, ETH_ALEN);
+ } else {
+ /* forward unknow IP packet to upper TCP/IP */
+ printk("NAT25: Replace DA with BR's MAC\n");
+ if ((*(u32 *)vif->br_mac) == 0 && (*(u16 *)(vif->br_mac + 4)) == 0) {
+ void netdev_br_init(struct net_device *netdev);
+ printk("Re-init netdev_br_init() due to br_mac==0!\n");
+ netdev_br_init(vif->ndev);
+ }
+ memcpy(skb->data, vif->br_mac, ETH_ALEN);
+ }
+ }
+ }
+ }
+ return 0;
+
+ default:
+ return -1;
+ }
+ }
+
+ /*---------------------------------------------------*/
+ /* Handle ARP frame */
+ /*---------------------------------------------------*/
+ else if (protocol == __constant_htons(ETH_P_ARP)) {
+ struct arphdr *arp = (struct arphdr *)(skb->data + ETH_HLEN);
+ unsigned char *arp_ptr = (unsigned char *)(arp + 1);
+ unsigned int *sender, *target;
+
+ if (arp->ar_pro != __constant_htons(ETH_P_IP)) {
+ printk("NAT25: arp protocol unknown (%4x)!\n", htons(arp->ar_pro));
+ return -1;
+ }
+
+ switch (method) {
+ case NAT25_CHECK:
+ return 0; /* skb_copy for all ARP frame */
+
+ case NAT25_INSERT: {
+ printk("NAT25: Insert ARP, MAC=%02x%02x%02x%02x%02x%02x\n", arp_ptr[0],
+ arp_ptr[1], arp_ptr[2], arp_ptr[3], arp_ptr[4], arp_ptr[5]);
+
+ /* change to ARP sender mac address to wlan STA address */
+ memcpy(arp_ptr, vif->ndev->dev_addr, ETH_ALEN);
+
+ arp_ptr += arp->ar_hln;
+ sender = (unsigned int *)arp_ptr;
+
+ __nat25_generate_ipv4_network_addr(networkAddr, sender);
+
+ __nat25_db_network_insert(vif, skb->data + ETH_ALEN, networkAddr);
+
+ __nat25_db_print(vif);
+ }
+ return 0;
+
+ case NAT25_LOOKUP: {
+ printk("NAT25: Lookup ARP\n");
+
+ arp_ptr += arp->ar_hln;
+ sender = (unsigned int *)arp_ptr;
+ arp_ptr += (arp->ar_hln + arp->ar_pln);
+ target = (unsigned int *)arp_ptr;
+
+ __nat25_generate_ipv4_network_addr(networkAddr, target);
+
+ __nat25_db_network_lookup_and_replace(vif, skb, networkAddr);
+
+ /* change to ARP target mac address to Lookup result */
+ arp_ptr = (unsigned char *)(arp + 1);
+ arp_ptr += (arp->ar_hln + arp->ar_pln);
+ memcpy(arp_ptr, skb->data, ETH_ALEN);
+ }
+ return 0;
+
+ default:
+ return -1;
+ }
+ }
+
+ /*---------------------------------------------------*/
+ /* Handle IPX and Apple Talk frame */
+ /*---------------------------------------------------*/
+ else if ((protocol == __constant_htons(ETH_P_IPX)) ||
+ (protocol == __constant_htons(ETH_P_ATALK)) ||
+ (protocol == __constant_htons(ETH_P_AARP))) {
+ unsigned char ipx_header[2] = {0xFF, 0xFF};
+ struct ipxhdr *ipx = NULL;
+ struct elapaarp *ea = NULL;
+ struct ddpehdr *ddp = NULL;
+ unsigned char *framePtr = skb->data + ETH_HLEN;
+
+ if (protocol == __constant_htons(ETH_P_IPX)) {
+ printk("NAT25: Protocol=IPX (Ethernet II)\n");
+ ipx = (struct ipxhdr *)framePtr;
+ } else { /* if(protocol <= __constant_htons(ETH_FRAME_LEN)) */
+ if (!memcmp(ipx_header, framePtr, 2)) {
+ printk("NAT25: Protocol=IPX (Ethernet 802.3)\n");
+ ipx = (struct ipxhdr *)framePtr;
+ } else {
+ unsigned char ipx_8022_type = 0xE0;
+ unsigned char snap_8022_type = 0xAA;
+
+ if (*framePtr == snap_8022_type) {
+ unsigned char ipx_snap_id[5] = {0x0, 0x0, 0x0, 0x81, 0x37}; /* IPX SNAP ID */
+ unsigned char aarp_snap_id[5] = {0x00, 0x00, 0x00, 0x80, 0xF3}; /* Apple Talk AARP SNAP ID */
+ unsigned char ddp_snap_id[5] = {0x08, 0x00, 0x07, 0x80, 0x9B}; /* Apple Talk DDP SNAP ID */
+
+ framePtr += 3; /* eliminate the 802.2 header */
+
+ if (!memcmp(ipx_snap_id, framePtr, 5)) {
+ framePtr += 5; /* eliminate the SNAP header */
+
+ printk("NAT25: Protocol=IPX (Ethernet SNAP)\n");
+ ipx = (struct ipxhdr *)framePtr;
+ } else if (!memcmp(aarp_snap_id, framePtr, 5)) {
+ framePtr += 5; /* eliminate the SNAP header */
+
+ ea = (struct elapaarp *)framePtr;
+ } else if (!memcmp(ddp_snap_id, framePtr, 5)) {
+ framePtr += 5; /* eliminate the SNAP header */
+
+ ddp = (struct ddpehdr *)framePtr;
+ } else {
+ printk("NAT25: Protocol=Ethernet SNAP %02x%02x%02x%02x%02x\n", framePtr[0],
+ framePtr[1], framePtr[2], framePtr[3], framePtr[4]);
+ return -1;
+ }
+ } else if (*framePtr == ipx_8022_type) {
+ framePtr += 3; /* eliminate the 802.2 header */
+
+ if (!memcmp(ipx_header, framePtr, 2)) {
+ printk("NAT25: Protocol=IPX (Ethernet 802.2)\n");
+ ipx = (struct ipxhdr *)framePtr;
+ } else
+ return -1;
+ }
+ }
+ }
+
+ /* IPX */
+ if (ipx != NULL) {
+ switch (method) {
+ case NAT25_CHECK:
+ if (!memcmp(skb->data + ETH_ALEN, ipx->ipx_source.node, ETH_ALEN)) {
+ printk("NAT25: Check IPX skb_copy\n");
+ return 0;
+ }
+ return -1;
+
+ case NAT25_INSERT: {
+ printk("NAT25: Insert IPX, Dest=%08x,%02x%02x%02x%02x%02x%02x,%04x Source=%08x,%02x%02x%02x%02x%02x%02x,%04x\n",
+ ipx->ipx_dest.net,
+ ipx->ipx_dest.node[0],
+ ipx->ipx_dest.node[1],
+ ipx->ipx_dest.node[2],
+ ipx->ipx_dest.node[3],
+ ipx->ipx_dest.node[4],
+ ipx->ipx_dest.node[5],
+ ipx->ipx_dest.sock,
+ ipx->ipx_source.net,
+ ipx->ipx_source.node[0],
+ ipx->ipx_source.node[1],
+ ipx->ipx_source.node[2],
+ ipx->ipx_source.node[3],
+ ipx->ipx_source.node[4],
+ ipx->ipx_source.node[5],
+ ipx->ipx_source.sock);
+
+ if (!memcmp(skb->data + ETH_ALEN, ipx->ipx_source.node, ETH_ALEN)) {
+ printk("NAT25: Use IPX Net, and Socket as network addr\n");
+
+ __nat25_generate_ipx_network_addr_with_socket(networkAddr, &ipx->ipx_source.net, &ipx->ipx_source.sock);
+
+ /* change IPX source node addr to wlan STA address */
+ memcpy(ipx->ipx_source.node, vif->ndev->dev_addr, ETH_ALEN);
+ } else
+ __nat25_generate_ipx_network_addr_with_node(networkAddr, &ipx->ipx_source.net, ipx->ipx_source.node);
+
+ __nat25_db_network_insert(vif, skb->data + ETH_ALEN, networkAddr);
+
+ __nat25_db_print(vif);
+ }
+ return 0;
+
+ case NAT25_LOOKUP: {
+ if (!memcmp(vif->ndev->dev_addr, ipx->ipx_dest.node, ETH_ALEN)) {
+ printk("NAT25: Lookup IPX, Modify Destination IPX Node addr\n");
+
+ __nat25_generate_ipx_network_addr_with_socket(networkAddr, &ipx->ipx_dest.net, &ipx->ipx_dest.sock);
+
+ __nat25_db_network_lookup_and_replace(vif, skb, networkAddr);
+
+ /* replace IPX destination node addr with Lookup destination MAC addr */
+ memcpy(ipx->ipx_dest.node, skb->data, ETH_ALEN);
+ } else {
+ __nat25_generate_ipx_network_addr_with_node(networkAddr, &ipx->ipx_dest.net, ipx->ipx_dest.node);
+
+ __nat25_db_network_lookup_and_replace(vif, skb, networkAddr);
+ }
+ }
+ return 0;
+
+ default:
+ return -1;
+ }
+ }
+
+ /* AARP */
+ else if (ea != NULL) {
+ /* Sanity check fields. */
+ if (ea->hw_len != ETH_ALEN || ea->pa_len != AARP_PA_ALEN) {
+ printk("NAT25: Appletalk AARP Sanity check fail!\n");
+ return -1;
+ }
+
+ switch (method) {
+ case NAT25_CHECK:
+ return 0;
+
+ case NAT25_INSERT: {
+ /* change to AARP source mac address to wlan STA address */
+ memcpy(ea->hw_src, vif->ndev->dev_addr, ETH_ALEN);
+
+ printk("NAT25: Insert AARP, Source=%d,%d Destination=%d,%d\n",
+ ea->pa_src_net,
+ ea->pa_src_node,
+ ea->pa_dst_net,
+ ea->pa_dst_node);
+
+ __nat25_generate_apple_network_addr(networkAddr, &ea->pa_src_net, &ea->pa_src_node);
+
+ __nat25_db_network_insert(vif, skb->data + ETH_ALEN, networkAddr);
+
+ __nat25_db_print(vif);
+ }
+ return 0;
+
+ case NAT25_LOOKUP: {
+ printk("NAT25: Lookup AARP, Source=%d,%d Destination=%d,%d\n",
+ ea->pa_src_net,
+ ea->pa_src_node,
+ ea->pa_dst_net,
+ ea->pa_dst_node);
+
+ __nat25_generate_apple_network_addr(networkAddr, &ea->pa_dst_net, &ea->pa_dst_node);
+
+ __nat25_db_network_lookup_and_replace(vif, skb, networkAddr);
+
+ /* change to AARP destination mac address to Lookup result */
+ memcpy(ea->hw_dst, skb->data, ETH_ALEN);
+ }
+ return 0;
+
+ default:
+ return -1;
+ }
+ }
+
+ /* DDP */
+ else if (ddp != NULL) {
+ switch (method) {
+ case NAT25_CHECK:
+ return -1;
+
+ case NAT25_INSERT: {
+ printk("NAT25: Insert DDP, Source=%d,%d Destination=%d,%d\n",
+ ddp->deh_snet,
+ ddp->deh_snode,
+ ddp->deh_dnet,
+ ddp->deh_dnode);
+
+ __nat25_generate_apple_network_addr(networkAddr, &ddp->deh_snet, &ddp->deh_snode);
+
+ __nat25_db_network_insert(vif, skb->data + ETH_ALEN, networkAddr);
+
+ __nat25_db_print(vif);
+ }
+ return 0;
+
+ case NAT25_LOOKUP: {
+ printk("NAT25: Lookup DDP, Source=%d,%d Destination=%d,%d\n",
+ ddp->deh_snet,
+ ddp->deh_snode,
+ ddp->deh_dnet,
+ ddp->deh_dnode);
+
+ __nat25_generate_apple_network_addr(networkAddr, &ddp->deh_dnet, &ddp->deh_dnode);
+
+ __nat25_db_network_lookup_and_replace(vif, skb, networkAddr);
+ }
+ return 0;
+
+ default:
+ return -1;
+ }
+ }
+
+ return -1;
+ }
+
+ /*---------------------------------------------------*/
+ /* Handle PPPoE frame */
+ /*---------------------------------------------------*/
+ else if ((protocol == __constant_htons(ETH_P_PPP_DISC)) ||
+ (protocol == __constant_htons(ETH_P_PPP_SES))) {
+ struct pppoe_hdr *ph = (struct pppoe_hdr *)(skb->data + ETH_HLEN);
+ unsigned short *pMagic;
+
+ switch (method) {
+ case NAT25_CHECK:
+ if (ph->sid == 0)
+ return 0;
+ return 1;
+
+ case NAT25_INSERT:
+ if (ph->sid == 0) { /* Discovery phase according to tag */
+ if (ph->code == PADI_CODE || ph->code == PADR_CODE) {
+ if (vif->ethBrExtInfo.addPPPoETag) {
+ struct pppoe_tag *tag, *pOldTag;
+ unsigned char tag_buf[40];
+ int old_tag_len = 0;
+
+ tag = (struct pppoe_tag *)tag_buf;
+ pOldTag = (struct pppoe_tag *)__nat25_find_pppoe_tag(ph, ntohs(PTT_RELAY_SID));
+ if (pOldTag) { /* if SID existed, copy old value and delete it */
+ old_tag_len = ntohs(pOldTag->tag_len);
+ if (old_tag_len + TAG_HDR_LEN + MAGIC_CODE_LEN + RTL_RELAY_TAG_LEN > sizeof(tag_buf)) {
+ printk("SID tag length too long!\n");
+ return -1;
+ }
+
+ memcpy(tag->tag_data + MAGIC_CODE_LEN + RTL_RELAY_TAG_LEN,
+ pOldTag->tag_data, old_tag_len);
+
+ if (skb_pull_and_merge(skb, (unsigned char *)pOldTag, TAG_HDR_LEN + old_tag_len) < 0) {
+ printk("call skb_pull_and_merge() failed in PADI/R packet!\n");
+ return -1;
+ }
+ ph->length = htons(ntohs(ph->length) - TAG_HDR_LEN - old_tag_len);
+ }
+
+ tag->tag_type = PTT_RELAY_SID;
+ tag->tag_len = htons(MAGIC_CODE_LEN + RTL_RELAY_TAG_LEN + old_tag_len);
+
+ /* insert the magic_code+client mac in relay tag */
+ pMagic = (unsigned short *)tag->tag_data;
+ *pMagic = htons(MAGIC_CODE);
+ memcpy(tag->tag_data + MAGIC_CODE_LEN, skb->data + ETH_ALEN, ETH_ALEN);
+
+ /* Add relay tag */
+ if (__nat25_add_pppoe_tag(skb, tag) < 0)
+ return -1;
+
+ printk("NAT25: Insert PPPoE, forward %s packet\n",
+ (ph->code == PADI_CODE ? "PADI" : "PADR"));
+ } else { /* not add relay tag */
+ if (vif->pppoe_connection_in_progress &&
+ memcmp(skb->data + ETH_ALEN, vif->pppoe_addr, ETH_ALEN)) {
+ printk("Discard PPPoE packet due to another PPPoE connection is in progress!\n");
+ return -2;
+ }
+
+ if (vif->pppoe_connection_in_progress == 0)
+ memcpy(vif->pppoe_addr, skb->data + ETH_ALEN, ETH_ALEN);
+
+ vif->pppoe_connection_in_progress = WAIT_TIME_PPPOE;
+ }
+ } else
+ return -1;
+ } else { /* session phase */
+ printk("NAT25: Insert PPPoE, insert session packet to %s\n", skb->dev->name);
+
+ __nat25_generate_pppoe_network_addr(networkAddr, skb->data, &(ph->sid));
+
+ __nat25_db_network_insert(vif, skb->data + ETH_ALEN, networkAddr);
+
+ __nat25_db_print(vif);
+
+ if (!vif->ethBrExtInfo.addPPPoETag &&
+ vif->pppoe_connection_in_progress &&
+ !memcmp(skb->data + ETH_ALEN, vif->pppoe_addr, ETH_ALEN))
+ vif->pppoe_connection_in_progress = 0;
+ }
+ return 0;
+
+ case NAT25_LOOKUP:
+ if (ph->code == PADO_CODE || ph->code == PADS_CODE) {
+ if (vif->ethBrExtInfo.addPPPoETag) {
+ struct pppoe_tag *tag;
+ unsigned char *ptr;
+ unsigned short tagType, tagLen;
+ int offset = 0;
+
+ ptr = __nat25_find_pppoe_tag(ph, ntohs(PTT_RELAY_SID));
+ if (ptr == 0) {
+ printk("Fail to find PTT_RELAY_SID in FADO!\n");
+ return -1;
+ }
+
+ tag = (struct pppoe_tag *)ptr;
+ tagType = (unsigned short)((ptr[0] << 8) + ptr[1]);
+ tagLen = (unsigned short)((ptr[2] << 8) + ptr[3]);
+
+ if ((tagType != ntohs(PTT_RELAY_SID)) || (tagLen < (MAGIC_CODE_LEN + RTL_RELAY_TAG_LEN))) {
+ printk("Invalid PTT_RELAY_SID tag length [%d]!\n", tagLen);
+ return -1;
+ }
+
+ pMagic = (unsigned short *)tag->tag_data;
+ if (ntohs(*pMagic) != MAGIC_CODE) {
+ printk("Can't find MAGIC_CODE in %s packet!\n",
+ (ph->code == PADO_CODE ? "PADO" : "PADS"));
+ return -1;
+ }
+
+ memcpy(skb->data, tag->tag_data + MAGIC_CODE_LEN, ETH_ALEN);
+
+ if (tagLen > MAGIC_CODE_LEN + RTL_RELAY_TAG_LEN)
+ offset = TAG_HDR_LEN;
+
+ if (skb_pull_and_merge(skb, ptr + offset, TAG_HDR_LEN + MAGIC_CODE_LEN + RTL_RELAY_TAG_LEN - offset) < 0) {
+ printk("call skb_pull_and_merge() failed in PADO packet!\n");
+ return -1;
+ }
+ ph->length = htons(ntohs(ph->length) - (TAG_HDR_LEN + MAGIC_CODE_LEN + RTL_RELAY_TAG_LEN - offset));
+ if (offset > 0)
+ tag->tag_len = htons(tagLen - MAGIC_CODE_LEN - RTL_RELAY_TAG_LEN);
+
+ printk("NAT25: Lookup PPPoE, forward %s Packet from %s\n",
+ (ph->code == PADO_CODE ? "PADO" : "PADS"), skb->dev->name);
+ } else { /* not add relay tag */
+ if (!vif->pppoe_connection_in_progress) {
+ printk("Discard PPPoE packet due to no connection in progresss!\n");
+ return -1;
+ }
+ memcpy(skb->data, vif->pppoe_addr, ETH_ALEN);
+ vif->pppoe_connection_in_progress = WAIT_TIME_PPPOE;
+ }
+ } else {
+ if (ph->sid != 0) {
+ printk("NAT25: Lookup PPPoE, lookup session packet from %s\n", skb->dev->name);
+ __nat25_generate_pppoe_network_addr(networkAddr, skb->data + ETH_ALEN, &(ph->sid));
+
+ __nat25_db_network_lookup_and_replace(vif, skb, networkAddr);
+
+ __nat25_db_print(vif);
+ } else
+ return -1;
+
+ }
+ return 0;
+
+ default:
+ return -1;
+ }
+ }
+
+ /*---------------------------------------------------*/
+ /* Handle EAP frame */
+ /*---------------------------------------------------*/
+ else if (protocol == __constant_htons(0x888e)) {
+ switch (method) {
+ case NAT25_CHECK:
+ return -1;
+
+ case NAT25_INSERT:
+ return 0;
+
+ case NAT25_LOOKUP:
+ return 0;
+
+ default:
+ return -1;
+ }
+ }
+
+ /*---------------------------------------------------*/
+ /* Handle C-Media proprietary frame */
+ /*---------------------------------------------------*/
+ else if ((protocol == __constant_htons(0xe2ae)) ||
+ (protocol == __constant_htons(0xe2af))) {
+ switch (method) {
+ case NAT25_CHECK:
+ return -1;
+
+ case NAT25_INSERT:
+ return 0;
+
+ case NAT25_LOOKUP:
+ return 0;
+
+ default:
+ return -1;
+ }
+ }
+
+ /*---------------------------------------------------*/
+ /* Handle IPV6 frame */
+ /*---------------------------------------------------*/
+#ifdef CL_IPV6_PASS
+ else if (protocol == __constant_htons(ETH_P_IPV6)) {
+ struct ipv6hdr *iph = (struct ipv6hdr *)(skb->data + ETH_HLEN);
+
+ if (sizeof(*iph) >= (skb->len - ETH_HLEN)) {
+ printk("NAT25: malformed IPv6 packet !\n");
+ return -1;
+ }
+
+ switch (method) {
+ case NAT25_CHECK:
+ if (skb->data[0] & 1)
+ return 0;
+ return -1;
+
+ case NAT25_INSERT: {
+ printk("NAT25: Insert IP, SA=%4x:%4x:%4x:%4x:%4x:%4x:%4x:%4x,"
+ " DA=%4x:%4x:%4x:%4x:%4x:%4x:%4x:%4x\n",
+ iph->saddr.s6_addr16[0], iph->saddr.s6_addr16[1], iph->saddr.s6_addr16[2], iph->saddr.s6_addr16[3],
+ iph->saddr.s6_addr16[4], iph->saddr.s6_addr16[5], iph->saddr.s6_addr16[6], iph->saddr.s6_addr16[7],
+ iph->daddr.s6_addr16[0], iph->daddr.s6_addr16[1], iph->daddr.s6_addr16[2], iph->daddr.s6_addr16[3],
+ iph->daddr.s6_addr16[4], iph->daddr.s6_addr16[5], iph->daddr.s6_addr16[6], iph->daddr.s6_addr16[7]);
+
+ if (memcmp(&iph->saddr, "\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0", 16)) {
+ __nat25_generate_ipv6_network_addr(networkAddr, (unsigned int *)&iph->saddr);
+ __nat25_db_network_insert(vif, skb->data + ETH_ALEN, networkAddr);
+ __nat25_db_print(vif);
+
+ if (iph->nexthdr == IPPROTO_ICMPV6 &&
+ skb->len > (ETH_HLEN + sizeof(*iph) + 4)) {
+ if (update_nd_link_layer_addr(skb->data + ETH_HLEN + sizeof(*iph),
+ skb->len - ETH_HLEN - sizeof(*iph), vif->ndev->dev_addr)) {
+ struct icmp6hdr *hdr = (struct icmp6hdr *)(skb->data + ETH_HLEN + sizeof(*iph));
+ hdr->icmp6_cksum = 0;
+ hdr->icmp6_cksum = csum_ipv6_magic(&iph->saddr, &iph->daddr,
+ iph->payload_len,
+ IPPROTO_ICMPV6,
+ csum_partial((__u8 *)hdr, iph->payload_len, 0));
+ }
+ }
+ }
+ }
+ return 0;
+
+ case NAT25_LOOKUP:
+ printk("NAT25: Lookup IP, SA=%4x:%4x:%4x:%4x:%4x:%4x:%4x:%4x,"
+ " DA=%4x:%4x:%4x:%4x:%4x:%4x:%4x:%4x\n",
+ iph->saddr.s6_addr16[0], iph->saddr.s6_addr16[1], iph->saddr.s6_addr16[2], iph->saddr.s6_addr16[3],
+ iph->saddr.s6_addr16[4], iph->saddr.s6_addr16[5], iph->saddr.s6_addr16[6], iph->saddr.s6_addr16[7],
+ iph->daddr.s6_addr16[0], iph->daddr.s6_addr16[1], iph->daddr.s6_addr16[2], iph->daddr.s6_addr16[3],
+ iph->daddr.s6_addr16[4], iph->daddr.s6_addr16[5], iph->daddr.s6_addr16[6], iph->daddr.s6_addr16[7]);
+
+
+ __nat25_generate_ipv6_network_addr(networkAddr, (unsigned int *)&iph->daddr);
+ if (!__nat25_db_network_lookup_and_replace(vif, skb, networkAddr)) {
+#ifdef SUPPORT_RX_UNI2MCAST
+ if (iph->daddr.s6_addr[0] == 0xff)
+ convert_ipv6_mac_to_mc(skb);
+#endif
+ }
+ return 0;
+
+ default:
+ return -1;
+ }
+ }
+#endif /* CL_IPV6_PASS */
+
+ return -1;
+}
+
+
+int nat25_handle_frame(struct rwnx_vif *vif, struct sk_buff *skb)
+{
+ //printk("%s : vif_type=%d \n",__func__,RWNX_VIF_TYPE(vif));
+#ifdef BR_SUPPORT_DEBUG
+ if ((!vif->ethBrExtInfo.nat25_disable) && (!(skb->data[0] & 1))) {
+ printk("NAT25: Input Frame: DA=%02x%02x%02x%02x%02x%02x SA=%02x%02x%02x%02x%02x%02x\n",
+ skb->data[0],
+ skb->data[1],
+ skb->data[2],
+ skb->data[3],
+ skb->data[4],
+ skb->data[5],
+ skb->data[6],
+ skb->data[7],
+ skb->data[8],
+ skb->data[9],
+ skb->data[10],
+ skb->data[11]);
+ }
+#endif
+
+ if (!(skb->data[0] & 1)) {
+ int is_vlan_tag = 0, i, retval = 0;
+ unsigned short vlan_hdr = 0;
+
+ if (*((unsigned short *)(skb->data + ETH_ALEN * 2)) == __constant_htons(ETH_P_8021Q)) {
+ is_vlan_tag = 1;
+ vlan_hdr = *((unsigned short *)(skb->data + ETH_ALEN * 2 + 2));
+ for (i = 0; i < 6; i++)
+ *((unsigned short *)(skb->data + ETH_ALEN * 2 + 2 - i * 2)) = *((unsigned short *)(skb->data + ETH_ALEN * 2 - 2 - i * 2));
+ skb_pull(skb, 4);
+ }
+
+ if (!vif->ethBrExtInfo.nat25_disable) {
+ unsigned long irqL;
+ spin_lock_bh(&vif->br_ext_lock);
+ /*
+ * This function look up the destination network address from
+ * the NAT2.5 database. Return value = -1 means that the
+ * corresponding network protocol is NOT support.
+ */
+ if (!vif->ethBrExtInfo.nat25sc_disable &&
+ (*((unsigned short *)(skb->data + ETH_ALEN * 2)) == __constant_htons(ETH_P_IP)) &&
+ !memcmp(vif->scdb_ip, skb->data + ETH_HLEN + 16, 4)) {
+ memcpy(skb->data, vif->scdb_mac, ETH_ALEN);
+
+ spin_unlock_bh(&vif->br_ext_lock);
+ } else {
+ spin_unlock_bh(&vif->br_ext_lock);
+
+ retval = nat25_db_handle(vif, skb, NAT25_LOOKUP);
+ }
+ } else {
+ if (((*((unsigned short *)(skb->data + ETH_ALEN * 2)) == __constant_htons(ETH_P_IP)) &&
+ !memcmp(vif->br_ip, skb->data + ETH_HLEN + 16, 4)) ||
+ ((*((unsigned short *)(skb->data + ETH_ALEN * 2)) == __constant_htons(ETH_P_ARP)) &&
+ !memcmp(vif->br_ip, skb->data + ETH_HLEN + 24, 4))) {
+ /* for traffic to upper TCP/IP */
+ retval = nat25_db_handle(vif, skb, NAT25_LOOKUP);
+ }
+ }
+
+ if (is_vlan_tag) {
+ skb_push(skb, 4);
+ for (i = 0; i < 6; i++)
+ *((unsigned short *)(skb->data + i * 2)) = *((unsigned short *)(skb->data + 4 + i * 2));
+ *((unsigned short *)(skb->data + ETH_ALEN * 2)) = __constant_htons(ETH_P_8021Q);
+ *((unsigned short *)(skb->data + ETH_ALEN * 2 + 2)) = vlan_hdr;
+ }
+
+ if (retval == -1) {
+ /* DEBUG_ERR("NAT25: Lookup fail!\n"); */
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+#if 0
+void mac_clone(_adapter *priv, unsigned char *addr)
+{
+ struct sockaddr sa;
+
+ memcpy(sa.sa_data, addr, ETH_ALEN);
+ RTW_INFO("MAC Clone: Addr=%02x%02x%02x%02x%02x%02x\n",
+ addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
+ rtl8192cd_set_hwaddr(priv->dev, &sa);
+}
+
+
+int mac_clone_handle_frame(_adapter *priv, struct sk_buff *skb)
+{
+ if (priv->ethBrExtInfo.macclone_enable && !priv->macclone_completed) {
+ if (!(skb->data[ETH_ALEN] & 1)) { /* check any other particular MAC add */
+ if (memcmp(skb->data + ETH_ALEN, GET_MY_HWADDR(priv), ETH_ALEN) &&
+ ((priv->dev->br_port) &&
+ memcmp(skb->data + ETH_ALEN, priv->br_mac, ETH_ALEN))) {
+ mac_clone(priv, skb->data + ETH_ALEN);
+ priv->macclone_completed = 1;
+ }
+ }
+ }
+
+ return 0;
+}
+#endif /* 0 */
+
+#define SERVER_PORT 67
+#define CLIENT_PORT 68
+#define DHCP_MAGIC 0x63825363
+#define BROADCAST_FLAG 0x8000
+
+struct dhcpMessage {
+ u_int8_t op;
+ u_int8_t htype;
+ u_int8_t hlen;
+ u_int8_t hops;
+ u_int32_t xid;
+ u_int16_t secs;
+ u_int16_t flags;
+ u_int32_t ciaddr;
+ u_int32_t yiaddr;
+ u_int32_t siaddr;
+ u_int32_t giaddr;
+ u_int8_t chaddr[16];
+ u_int8_t sname[64];
+ u_int8_t file[128];
+ u_int32_t cookie;
+ u_int8_t options[308]; /* 312 - cookie */
+};
+
+void dhcp_flag_bcast(struct rwnx_vif *vif, struct sk_buff *skb)
+{
+ if (skb == NULL)
+ return;
+ //print_hex_dump(KERN_ERR, "SKB DUMP: SKB->DATA== ", DUMP_PREFIX_NONE, 32, 1, skb->data, 64,false);
+ if (!vif->ethBrExtInfo.dhcp_bcst_disable) {
+ unsigned short protocol = *((unsigned short *)(skb->data + 2 * ETH_ALEN));
+ printk("%s protocol: %04x\n", __func__, protocol);
+
+ if (protocol == __constant_htons(ETH_P_IP)) { /* IP */
+ struct iphdr *iph = (struct iphdr *)(skb->data + ETH_HLEN);
+
+ if (iph->protocol == IPPROTO_UDP) { /* UDP */
+ struct udphdr *udph = (struct udphdr *)((u8 *)iph + (iph->ihl << 2));
+
+ if ((udph->source == __constant_htons(CLIENT_PORT))
+ && (udph->dest == __constant_htons(SERVER_PORT))) { /* DHCP request */
+ struct dhcpMessage *dhcph =
+ (struct dhcpMessage *)((u8 *)udph + sizeof(struct udphdr));
+
+ if (dhcph->cookie == __constant_htonl(DHCP_MAGIC)) { /* match magic word */
+ if (!(dhcph->flags & htons(BROADCAST_FLAG))) { /* if not broadcast */
+ register int sum = 0;
+
+ printk("DHCP: change flag of DHCP request to broadcast.\n");
+
+ #if 1
+ /* or BROADCAST flag */
+ dhcph->flags |= htons(BROADCAST_FLAG);
+ /* recalculate checksum */
+ sum = ~(udph->check) & 0xffff;
+ sum += dhcph->flags;
+ while (sum >> 16)
+ sum = (sum & 0xffff) + (sum >> 16);
+ udph->check = ~sum;
+ #endif
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+
+void *scdb_findEntry(struct rwnx_vif *vif, unsigned char *macAddr,
+ unsigned char *ipAddr)
+{
+ printk("%s()\n",__func__);
+ unsigned char networkAddr[MAX_NETWORK_ADDR_LEN];
+ struct nat25_network_db_entry *db;
+ int hash;
+ /* _irqL irqL; */
+ /* _enter_critical_bh(&priv->br_ext_lock, &irqL); */
+
+ __nat25_generate_ipv4_network_addr(networkAddr, (unsigned int *)ipAddr);
+ hash = __nat25_network_hash(networkAddr);
+ db = vif->nethash[hash];
+ while (db != NULL) {
+ if (!memcmp(db->networkAddr, networkAddr, MAX_NETWORK_ADDR_LEN)) {
+ /* _exit_critical_bh(&priv->br_ext_lock, &irqL); */
+ return (void *)db;
+ }
+
+ db = db->next_hash;
+ }
+
+ /* _exit_critical_bh(&priv->br_ext_lock, &irqL); */
+ return NULL;
+}
+
+#endif /* CONFIG_BR_SUPPORT */
diff --git a/drivers/net/wireless/aic8800/aic8800_fdrv/aic_br_ext.h b/drivers/net/wireless/aic8800/aic8800_fdrv/aic_br_ext.h
new file mode 100644
index 000000000000..71ebeb293b8e
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic8800_fdrv/aic_br_ext.h
@@ -0,0 +1,73 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2017 Realtek Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ *****************************************************************************/
+#ifndef _AIC_BR_EXT_H_
+#define _AIC_BR_EXT_H_
+
+#define CL_IPV6_PASS 1
+#define MACADDRLEN 6
+#define WLAN_ETHHDR_LEN 14
+
+#define NAT25_HASH_BITS 4
+#define NAT25_HASH_SIZE (1 << NAT25_HASH_BITS)
+#define NAT25_AGEING_TIME 300
+
+#define NDEV_FMT "%s"
+#define NDEV_ARG(ndev) ndev->name
+#define ADPT_FMT "%s"
+//#define ADPT_ARG(adapter) (adapter->pnetdev ? adapter->pnetdev->name : NULL)
+#define FUNC_NDEV_FMT "%s(%s)"
+#define FUNC_NDEV_ARG(ndev) __func__, ndev->name
+#define FUNC_ADPT_FMT "%s(%s)"
+//#define FUNC_ADPT_ARG(adapter) __func__, (adapter->pnetdev ? adapter->pnetdev->name : NULL)
+#define MAC_FMT "%02x:%02x:%02x:%02x:%02x:%02x"
+#define MAC_ARG(x) ((u8 *)(x))[0], ((u8 *)(x))[1], ((u8 *)(x))[2], ((u8 *)(x))[3], ((u8 *)(x))[4], ((u8 *)(x))[5]
+
+
+#ifdef CL_IPV6_PASS
+ #define MAX_NETWORK_ADDR_LEN 17
+#else
+ #define MAX_NETWORK_ADDR_LEN 11
+#endif
+
+struct nat25_network_db_entry {
+ struct nat25_network_db_entry *next_hash;
+ struct nat25_network_db_entry **pprev_hash;
+ atomic_t use_count;
+ unsigned char macAddr[6];
+ unsigned long ageing_timer;
+ unsigned char networkAddr[MAX_NETWORK_ADDR_LEN];
+};
+
+enum NAT25_METHOD {
+ NAT25_MIN,
+ NAT25_CHECK,
+ NAT25_INSERT,
+ NAT25_LOOKUP,
+ NAT25_PARSE,
+ NAT25_MAX
+};
+
+struct br_ext_info {
+ unsigned int nat25_disable;
+ unsigned int macclone_enable;
+ unsigned int dhcp_bcst_disable;
+ int addPPPoETag; /* 1: Add PPPoE relay-SID, 0: disable */
+ unsigned char nat25_dmzMac[MACADDRLEN];
+ unsigned int nat25sc_disable;
+};
+
+void nat25_db_cleanup(struct rwnx_vif *vif);
+
+#endif /* _AIC_BR_EXT_H_ */
diff --git a/drivers/net/wireless/aic8800/aic8800_fdrv/aic_vendor.c b/drivers/net/wireless/aic8800/aic8800_fdrv/aic_vendor.c
new file mode 100644
index 000000000000..4e70583bd770
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic8800_fdrv/aic_vendor.c
@@ -0,0 +1,879 @@
+#include "aic_vendor.h"
+#include "rwnx_defs.h"
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/inetdevice.h>
+#include <linux/rtnetlink.h>
+#include <net/netlink.h>
+#include "rwnx_version_gen.h"
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
+
+static struct wifi_ring_buffer_status ring_buffer[] = {
+ {
+ .name = "aicwf_ring_buffer0",
+ .flags = 0,
+ .ring_id = 0,
+ .verbose_level = 0,
+ .written_bytes = 0,
+ .read_bytes = 0,
+ .written_records = 0,
+ },
+};
+
+static struct wlan_driver_wake_reason_cnt_t wake_reason_cnt = {
+ .total_cmd_event_wake = 10,
+};
+#endif
+
+int aic_dev_start_mkeep_alive(struct rwnx_hw *rwnx_hw, struct rwnx_vif *rwnx_vif,
+ u8 mkeep_alive_id, u8 *ip_pkt, u16 ip_pkt_len, u8 *src_mac, u8 *dst_mac, u32 period_msec)
+{
+ u8 *data, *pos;
+
+ data = kzalloc(ip_pkt_len + 14, GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ pos = data;
+ memcpy(pos, dst_mac, 6);
+ pos += 6;
+ memcpy(pos, src_mac, 6);
+ pos += 6;
+ /* Mapping Ethernet type (ETHERTYPE_IP: 0x0800) */
+ *(pos++) = 0x08;
+ *(pos++) = 0x00;
+
+ /* Mapping IP pkt */
+ memcpy(pos, ip_pkt, ip_pkt_len);
+ pos += ip_pkt_len;
+
+ //add send 802.3 pkt(raw data)
+ kfree(data);
+
+ return 0;
+}
+
+int aic_dev_stop_mkeep_alive(struct rwnx_hw *rwnx_hw, struct rwnx_vif *rwnx_vif, u8 mkeep_alive_id)
+{
+ int res = -1;
+
+ /*
+ * The mkeep_alive packet is for STA interface only; if the bss is configured as AP,
+ * dongle shall reject a mkeep_alive request.
+ */
+ if (rwnx_vif->wdev.iftype != NL80211_IFTYPE_STATION)
+ return res;
+
+ printk("%s execution\n", __func__);
+
+ //add send stop keep alive
+ res = 0;
+ return res;
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
+static int aicwf_vendor_start_mkeep_alive(struct wiphy *wiphy, struct wireless_dev *wdev,
+ const void *data, int len)
+{
+ /* max size of IP packet for keep alive */
+ const int MKEEP_ALIVE_IP_PKT_MAX = 256;
+
+ int ret = 0, rem, type;
+ u8 mkeep_alive_id = 0;
+ u8 *ip_pkt = NULL;
+ u16 ip_pkt_len = 0;
+ u8 src_mac[6];
+ u8 dst_mac[6];
+ u32 period_msec = 0;
+ const struct nlattr *iter;
+ struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy);
+ struct rwnx_vif *rwnx_vif = container_of(wdev, struct rwnx_vif, wdev);
+ gfp_t kflags = in_atomic() ? GFP_ATOMIC : GFP_KERNEL;
+ printk("%s\n", __func__);
+
+ nla_for_each_attr(iter, data, len, rem) {
+ type = nla_type(iter);
+ switch (type) {
+ case MKEEP_ALIVE_ATTRIBUTE_ID:
+ mkeep_alive_id = nla_get_u8(iter);
+ break;
+ case MKEEP_ALIVE_ATTRIBUTE_IP_PKT_LEN:
+ ip_pkt_len = nla_get_u16(iter);
+ if (ip_pkt_len > MKEEP_ALIVE_IP_PKT_MAX) {
+ ret = -EINVAL;
+ goto exit;
+ }
+ break;
+ case MKEEP_ALIVE_ATTRIBUTE_IP_PKT:
+ if (!ip_pkt_len) {
+ ret = -EINVAL;
+ printk("ip packet length is 0\n");
+ goto exit;
+ }
+ ip_pkt = (u8 *)kzalloc(ip_pkt_len, kflags);
+ if (ip_pkt == NULL) {
+ ret = -ENOMEM;
+ printk("Failed to allocate mem for ip packet\n");
+ goto exit;
+ }
+ memcpy(ip_pkt, (u8 *)nla_data(iter), ip_pkt_len);
+ break;
+ case MKEEP_ALIVE_ATTRIBUTE_SRC_MAC_ADDR:
+ memcpy(src_mac, nla_data(iter), 6);
+ break;
+ case MKEEP_ALIVE_ATTRIBUTE_DST_MAC_ADDR:
+ memcpy(dst_mac, nla_data(iter), 6);
+ break;
+ case MKEEP_ALIVE_ATTRIBUTE_PERIOD_MSEC:
+ period_msec = nla_get_u32(iter);
+ break;
+ default:
+ pr_err("%s(%d), Unknown type: %d\n", __func__, __LINE__, type);
+ ret = -EINVAL;
+ goto exit;
+ }
+ }
+
+ if (ip_pkt == NULL) {
+ ret = -EINVAL;
+ printk("ip packet is NULL\n");
+ goto exit;
+ }
+
+ ret = aic_dev_start_mkeep_alive(rwnx_hw, rwnx_vif, mkeep_alive_id, ip_pkt, ip_pkt_len, src_mac,
+ dst_mac, period_msec);
+ if (ret < 0) {
+ printk("start_mkeep_alive is failed ret: %d\n", ret);
+ }
+
+exit:
+ if (ip_pkt) {
+ kfree(ip_pkt);
+ }
+
+ return ret;
+}
+
+static int aicwf_vendor_stop_mkeep_alive(struct wiphy *wiphy, struct wireless_dev *wdev,
+ const void *data, int len)
+{
+ int ret = 0, rem, type;
+ u8 mkeep_alive_id = 0;
+ const struct nlattr *iter;
+ struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy);
+ struct rwnx_vif *rwnx_vif = container_of(wdev, struct rwnx_vif, wdev);
+
+ printk("%s\n", __func__);
+ nla_for_each_attr(iter, data, len, rem) {
+ type = nla_type(iter);
+ switch (type) {
+ case MKEEP_ALIVE_ATTRIBUTE_ID:
+ mkeep_alive_id = nla_get_u8(iter);
+ break;
+ default:
+ pr_err("%s(%d), Unknown type: %d\n", __func__, __LINE__, type);
+ ret = -EINVAL;
+ break;
+ }
+ }
+
+ ret = aic_dev_stop_mkeep_alive(rwnx_hw, rwnx_vif, mkeep_alive_id);
+ if (ret < 0) {
+ printk("stop_mkeep_alive is failed ret: %d\n", ret);
+ }
+
+ return ret;
+}
+
+static int aicwf_vendor_get_ver(struct wiphy *wiphy, struct wireless_dev *wdev,
+ const void *data, int len)
+{
+ int ret = 0, rem, type;
+ const struct nlattr *iter;
+ int payload = 0;
+ char version[128];
+ int attr = -1;
+ struct sk_buff *reply;
+
+ nla_for_each_attr(iter, data, len, rem) {
+ type = nla_type(iter);
+ switch (type) {
+ case LOGGER_ATTRIBUTE_DRIVER_VER:
+ memcpy(version, RWNX_VERS_BANNER, sizeof(RWNX_VERS_BANNER));
+ payload = strlen(version);
+ attr = LOGGER_ATTRIBUTE_DRIVER_VER;
+ break;
+ case LOGGER_ATTRIBUTE_FW_VER:
+ memcpy(version, wiphy->fw_version, sizeof(wiphy->fw_version));
+ payload = strlen(version);
+ attr = LOGGER_ATTRIBUTE_FW_VER;
+ break;
+ default:
+ AICWFDBG(LOGERROR, "%s(%d), Unknown type: %d\n", __func__, __LINE__, type);
+ return -EINVAL;
+ }
+ }
+
+ if (attr < 0)
+ return -EINVAL;
+
+ reply = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, payload);
+
+ if (!reply)
+ return -ENOMEM;
+
+ if (nla_put(reply, attr,
+ payload, version)) {
+ wiphy_err(wiphy, "%s put version error\n", __func__);
+ goto out_put_fail;
+ }
+
+ ret = cfg80211_vendor_cmd_reply(reply);
+ if (ret)
+ wiphy_err(wiphy, "%s reply cmd error\n", __func__);
+ return ret;
+
+out_put_fail:
+ kfree_skb(reply);
+ return -EMSGSIZE;
+}
+
+static int aicwf_vendor_subcmd_get_channel_list(struct wiphy *wiphy, struct wireless_dev *wdev,
+ const void *data, int len)
+{
+ int ret = 0, rem, type;
+ const struct nlattr *iter;
+ struct sk_buff *reply;
+ int num_channels = 0;
+ int *channel_list = NULL;
+ int payload;
+ int i = 0;
+ struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy);
+ struct ieee80211_supported_band *rwnx_band_2GHz = rwnx_hw->wiphy->bands[NL80211_BAND_2GHZ];
+ struct ieee80211_supported_band *rwnx_band_5GHz = rwnx_hw->wiphy->bands[NL80211_BAND_5GHZ];
+
+ num_channels += rwnx_band_2GHz->n_channels;
+ num_channels += (rwnx_hw->band_5g_support) ? rwnx_band_5GHz->n_channels : 0;
+
+ channel_list = (int *)kzalloc(sizeof(int) * num_channels, GFP_KERNEL);
+ if (!channel_list)
+ return -ENOMEM;
+
+ for (i = 0; i < rwnx_band_2GHz->n_channels; i++)
+ channel_list[i] = rwnx_band_2GHz->channels[i].center_freq;
+
+ for (; rwnx_hw->band_5g_support && i < num_channels; i++)
+ channel_list[i] = rwnx_band_5GHz->channels[i].center_freq;
+
+ payload = sizeof(num_channels) + sizeof(int) * num_channels + 4;
+
+ nla_for_each_attr(iter, data, len, rem) {
+ type = nla_type(iter);
+ switch (type) {
+ case GSCAN_ATTRIBUTE_BAND:
+ reply = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, payload);
+
+ if (!reply)
+ return -ENOMEM;
+
+ if (nla_put_u32(reply, GSCAN_ATTRIBUTE_NUM_CHANNELS, num_channels))
+ goto out_put_fail;
+
+ if (nla_put(reply, GSCAN_ATTRIBUTE_CHANNEL_LIST, sizeof(int) * num_channels, channel_list))
+ goto out_put_fail;
+
+ ret = cfg80211_vendor_cmd_reply(reply);
+ if (ret)
+ wiphy_err(wiphy, "%s reply cmd error\n", __func__);
+ break;
+ default:
+ pr_err("%s(%d), Unknown type: %d\n", __func__, __LINE__, type);
+ return -EINVAL;
+ }
+ }
+
+ kfree(channel_list);
+ return ret;
+
+out_put_fail:
+ kfree(channel_list);
+ kfree_skb(reply);
+ return -EMSGSIZE;
+}
+
+static int aicwf_vendor_subcmd_set_country_code(struct wiphy *wiphy, struct wireless_dev *wdev,
+ const void *data, int len)
+{
+ int ret = 0, rem, type;
+ const struct nlattr *iter;
+
+ nla_for_each_attr(iter, data, len, rem) {
+ type = nla_type(iter);
+ switch (type) {
+ case ANDR_WIFI_ATTRIBUTE_COUNTRY:
+ printk("%s(%d), ANDR_WIFI_ATTRIBUTE_COUNTRY: %s\n", __func__, __LINE__, (char *)nla_data(iter));
+ break;
+ default:
+ pr_err("%s(%d), Unknown type: %d\n", __func__, __LINE__, type);
+ return -EINVAL;
+ }
+ }
+
+ /* TODO
+ * Add handle in the future!
+ */
+
+ return ret;
+}
+
+static int aicwf_vendor_logger_trigger_memory_dump(struct wiphy *wiphy, struct wireless_dev *wdev,
+ const void *data, int len)
+{
+ /* TODO
+ * Add handle in the future!
+ */
+ return 0;
+}
+
+static int aicwf_vendor_subcmd_get_feature_set(struct wiphy *wiphy, struct wireless_dev *wdev,
+ const void *data, int len)
+{
+ int ret;
+ struct sk_buff *reply;
+ uint32_t feature = 0, payload;
+ struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy);
+
+ payload = sizeof(feature);
+ reply = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, payload);
+
+ if (!reply)
+ return -ENOMEM;
+
+ /* TODO
+ * Add handle in the future!
+ */
+ /*bit 1:Basic infrastructure mode*/
+ if (wiphy->interface_modes & BIT(NL80211_IFTYPE_STATION))
+ feature |= WIFI_FEATURE_INFRA;
+
+ /*bit 2:Support for 5 GHz Band*/
+ if (rwnx_hw->band_5g_support)
+ feature |= WIFI_FEATURE_INFRA_5G;
+
+ /*bit3:HOTSPOT is a supplicant feature, enable it by default*/
+ feature |= WIFI_FEATURE_HOTSPOT;
+
+ /*bit 4:P2P*/
+ if ((wiphy->interface_modes & BIT(NL80211_IFTYPE_P2P_CLIENT)) &&
+ (wiphy->interface_modes & BIT(NL80211_IFTYPE_P2P_GO)))
+ feature |= WIFI_FEATURE_P2P;
+
+ /*bit 5:soft AP feature supported*/
+ if (wiphy->interface_modes & BIT(NL80211_IFTYPE_AP))
+ feature |= WIFI_FEATURE_SOFT_AP;
+
+ /*bit 18:WiFi Logger*/
+ feature |= WIFI_FEATURE_LOGGER;
+
+ /*bit 21:WiFi mkeep_alive*/
+ feature |= WIFI_FEATURE_MKEEP_ALIVE;
+
+ if (nla_put_u32(reply, ANDR_WIFI_ATTRIBUTE_NUM_FEATURE_SET, feature)) {
+ wiphy_err(wiphy, "%s put u32 error\n", __func__);
+ goto out_put_fail;
+ }
+
+ ret = cfg80211_vendor_cmd_reply(reply);
+ if (ret)
+ wiphy_err(wiphy, "%s reply cmd error\n", __func__);
+
+ return ret;
+
+out_put_fail:
+ kfree_skb(reply);
+ return -EMSGSIZE;
+}
+
+static int aicwf_vendor_logger_get_feature(struct wiphy *wiphy, struct wireless_dev *wdev,
+ const void *data, int len)
+{
+ int ret;
+ struct sk_buff *reply;
+ uint32_t feature = 0, payload;
+
+ payload = sizeof(feature);
+ reply = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, payload);
+
+ if (!reply)
+ return -ENOMEM;
+
+ feature |= WIFI_LOGGER_MEMORY_DUMP_SUPPORTED;
+ feature |= WIFI_LOGGER_CONNECT_EVENT_SUPPORTED;
+
+ /*vts will test wake reason state function*/
+ feature |= WIFI_LOGGER_WAKE_LOCK_SUPPORTED;
+
+ if (nla_put_u32(reply, ANDR_WIFI_ATTRIBUTE_NUM_FEATURE_SET, feature)) {
+ wiphy_err(wiphy, "put skb u32 failed\n");
+ goto out_put_fail;
+ }
+
+ ret = cfg80211_vendor_cmd_reply(reply);
+ if (ret)
+ wiphy_err(wiphy, "reply cmd error\n");
+
+ return ret;
+
+out_put_fail:
+ kfree_skb(reply);
+ return -EMSGSIZE;
+}
+
+static int aicwf_vendor_logger_get_ring_status(struct wiphy *wiphy, struct wireless_dev *wdev,
+ const void *data, int len)
+{
+ int ret;
+ struct sk_buff *reply;
+ uint32_t payload;
+ uint32_t ring_buffer_nums = sizeof(ring_buffer) / sizeof(ring_buffer[0]);
+
+ payload = sizeof(ring_buffer_nums) + sizeof(ring_buffer);
+ reply = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, payload);
+
+ if (!reply)
+ return -ENOMEM;
+
+ if (nla_put_u32(reply, LOGGER_ATTRIBUTE_RING_NUM, ring_buffer_nums)) {
+ wiphy_err(wiphy, "put skb u32 failed\n");
+ goto out_put_fail;
+ }
+
+ if (nla_put(reply, LOGGER_ATTRIBUTE_RING_STATUS, sizeof(ring_buffer), ring_buffer)) {
+ wiphy_err(wiphy, "put skb failed\n");
+ goto out_put_fail;
+ }
+
+ ret = cfg80211_vendor_cmd_reply(reply);
+ if (ret)
+ wiphy_err(wiphy, "reply cmd error\n");
+
+ return ret;
+
+out_put_fail:
+ kfree_skb(reply);
+ return -EMSGSIZE;
+}
+
+static int aicwf_vendor_logger_start_logging(struct wiphy *wiphy, struct wireless_dev *wdev,
+ const void *data, int len)
+{
+ int ret = 0, rem, type, intval, size, i;
+ const struct nlattr *iter;
+ struct wifi_ring_buffer_status rb;
+
+ nla_for_each_attr(iter, data, len, rem) {
+ type = nla_type(iter);
+ switch (type) {
+ case LOGGER_ATTRIBUTE_LOG_LEVEL:
+ rb.verbose_level = nla_get_u32(iter);
+ break;
+ case LOGGER_ATTRIBUTE_RING_FLAGS:
+ rb.flags = nla_get_u32(iter);
+ break;
+ case LOGGER_ATTRIBUTE_LOG_TIME_INTVAL:
+ intval = nla_get_u32(iter);
+ break;
+ case LOGGER_ATTRIBUTE_LOG_MIN_DATA_SIZE:
+ size = nla_get_u32(iter);
+ break;
+ case LOGGER_ATTRIBUTE_RING_NAME:
+ strcpy(rb.name, nla_data(iter));
+ break;
+ default:
+ AICWFDBG(LOGERROR, "%s(%d), Unknown type: %d\n", __func__, __LINE__, type);
+ return -EINVAL;
+ }
+ }
+
+ ret = -EINVAL;
+ for (i = 0; i < sizeof(ring_buffer) / sizeof(ring_buffer[0]); i++) {
+ if (strcmp(rb.name, ring_buffer[i].name) == 0) {
+ ret = 0;
+ break;
+ }
+ }
+
+ /* TODO
+ * Add handle in the future
+ */
+
+ return ret;
+}
+
+static int aicwf_vendor_logger_get_ring_data(struct wiphy *wiphy, struct wireless_dev *wdev,
+ const void *data, int len)
+{
+ int ret = 0, rem, type, i;
+ const struct nlattr *iter;
+ struct wifi_ring_buffer_status rb;
+
+ nla_for_each_attr(iter, data, len, rem) {
+ type = nla_type(iter);
+ switch (type) {
+ case LOGGER_ATTRIBUTE_RING_NAME:
+ strcpy(rb.name, nla_data(iter));
+ break;
+ default:
+ pr_err("%s(%d), Unknown type: %d\n", __func__, __LINE__, type);
+ return -EINVAL;
+ }
+ }
+
+ ret = -EINVAL;
+ for (i = 0; i < sizeof(ring_buffer) / sizeof(ring_buffer[0]); i++) {
+ if (strcmp(rb.name, ring_buffer[i].name) == 0) {
+ ret = 0;
+ break;
+ }
+ }
+
+ /* TODO
+ * Add handle in the future
+ */
+
+ return ret;
+}
+
+static int aicwf_vendor_logger_get_wake_reason_stats(struct wiphy *wiphy, struct wireless_dev *wdev,
+ const void *data, int len)
+{
+ int ret;
+ struct sk_buff *reply;
+ uint32_t payload;
+
+ payload = sizeof(wake_reason_cnt.total_cmd_event_wake);
+ reply = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, payload);
+
+ if (!reply)
+ return -ENOMEM;
+
+ /* TODO
+ * Add handle in the future
+ */
+ if (nla_put_u32(reply, WAKE_STAT_ATTRIBUTE_TOTAL_CMD_EVENT, wake_reason_cnt.total_cmd_event_wake))
+ goto out_put_fail;
+
+ ret = cfg80211_vendor_cmd_reply(reply);
+ if (ret)
+ wiphy_err(wiphy, "reply cmd error\n");
+
+ return ret;
+
+out_put_fail:
+ kfree_skb(reply);
+ return -EMSGSIZE;
+}
+
+static int aicwf_vendor_apf_subcmd_get_capabilities(struct wiphy *wiphy, struct wireless_dev *wdev,
+ const void *data, int len)
+{
+ /* TODO
+ * Add handle in the future
+ */
+ return 0;
+}
+
+static int aicwf_vendor_sub_cmd_set_mac(struct wiphy *wiphy, struct wireless_dev *wdev,
+ const void *data, int len)
+{
+ int ret = 0, rem, type;
+ const struct nlattr *iter;
+ u8 mac[ETH_ALEN];
+
+ printk("%s enter \r\n", __func__);
+
+ nla_for_each_attr(iter, data, len, rem) {
+ type = nla_type(iter);
+ switch (type) {
+ case WIFI_VENDOR_ATTR_DRIVER_MAC_ADDR:
+ memcpy(mac, nla_data(iter), ETH_ALEN);
+ printk("%s, %02X:%02X:%02X:%02X:%02X:%02X\n", __func__,
+ mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+ break;
+ default:
+ pr_err("%s(%d), Unknown type: %d\n", __func__, __LINE__, type);
+ return -EINVAL;
+ }
+ }
+
+ /* TODO
+ * Add handle in the future
+ */
+
+ return ret;
+}
+#endif
+
+static const struct nla_policy
+aicwf_cfg80211_mkeep_alive_policy[MKEEP_ALIVE_ATTRIBUTE_MAX+1] = {
+ [0] = {.type = NLA_UNSPEC },
+ [MKEEP_ALIVE_ATTRIBUTE_ID] = { .type = NLA_U8 },
+ [MKEEP_ALIVE_ATTRIBUTE_IP_PKT] = { .type = NLA_MSECS },
+ [MKEEP_ALIVE_ATTRIBUTE_IP_PKT_LEN] = { .type = NLA_U16 },
+ [MKEEP_ALIVE_ATTRIBUTE_SRC_MAC_ADDR] = { .type = NLA_MSECS,
+ .len = ETH_ALEN },
+ [MKEEP_ALIVE_ATTRIBUTE_DST_MAC_ADDR] = { .type = NLA_MSECS,
+ .len = ETH_ALEN },
+ [MKEEP_ALIVE_ATTRIBUTE_PERIOD_MSEC] = { .type = NLA_U32 },
+};
+
+static const struct nla_policy
+aicwf_cfg80211_logger_policy[LOGGER_ATTRIBUTE_MAX + 1] = {
+ [0] = {.type = NLA_UNSPEC },
+ [LOGGER_ATTRIBUTE_DRIVER_VER] = { .type = NLA_BINARY },
+ [LOGGER_ATTRIBUTE_FW_VER] = { .type = NLA_BINARY },
+ [LOGGER_ATTRIBUTE_LOG_LEVEL] = { .type = NLA_U32 },
+ [LOGGER_ATTRIBUTE_RING_FLAGS] = { .type = NLA_U32 },
+ [LOGGER_ATTRIBUTE_LOG_TIME_INTVAL] = { .type = NLA_U32 },
+ [LOGGER_ATTRIBUTE_LOG_MIN_DATA_SIZE] = { .type = NLA_U32 },
+ [LOGGER_ATTRIBUTE_RING_NAME] = { .type = NLA_STRING },
+};
+
+static const struct nla_policy
+aicwf_cfg80211_subcmd_policy[GSCAN_ATTRIBUTE_MAX + 1] = {
+ [0] = {.type = NLA_UNSPEC },
+ [GSCAN_ATTRIBUTE_BAND] = { .type = NLA_U32 },
+};
+
+static const struct nla_policy
+aicwf_cfg80211_andr_wifi_policy[ANDR_WIFI_ATTRIBUTE_MAX + 1] = {
+ [0] = {.type = NLA_UNSPEC },
+ [ANDR_WIFI_ATTRIBUTE_COUNTRY] = { .type = NLA_STRING },
+};
+
+static const struct nla_policy
+aicwf_cfg80211_subcmd_set_mac_policy[WIFI_VENDOR_ATTR_DRIVER_MAX + 1] = {
+ [0] = {.type = NLA_UNSPEC },
+ [WIFI_VENDOR_ATTR_DRIVER_MAC_ADDR] = { .type = NLA_MSECS, .len = ETH_ALEN },
+};
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
+static int aicwf_dump_interface(struct wiphy *wiphy,
+ struct wireless_dev *wdev, struct sk_buff *skb,
+ const void *data, int data_len,
+ unsigned long *storage)
+{
+ return 0;
+}
+
+const struct wiphy_vendor_command aicwf_vendor_cmd[] = {
+ {
+ {
+ .vendor_id = GOOGLE_OUI,
+ .subcmd = WIFI_OFFLOAD_SUBCMD_START_MKEEP_ALIVE
+ },
+ .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
+ .doit = aicwf_vendor_start_mkeep_alive,
+ .dumpit = aicwf_dump_interface,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 3, 0)
+ .policy = aicwf_cfg80211_mkeep_alive_policy,
+ .maxattr = MKEEP_ALIVE_ATTRIBUTE_MAX
+#endif
+ },
+ {
+ {
+ .vendor_id = GOOGLE_OUI,
+ .subcmd = WIFI_OFFLOAD_SUBCMD_STOP_MKEEP_ALIVE
+ },
+ .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
+ .doit = aicwf_vendor_stop_mkeep_alive,
+ .dumpit = aicwf_dump_interface,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 3, 0)
+ .policy = aicwf_cfg80211_mkeep_alive_policy,
+ .maxattr = MKEEP_ALIVE_ATTRIBUTE_MAX
+#endif
+ },
+ {
+ {
+ .vendor_id = GOOGLE_OUI,
+ .subcmd = LOGGER_GET_VER
+ },
+ .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
+ .doit = aicwf_vendor_get_ver,
+ .dumpit = aicwf_dump_interface,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 3, 0)
+ .policy = aicwf_cfg80211_logger_policy,
+ .maxattr = LOGGER_ATTRIBUTE_MAX
+#endif
+ },
+ {
+ {
+ .vendor_id = GOOGLE_OUI,
+ .subcmd = GSCAN_SUBCMD_GET_CHANNEL_LIST
+ },
+ .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
+ .doit = aicwf_vendor_subcmd_get_channel_list,
+ .dumpit = aicwf_dump_interface,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 3, 0)
+ .policy = aicwf_cfg80211_subcmd_policy,
+ .maxattr = GSCAN_ATTRIBUTE_MAX
+#endif
+ },
+ {
+ {
+ .vendor_id = GOOGLE_OUI,
+ .subcmd = WIFI_SUBCMD_SET_COUNTRY_CODE
+ },
+ .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
+ .doit = aicwf_vendor_subcmd_set_country_code,
+ .dumpit = aicwf_dump_interface,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 3, 0)
+ .policy = aicwf_cfg80211_andr_wifi_policy,
+ .maxattr = ANDR_WIFI_ATTRIBUTE_MAX
+#endif
+ },
+ {
+ {
+ .vendor_id = GOOGLE_OUI,
+ .subcmd = LOGGER_TRIGGER_MEM_DUMP
+ },
+ .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
+ .doit = aicwf_vendor_logger_trigger_memory_dump,
+ .dumpit = aicwf_dump_interface,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 3, 0)
+ .policy = VENDOR_CMD_RAW_DATA,
+#endif
+ },
+ {
+ {
+ .vendor_id = GOOGLE_OUI,
+ .subcmd = WIFI_SUBCMD_GET_FEATURE_SET
+ },
+ .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
+ .doit = aicwf_vendor_subcmd_get_feature_set,
+ .dumpit = aicwf_dump_interface,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 3, 0)
+ .policy = VENDOR_CMD_RAW_DATA,
+#endif
+ },
+ {
+ {
+ .vendor_id = GOOGLE_OUI,
+ .subcmd = LOGGER_GET_FEATURE
+ },
+ .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
+ .doit = aicwf_vendor_logger_get_feature,
+ .dumpit = aicwf_dump_interface,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 3, 0)
+ .policy = VENDOR_CMD_RAW_DATA,
+#endif
+ },
+ {
+ {
+ .vendor_id = GOOGLE_OUI,
+ .subcmd = LOGGER_GET_RING_STATUS
+ },
+ .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
+ .doit = aicwf_vendor_logger_get_ring_status,
+ .dumpit = aicwf_dump_interface,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 3, 0)
+ .policy = VENDOR_CMD_RAW_DATA,
+#endif
+ },
+ {
+ {
+ .vendor_id = GOOGLE_OUI,
+ .subcmd = LOGGER_START_LOGGING
+ },
+ .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
+ .doit = aicwf_vendor_logger_start_logging,
+ .dumpit = aicwf_dump_interface,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 3, 0)
+ .policy = aicwf_cfg80211_logger_policy,
+ .maxattr = LOGGER_ATTRIBUTE_MAX
+#endif
+ },
+ {
+ {
+ .vendor_id = GOOGLE_OUI,
+ .subcmd = LOGGER_GET_RING_DATA
+ },
+ .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
+ .doit = aicwf_vendor_logger_get_ring_data,
+ .dumpit = aicwf_dump_interface,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 3, 0)
+ .policy = aicwf_cfg80211_logger_policy,
+ .maxattr = LOGGER_ATTRIBUTE_MAX
+#endif
+ },
+ {
+ {
+ .vendor_id = GOOGLE_OUI,
+ .subcmd = LOGGER_GET_WAKE_REASON_STATS
+ },
+ .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
+ .doit = aicwf_vendor_logger_get_wake_reason_stats,
+ .dumpit = aicwf_dump_interface,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 3, 0)
+ .policy = VENDOR_CMD_RAW_DATA,
+#endif
+ },
+ {
+ {
+ .vendor_id = GOOGLE_OUI,
+ .subcmd = APF_SUBCMD_GET_CAPABILITIES
+ },
+ .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
+ .doit = aicwf_vendor_apf_subcmd_get_capabilities,
+ .dumpit = aicwf_dump_interface,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 3, 0)
+ .policy = VENDOR_CMD_RAW_DATA,
+#endif
+ },
+ {
+ {
+ .vendor_id = GOOGLE_OUI,
+ .subcmd = VENDOR_NL80211_SUBCMD_SET_MAC
+ },
+ .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_RUNNING,
+ .doit = aicwf_vendor_sub_cmd_set_mac,
+ .dumpit = aicwf_dump_interface,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 3, 0)
+ .policy = aicwf_cfg80211_subcmd_set_mac_policy,
+ .maxattr = WIFI_VENDOR_ATTR_DRIVER_MAX,
+#endif
+ },
+ {
+ {
+ .vendor_id = BRCM_OUI,
+ .subcmd = VENDOR_NL80211_SUBCMD_SET_MAC
+ },
+ .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_RUNNING,
+ .doit = aicwf_vendor_sub_cmd_set_mac,
+ .dumpit = aicwf_dump_interface,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 3, 0)
+ .policy = aicwf_cfg80211_subcmd_set_mac_policy,
+ .maxattr = WIFI_VENDOR_ATTR_DRIVER_MAX,
+#endif
+ },
+};
+#endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
+static const struct nl80211_vendor_cmd_info aicwf_vendor_events[] = {
+};
+#endif
+
+int aicwf_vendor_init(struct wiphy *wiphy)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
+ wiphy->vendor_commands = aicwf_vendor_cmd;
+ wiphy->n_vendor_commands = ARRAY_SIZE(aicwf_vendor_cmd);
+ wiphy->vendor_events = aicwf_vendor_events;
+ wiphy->n_vendor_events = ARRAY_SIZE(aicwf_vendor_events);
+#endif
+ return 0;
+}
diff --git a/drivers/net/wireless/aic8800/aic8800_fdrv/aic_vendor.h b/drivers/net/wireless/aic8800/aic8800_fdrv/aic_vendor.h
new file mode 100644
index 000000000000..0e1e3e0c7708
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic8800_fdrv/aic_vendor.h
@@ -0,0 +1,346 @@
+#ifndef _AIC_VENDOR_H
+#define _AIC_VENDOR_H
+
+#include <linux/types.h>
+
+#define GOOGLE_OUI 0x001A11
+#define BRCM_OUI 0x001018
+
+typedef enum {
+ START_MKEEP_ALIVE,
+ STOP_MKEEP_ALIVE,
+} GetCmdType;
+
+typedef enum {
+ /* don't use 0 as a valid subcommand */
+ VENDOR_NL80211_SUBCMD_UNSPECIFIED,
+
+ /* define all vendor startup commands between 0x0 and 0x0FFF */
+ VENDOR_NL80211_SUBCMD_RANGE_START = 0x0001,
+ VENDOR_NL80211_SUBCMD_RANGE_END = 0x0FFF,
+
+ /* define all GScan related commands between 0x1000 and 0x10FF */
+ ANDROID_NL80211_SUBCMD_GSCAN_RANGE_START = 0x1000,
+ ANDROID_NL80211_SUBCMD_GSCAN_RANGE_END = 0x10FF,
+
+ /* define all NearbyDiscovery related commands between 0x1100 and 0x11FF */
+ ANDROID_NL80211_SUBCMD_NBD_RANGE_START = 0x1100,
+ ANDROID_NL80211_SUBCMD_NBD_RANGE_END = 0x11FF,
+
+ /* define all RTT related commands between 0x1100 and 0x11FF */
+ ANDROID_NL80211_SUBCMD_RTT_RANGE_START = 0x1100,
+ ANDROID_NL80211_SUBCMD_RTT_RANGE_END = 0x11FF,
+
+ ANDROID_NL80211_SUBCMD_LSTATS_RANGE_START = 0x1200,
+ ANDROID_NL80211_SUBCMD_LSTATS_RANGE_END = 0x12FF,
+
+ /* define all Logger related commands between 0x1400 and 0x14FF */
+ ANDROID_NL80211_SUBCMD_DEBUG_RANGE_START = 0x1400,
+ ANDROID_NL80211_SUBCMD_DEBUG_RANGE_END = 0x14FF,
+
+ /* define all wifi offload related commands between 0x1600 and 0x16FF */
+ ANDROID_NL80211_SUBCMD_WIFI_OFFLOAD_RANGE_START = 0x1600,
+ ANDROID_NL80211_SUBCMD_WIFI_OFFLOAD_RANGE_END = 0x16FF,
+
+ /* define all NAN related commands between 0x1700 and 0x17FF */
+ ANDROID_NL80211_SUBCMD_NAN_RANGE_START = 0x1700,
+ ANDROID_NL80211_SUBCMD_NAN_RANGE_END = 0x17FF,
+
+ /* define all Android Packet Filter related commands between 0x1800 and 0x18FF */
+ ANDROID_NL80211_SUBCMD_PKT_FILTER_RANGE_START = 0x1800,
+ ANDROID_NL80211_SUBCMD_PKT_FILTER_RANGE_END = 0x18FF,
+
+ /* This is reserved for future usage */
+
+} ANDROID_VENDOR_SUB_COMMAND;
+
+typedef enum {
+ WIFI_OFFLOAD_SUBCMD_START_MKEEP_ALIVE = ANDROID_NL80211_SUBCMD_WIFI_OFFLOAD_RANGE_START,
+ WIFI_OFFLOAD_SUBCMD_STOP_MKEEP_ALIVE,
+} WIFI_OFFLOAD_SUB_COMMAND;
+
+
+enum mkeep_alive_attributes {
+ MKEEP_ALIVE_ATTRIBUTE_ID = 0x1,
+ MKEEP_ALIVE_ATTRIBUTE_IP_PKT,
+ MKEEP_ALIVE_ATTRIBUTE_IP_PKT_LEN,
+ MKEEP_ALIVE_ATTRIBUTE_SRC_MAC_ADDR,
+ MKEEP_ALIVE_ATTRIBUTE_DST_MAC_ADDR,
+ MKEEP_ALIVE_ATTRIBUTE_PERIOD_MSEC,
+ MKEEP_ALIVE_ATTRIBUTE_AFTER_LAST,
+ MKEEP_ALIVE_ATTRIBUTE_MAX = MKEEP_ALIVE_ATTRIBUTE_AFTER_LAST - 1
+};
+
+enum debug_sub_command {
+ LOGGER_START_LOGGING = ANDROID_NL80211_SUBCMD_DEBUG_RANGE_START,
+ LOGGER_TRIGGER_MEM_DUMP,
+ LOGGER_GET_MEM_DUMP,
+ LOGGER_GET_VER,
+ LOGGER_GET_RING_STATUS,
+ LOGGER_GET_RING_DATA,
+ LOGGER_GET_FEATURE,
+ LOGGER_RESET_LOGGING,
+ LOGGER_TRIGGER_DRIVER_MEM_DUMP,
+ LOGGER_GET_DRIVER_MEM_DUMP,
+ LOGGER_START_PKT_FATE_MONITORING,
+ LOGGER_GET_TX_PKT_FATES,
+ LOGGER_GET_RX_PKT_FATES,
+ LOGGER_GET_WAKE_REASON_STATS,
+ LOGGER_DEBUG_GET_DUMP,
+ LOGGER_FILE_DUMP_DONE_IND,
+ LOGGER_SET_HAL_START,
+ LOGGER_HAL_STOP,
+ LOGGER_SET_HAL_PID,
+};
+
+enum logger_attributes {
+ LOGGER_ATTRIBUTE_INVALID = 0,
+ LOGGER_ATTRIBUTE_DRIVER_VER,
+ LOGGER_ATTRIBUTE_FW_VER,
+ LOGGER_ATTRIBUTE_RING_ID,
+ LOGGER_ATTRIBUTE_RING_NAME,
+ LOGGER_ATTRIBUTE_RING_FLAGS,
+ LOGGER_ATTRIBUTE_LOG_LEVEL,
+ LOGGER_ATTRIBUTE_LOG_TIME_INTVAL,
+ LOGGER_ATTRIBUTE_LOG_MIN_DATA_SIZE,
+ LOGGER_ATTRIBUTE_FW_DUMP_LEN,
+ LOGGER_ATTRIBUTE_FW_DUMP_DATA,
+ // LOGGER_ATTRIBUTE_FW_ERR_CODE,
+ LOGGER_ATTRIBUTE_RING_DATA,
+ LOGGER_ATTRIBUTE_RING_STATUS,
+ LOGGER_ATTRIBUTE_RING_NUM,
+ LOGGER_ATTRIBUTE_DRIVER_DUMP_LEN,
+ LOGGER_ATTRIBUTE_DRIVER_DUMP_DATA,
+ LOGGER_ATTRIBUTE_PKT_FATE_NUM,
+ LOGGER_ATTRIBUTE_PKT_FATE_DATA,
+ LOGGER_ATTRIBUTE_AFTER_LAST,
+ LOGGER_ATTRIBUTE_MAX = LOGGER_ATTRIBUTE_AFTER_LAST - 1,
+};
+
+enum wifi_sub_command {
+ GSCAN_SUBCMD_GET_CAPABILITIES = ANDROID_NL80211_SUBCMD_GSCAN_RANGE_START,
+ GSCAN_SUBCMD_SET_CONFIG, /* 0x1001 */
+ GSCAN_SUBCMD_SET_SCAN_CONFIG, /* 0x1002 */
+ GSCAN_SUBCMD_ENABLE_GSCAN, /* 0x1003 */
+ GSCAN_SUBCMD_GET_SCAN_RESULTS, /* 0x1004 */
+ GSCAN_SUBCMD_SCAN_RESULTS, /* 0x1005 */
+ GSCAN_SUBCMD_SET_HOTLIST, /* 0x1006 */
+ GSCAN_SUBCMD_SET_SIGNIFICANT_CHANGE_CONFIG, /* 0x1007 */
+ GSCAN_SUBCMD_ENABLE_FULL_SCAN_RESULTS, /* 0x1008 */
+ GSCAN_SUBCMD_GET_CHANNEL_LIST, /* 0x1009 */
+ WIFI_SUBCMD_GET_FEATURE_SET, /* 0x100A */
+ WIFI_SUBCMD_GET_FEATURE_SET_MATRIX, /* 0x100B */
+ WIFI_SUBCMD_SET_PNO_RANDOM_MAC_OUI, /* 0x100C */
+ WIFI_SUBCMD_NODFS_SET, /* 0x100D */
+ WIFI_SUBCMD_SET_COUNTRY_CODE, /* 0x100E */
+ /* Add more sub commands here */
+ GSCAN_SUBCMD_SET_EPNO_SSID, /* 0x100F */
+ WIFI_SUBCMD_SET_SSID_WHITE_LIST, /* 0x1010 */
+ WIFI_SUBCMD_SET_ROAM_PARAMS, /* 0x1011 */
+ WIFI_SUBCMD_ENABLE_LAZY_ROAM, /* 0x1012 */
+ WIFI_SUBCMD_SET_BSSID_PREF, /* 0x1013 */
+ WIFI_SUBCMD_SET_BSSID_BLACKLIST, /* 0x1014 */
+ GSCAN_SUBCMD_ANQPO_CONFIG, /* 0x1015 */
+ WIFI_SUBCMD_SET_RSSI_MONITOR, /* 0x1016 */
+ WIFI_SUBCMD_CONFIG_ND_OFFLOAD, /* 0x1017 */
+ /* Add more sub commands here */
+ GSCAN_SUBCMD_MAX,
+ APF_SUBCMD_GET_CAPABILITIES = ANDROID_NL80211_SUBCMD_PKT_FILTER_RANGE_START,
+ APF_SUBCMD_SET_FILTER,
+};
+
+enum gscan_attributes {
+ GSCAN_ATTRIBUTE_NUM_BUCKETS = 10,
+ GSCAN_ATTRIBUTE_BASE_PERIOD,
+ GSCAN_ATTRIBUTE_BUCKETS_BAND,
+ GSCAN_ATTRIBUTE_BUCKET_ID,
+ GSCAN_ATTRIBUTE_BUCKET_PERIOD,
+ GSCAN_ATTRIBUTE_BUCKET_NUM_CHANNELS,
+ GSCAN_ATTRIBUTE_BUCKET_CHANNELS,
+ GSCAN_ATTRIBUTE_NUM_AP_PER_SCAN,
+ GSCAN_ATTRIBUTE_REPORT_THRESHOLD,
+ GSCAN_ATTRIBUTE_NUM_SCANS_TO_CACHE,
+ GSCAN_ATTRIBUTE_BAND = GSCAN_ATTRIBUTE_BUCKETS_BAND,
+
+ GSCAN_ATTRIBUTE_ENABLE_FEATURE = 20,
+ GSCAN_ATTRIBUTE_SCAN_RESULTS_COMPLETE, /* indicates no more results */
+ GSCAN_ATTRIBUTE_FLUSH_FEATURE, /* Flush all the configs */
+ GSCAN_ENABLE_FULL_SCAN_RESULTS,
+ GSCAN_ATTRIBUTE_REPORT_EVENTS,
+
+ /* remaining reserved for additional attributes */
+ GSCAN_ATTRIBUTE_NUM_OF_RESULTS = 30,
+ GSCAN_ATTRIBUTE_FLUSH_RESULTS,
+ GSCAN_ATTRIBUTE_SCAN_RESULTS, /* flat array of wifi_scan_result */
+ GSCAN_ATTRIBUTE_SCAN_ID, /* indicates scan number */
+ GSCAN_ATTRIBUTE_SCAN_FLAGS, /* indicates if scan was aborted */
+ GSCAN_ATTRIBUTE_AP_FLAGS, /* flags on significant change event */
+ GSCAN_ATTRIBUTE_NUM_CHANNELS,
+ GSCAN_ATTRIBUTE_CHANNEL_LIST,
+ GSCAN_ATTRIBUTE_CH_BUCKET_BITMASK,
+
+ GSCAN_ATTRIBUTE_AFTER_LAST,
+ GSCAN_ATTRIBUTE_MAX = GSCAN_ATTRIBUTE_AFTER_LAST - 1,
+};
+
+enum andr_wifi_attributes {
+ ANDR_WIFI_ATTRIBUTE_NUM_FEATURE_SET,
+ ANDR_WIFI_ATTRIBUTE_FEATURE_SET,
+ ANDR_WIFI_ATTRIBUTE_PNO_RANDOM_MAC_OUI,
+ ANDR_WIFI_ATTRIBUTE_NODFS_SET,
+ ANDR_WIFI_ATTRIBUTE_COUNTRY,
+ ANDR_WIFI_ATTRIBUTE_ND_OFFLOAD_VALUE,
+ // Add more attribute here
+ ANDR_WIFI_ATTRIBUTE_AFTER_LAST,
+ ANDR_WIFI_ATTRIBUTE_MAX = ANDR_WIFI_ATTRIBUTE_AFTER_LAST - 1,
+};
+
+enum wifi_support_feature {
+ /* Feature enums */
+ WIFI_FEATURE_INFRA = 0x0001, /* Basic infrastructure mode */
+ WIFI_FEATURE_INFRA_5G = 0x0002, /* Support for 5, GHz Band */
+ WIFI_FEATURE_HOTSPOT = 0x0004, /* Support for GAS/ANQP */
+ WIFI_FEATURE_P2P = 0x0008, /* Wifi-Direct */
+ WIFI_FEATURE_SOFT_AP = 0x0010, /* Soft AP */
+ WIFI_FEATURE_GSCAN = 0x0020, /* Google-Scan APIs */
+ WIFI_FEATURE_NAN = 0x0040, /* Neighbor Awareness Networking */
+ WIFI_FEATURE_D2D_RTT = 0x0080, /* Device-to-device RTT */
+ WIFI_FEATURE_D2AP_RTT = 0x0100, /* Device-to-AP RTT */
+ WIFI_FEATURE_BATCH_SCAN = 0x0200, /* Batched Scan (legacy) */
+ WIFI_FEATURE_PNO = 0x0400, /* Preferred network offload */
+ WIFI_FEATURE_ADDITIONAL_STA = 0x0800, /* Support for two STAs */
+ WIFI_FEATURE_TDLS = 0x1000, /* Tunnel directed link setup */
+ WIFI_FEATURE_TDLS_OFFCHANNEL = 0x2000, /* Support for TDLS off channel */
+ WIFI_FEATURE_EPR = 0x4000, /* Enhanced power reporting */
+ WIFI_FEATURE_AP_STA = 0x8000, /* Support for AP STA Concurrency */
+ WIFI_FEATURE_LINK_LAYER_STATS = 0x10000, /* Support for Linkstats */
+ WIFI_FEATURE_LOGGER = 0x20000, /* WiFi Logger */
+ WIFI_FEATURE_HAL_EPNO = 0x40000, /* WiFi PNO enhanced */
+ WIFI_FEATURE_RSSI_MONITOR = 0x80000, /* RSSI Monitor */
+ WIFI_FEATURE_MKEEP_ALIVE = 0x100000, /* WiFi mkeep_alive */
+ WIFI_FEATURE_CONFIG_NDO = 0x200000, /* ND offload configure */
+ WIFI_FEATURE_TX_TRANSMIT_POWER = 0x400000, /* Capture Tx transmit power levels */
+ WIFI_FEATURE_CONTROL_ROAMING = 0x800000, /* Enable/Disable firmware roaming */
+ WIFI_FEATURE_IE_WHITELIST = 0x1000000, /* Support Probe IE white listing */
+ WIFI_FEATURE_SCAN_RAND = 0x2000000, /* Support MAC & Probe Sequence Number randomization */
+ WIFI_FEATURE_INVALID = 0xFFFFFFFF, /* Invalid Feature */
+};
+
+enum wifi_logger_feature {
+ WIFI_LOGGER_MEMORY_DUMP_SUPPORTED = (1 << (0)), // Memory dump of FW
+ WIFI_LOGGER_PER_PACKET_TX_RX_STATUS_SUPPORTED = (1 << (1)), // PKT status
+ WIFI_LOGGER_CONNECT_EVENT_SUPPORTED = (1 << (2)), // Connectivity event
+ WIFI_LOGGER_POWER_EVENT_SUPPORTED = (1 << (3)), // POWER of Driver
+ WIFI_LOGGER_WAKE_LOCK_SUPPORTED = (1 << (4)), // WAKE LOCK of Driver
+ WIFI_LOGGER_VERBOSE_SUPPORTED = (1 << (5)), // verbose log of FW
+ WIFI_LOGGER_WATCHDOG_TIMER_SUPPORTED = (1 << (6)), // monitor the health of FW
+ WIFI_LOGGER_DRIVER_DUMP_SUPPORTED = (1 << (7)), // dumps driver state
+ WIFI_LOGGER_PACKET_FATE_SUPPORTED = (1 << (8)), // tracks connection packets' fate
+};
+
+enum wake_stats_attributes {
+ WAKE_STAT_ATTRIBUTE_TOTAL_CMD_EVENT,
+ WAKE_STAT_ATTRIBUTE_CMD_EVENT_WAKE,
+ WAKE_STAT_ATTRIBUTE_CMD_EVENT_COUNT,
+ WAKE_STAT_ATTRIBUTE_CMD_COUNT_USED,
+ WAKE_STAT_ATTRIBUTE_TOTAL_DRIVER_FW,
+ WAKE_STAT_ATTRIBUTE_DRIVER_FW_WAKE,
+ WAKE_STAT_ATTRIBUTE_DRIVER_FW_COUNT,
+ WAKE_STAT_ATTRIBUTE_DRIVER_FW_COUNT_USED,
+ WAKE_STAT_ATTRIBUTE_TOTAL_RX_DATA_WAKE,
+ WAKE_STAT_ATTRIBUTE_RX_UNICAST_COUNT,
+ WAKE_STAT_ATTRIBUTE_RX_MULTICAST_COUNT,
+ WAKE_STAT_ATTRIBUTE_RX_BROADCAST_COUNT,
+ WAKE_STAT_ATTRIBUTE_RX_ICMP_PKT,
+ WAKE_STAT_ATTRIBUTE_RX_ICMP6_PKT,
+ WAKE_STAT_ATTRIBUTE_RX_ICMP6_RA,
+ WAKE_STAT_ATTRIBUTE_RX_ICMP6_NA,
+ WAKE_STAT_ATTRIBUTE_RX_ICMP6_NS,
+ WAKE_STAT_ATTRIBUTE_IPV4_RX_MULTICAST_ADD_CNT,
+ WAKE_STAT_ATTRIBUTE_IPV6_RX_MULTICAST_ADD_CNT,
+ WAKE_STAT_ATTRIBUTE_OTHER__RX_MULTICAST_ADD_CNT,
+ WAKE_STAT_ATTRIBUTE_RX_MULTICAST_PKT_INFO,
+ WAKE_STAT_ATTRIBUTE_AFTER_LAST,
+ WAKE_STAT_ATTRIBUTE_MAX = WAKE_STAT_ATTRIBUTE_AFTER_LAST - 1,
+};
+
+enum vendor_nl80211_subcmd {
+ /* copied from wpa_supplicant brcm definations */
+ VENDOR_NL80211_SUBCMD_UNSPEC = 0,
+ VENDOR_NL80211_SUBCMD_SET_PMK = 4,
+ VENDOR_NL80211_SUBCMD_SET_MAC = 6,
+ VENDOR_NL80211_SCMD_ACS = 9,
+ VENDOR_NL80211_SCMD_MAX = 10,
+};
+
+enum nl80211_vendor_subcmd_attributes {
+ WIFI_VENDOR_ATTR_DRIVER_CMD = 0,
+ WIFI_VENDOR_ATTR_DRIVER_KEY_PMK = 1,
+ WIFI_VENDOR_ATTR_DRIVER_MAC_ADDR = 3,
+ WIFI_VENDOR_ATTR_DRIVER_AFTER_LAST = 5,
+ WIFI_VENDOR_ATTR_DRIVER_MAX =
+ WIFI_VENDOR_ATTR_DRIVER_AFTER_LAST - 1,
+};
+
+typedef int wifi_ring_buffer_id;
+
+struct wifi_ring_buffer_status {
+ u8 name[32];
+ u32 flags;
+ wifi_ring_buffer_id ring_id;
+ u32 ring_buffer_byte_size;
+ u32 verbose_level;
+ u32 written_bytes;
+ u32 read_bytes;
+ u32 written_records;
+};
+
+struct rx_data_cnt_details_t {
+ int rx_unicast_cnt; /*Total rx unicast packet which woke up host */
+ int rx_multicast_cnt; /*Total rx multicast packet which woke up host */
+ int rx_broadcast_cnt; /*Total rx broadcast packet which woke up host */
+};
+
+struct rx_wake_pkt_type_classification_t {
+ int icmp_pkt; /*wake icmp packet count */
+ int icmp6_pkt; /*wake icmp6 packet count */
+ int icmp6_ra; /*wake icmp6 RA packet count */
+ int icmp6_na; /*wake icmp6 NA packet count */
+ int icmp6_ns; /*wake icmp6 NS packet count */
+ //ToDo: Any more interesting classification to add?
+};
+
+struct rx_multicast_cnt_t{
+ int ipv4_rx_multicast_addr_cnt; /*Rx wake packet was ipv4 multicast */
+ int ipv6_rx_multicast_addr_cnt; /*Rx wake packet was ipv6 multicast */
+ int other_rx_multicast_addr_cnt;/*Rx wake packet was non-ipv4 and non-ipv6*/
+};
+
+struct wlan_driver_wake_reason_cnt_t {
+ int total_cmd_event_wake; /* Total count of cmd event wakes */
+ int *cmd_event_wake_cnt; /* Individual wake count array, each index a reason */
+ int cmd_event_wake_cnt_sz; /* Max number of cmd event wake reasons */
+ int cmd_event_wake_cnt_used; /* Number of cmd event wake reasons specific to the driver */
+
+ int total_driver_fw_local_wake; /* Total count of drive/fw wakes, for local reasons */
+ int *driver_fw_local_wake_cnt; /* Individual wake count array, each index a reason */
+ int driver_fw_local_wake_cnt_sz; /* Max number of local driver/fw wake reasons */
+ int driver_fw_local_wake_cnt_used; /* Number of local driver/fw wake reasons specific to the driver */
+
+ int total_rx_data_wake; /* total data rx packets, that woke up host */
+ struct rx_data_cnt_details_t rx_wake_details;
+ struct rx_wake_pkt_type_classification_t rx_wake_pkt_classification_info;
+ struct rx_multicast_cnt_t rx_multicast_wake_pkt_info;
+};
+
+typedef struct wl_mkeep_alive_pkt {
+ u16 version; /* Version for mkeep_alive */
+ u16 length; /* length of fixed parameters in the structure */
+ u32 period_msec; /* high bit on means immediate send */
+ u16 len_bytes;
+ u8 keep_alive_id; /* 0 - 3 for N = 4 */
+ u8 data[1];
+} wl_mkeep_alive_pkt_t;
+
+#endif /* _AIC_VENDOR_H */
+
diff --git a/drivers/net/wireless/aic8800/aic8800_fdrv/aicwf_compat_8800d80.c b/drivers/net/wireless/aic8800/aic8800_fdrv/aicwf_compat_8800d80.c
new file mode 100644
index 000000000000..d44ad7ddf790
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic8800_fdrv/aicwf_compat_8800d80.c
@@ -0,0 +1,66 @@
+#include "rwnx_main.h"
+#include "rwnx_msg_tx.h"
+#include "reg_access.h"
+
+#define FW_USERCONFIG_NAME_8800D80 "aic_userconfig_8800d80.txt"
+
+extern char aic_fw_path[200];
+
+int rwnx_request_firmware_common(struct rwnx_hw *rwnx_hw,
+ u32** buffer, const char *filename);
+void rwnx_plat_userconfig_parsing(char *buffer, int size);
+void rwnx_release_firmware_common(u32** buffer);
+
+
+int aicwf_set_rf_config_8800d80(struct rwnx_hw *rwnx_hw, struct mm_set_rf_calib_cfm *cfm)
+{
+ int ret = 0;
+
+ if ((ret = rwnx_send_txpwr_lvl_v3_req(rwnx_hw))) {
+ return -1;
+ }
+ if ((ret = rwnx_send_txpwr_ofst_req(rwnx_hw))) {
+ return -1;
+ }
+ if (testmode == 0) {
+ if ((ret = rwnx_send_rf_calib_req(rwnx_hw, cfm))) {
+ return -1;
+ }
+ }
+ return 0 ;
+}
+
+
+int rwnx_plat_userconfig_load_8800d80(struct rwnx_hw *rwnx_hw){
+ int size;
+ u32 *dst=NULL;
+ char *filename = FW_USERCONFIG_NAME_8800D80;
+
+#ifndef ANDROID_PLATFORM
+ sprintf(aic_fw_path, "%s/%s", aic_fw_path, "aic8800D80");
+#endif
+
+ AICWFDBG(LOGINFO, "userconfig file path:%s \r\n", filename);
+
+ /* load file */
+ size = rwnx_request_firmware_common(rwnx_hw, &dst, filename);
+ if (size <= 0) {
+ AICWFDBG(LOGERROR, "wrong size of firmware file\n");
+ dst = NULL;
+ return 0;
+ }
+
+ /* Copy the file on the Embedded side */
+ AICWFDBG(LOGINFO, "### Load file done: %s, size=%d\n", filename, size);
+
+ rwnx_plat_userconfig_parsing((char *)dst, size);
+
+ rwnx_release_firmware_common(&dst);
+
+ AICWFDBG(LOGINFO, "userconfig download complete\n\n");
+ return 0;
+
+}
+
+
+
diff --git a/drivers/net/wireless/aic8800/aic8800_fdrv/aicwf_compat_8800d80.h b/drivers/net/wireless/aic8800/aic8800_fdrv/aicwf_compat_8800d80.h
new file mode 100644
index 000000000000..27565d802e0b
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic8800_fdrv/aicwf_compat_8800d80.h
@@ -0,0 +1,6 @@
+#include <linux/types.h>
+
+int rwnx_plat_userconfig_load_8800d80(struct rwnx_hw *rwnx_hw);
+int aicwf_set_rf_config_8800d80(struct rwnx_hw *rwnx_hw, struct mm_set_rf_calib_cfm *cfm);
+
+
diff --git a/drivers/net/wireless/aic8800/aic8800_fdrv/aicwf_compat_8800dc.c b/drivers/net/wireless/aic8800/aic8800_fdrv/aicwf_compat_8800dc.c
new file mode 100644
index 000000000000..b4b9eb4bc1fa
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic8800_fdrv/aicwf_compat_8800dc.c
@@ -0,0 +1,2329 @@
+#include "rwnx_main.h"
+#include "rwnx_msg_tx.h"
+#include "reg_access.h"
+#include "rwnx_platform.h"
+#include "aicwf_compat_8800dc.h"
+
+#define RWNX_MAC_FW_RF_BASE_NAME_8800DC "lmacfw_rf_8800dc.bin"
+
+#ifdef CONFIG_FOR_IPCAM
+#define RWNX_MAC_PATCH_BASE_NAME_8800DC "fmacfw_patch_8800dc_ipc"
+#else
+#define RWNX_MAC_PATCH_BASE_NAME_8800DC "fmacfw_patch_8800dc"
+#endif
+#define RWNX_MAC_PATCH_NAME2_8800DC RWNX_MAC_PATCH_BASE_NAME_8800DC".bin"
+#define RWNX_MAC_PATCH_NAME2_8800DC_U02 RWNX_MAC_PATCH_BASE_NAME_8800DC"_u02.bin"
+
+#define RWNX_MAC_CALIB_BASE_NAME_8800DC "fmacfw_calib_8800dc"
+#define RWNX_MAC_CALIB_NAME_8800DC_U02 RWNX_MAC_CALIB_BASE_NAME_8800DC"_u02.bin"
+
+#ifdef CONFIG_FOR_IPCAM
+#define RWNX_MAC_PATCH_TABLE_NAME_8800DC "fmacfw_patch_tbl_8800dc_ipc"
+#else
+#define RWNX_MAC_PATCH_TABLE_NAME_8800DC "fmacfw_patch_tbl_8800dc"
+#endif
+#define RWNX_MAC_PATCH_TABLE_8800DC RWNX_MAC_PATCH_TABLE_NAME_8800DC ".bin"
+#define RWNX_MAC_PATCH_TABLE_8800DC_U02 RWNX_MAC_PATCH_TABLE_NAME_8800DC "_u02.bin"
+
+
+#define RWNX_MAC_RF_PATCH_BASE_NAME_8800DC "fmacfw_rf_patch_8800dc"
+#define RWNX_MAC_RF_PATCH_NAME_8800DC RWNX_MAC_RF_PATCH_BASE_NAME_8800DC".bin"
+#define FW_USERCONFIG_NAME_8800DC "aic_userconfig_8800dc.txt"
+#define FW_USERCONFIG_NAME_8800DW "aic_userconfig_8800dw.txt"
+
+
+int rwnx_plat_bin_fw_upload_2(struct rwnx_hw *rwnx_hw, u32 fw_addr,
+ char *filename);
+int rwnx_request_firmware_common(struct rwnx_hw *rwnx_hw,
+ u32** buffer, const char *filename);
+void rwnx_plat_userconfig_parsing(char *buffer, int size);
+void rwnx_release_firmware_common(u32** buffer);
+
+typedef u32 (*array2_tbl_t)[2];
+
+u32 syscfg_tbl_masked_8800dc[][3] = {
+ //#ifdef CONFIG_PMIC_SETTING
+ #if defined(CONFIG_VRF_DCDC_MODE)
+ {0x7000216C, (0x3 << 2), (0x1 << 2)}, // pmic_pmu_init
+ {0x700021BC, (0x3 << 2), (0x1 << 2)},
+ {0x70002118, ((0x7 << 4) | (0x1 << 7)), ((0x2 << 4) | (0x1 << 7))},
+ {0x70002104, ((0x3F << 0) | (0x1 << 6)), ((0x2 << 0) | (0x1 << 6))},
+ {0x7000210C, ((0x3F << 0) | (0x1 << 6)), ((0x2 << 0) | (0x1 << 6))},
+ {0x70002170, (0xF << 0), (0x1 << 0)},
+ {0x70002190, (0x3F << 0), (24 << 0)},
+ {0x700021CC, ((0x7 << 4) | (0x1 << 7)), ((0x0 << 4) | (0x0 << 7))},
+ {0x700010A0, (0x1 << 11), (0x1 << 11)},
+ {0x70001034, ((0x1 << 20) | (0x7 << 26)), ((0x0 << 20) | (0x2 << 26))},
+ {0x70001038, (0x1 << 8), (0x1 << 8)},
+ {0x70001094, (0x3 << 2), (0x0 << 2)},
+ {0x700021D0, ((0x1 << 5) | (0x1 << 6)), ((0x1 << 5) | (0x1 << 6))},
+ {0x70001000, ((0x1 << 0) | (0x1 << 20) | (0x1 << 22)),
+ ((0x1 << 0) | (0x1 << 20) | (0x0 << 22))},
+ {0x70001028, (0xf << 2), (0x1 << 2)},
+ #else
+ {0x7000216C, (0x3 << 2), (0x1 << 2)}, // pmic_pmu_init
+ {0x700021BC, (0x3 << 2), (0x1 << 2)},
+ {0x70002118, ((0x7 << 4) | (0x1 << 7)), ((0x2 << 4) | (0x1 << 7))},
+ {0x70002104, ((0x3F << 0) | (0x1 << 6)), ((0x2 << 0) | (0x1 << 6))},
+ {0x7000210C, ((0x3F << 0) | (0x1 << 6)), ((0x2 << 0) | (0x1 << 6))},
+ {0x70002170, (0xF << 0), (0x1 << 0)},
+ {0x70002190, (0x3F << 0), (24 << 0)},
+ {0x700021CC, ((0x7 << 4) | (0x1 << 7)), ((0x0 << 4) | (0x0 << 7))},
+ {0x700010A0, (0x1 << 11), (0x1 << 11)},
+ {0x70001034, ((0x1 << 20) | (0x7 << 26)), ((0x0 << 20) | (0x2 << 26))},
+ {0x70001038, (0x1 << 8), (0x1 << 8)},
+ {0x70001094, (0x3 << 2), (0x0 << 2)},
+ {0x700021D0, ((0x1 << 5) | (0x1 << 6)), ((0x1 << 5) | (0x1 << 6))},
+ {0x70001000, ((0x1 << 0) | (0x1 << 20) | (0x1 << 22)),
+ ((0x0 << 0) | (0x1 << 20) | (0x0 << 22))},
+ #endif
+ //#endif /* CONFIG_PMIC_SETTING */
+ {0x00000000, 0x00000000, 0x00000000}, // last one
+};
+
+u32 syscfg_tbl_masked_8800dc_u01[][3] = {
+ //#ifdef CONFIG_PMIC_SETTING
+ {0x70001000, (0x1 << 16), (0x1 << 16)}, // for low temperature
+ {0x70001028, (0x1 << 6), (0x1 << 6)},
+ {0x70001000, (0x1 << 16), (0x0 << 16)},
+ //#endif /* CONFIG_PMIC_SETTING */
+};
+
+
+u32 syscfg_tbl_8800dc[][2] = {
+ {0x40500010, 0x00000004},
+ {0x40500010, 0x00000006},//160m clk
+};
+
+
+u32 patch_tbl_wifisetting[][2] =
+{
+ #if !defined(CONFIG_FPGA_VERIFICATION)
+ {0x0090, 0x0013FC00}, //rx_ringbuf_start2
+ #endif
+#ifdef CONFIG_USB_TX_AGGR
+ {0x0100, 0x03021714}, //usb fc params(rx msg fc recover, rx msg fc trigger, wifi fc recover, wifi fc trigger)
+ {0x0120, 0x140A0100}, //usb agg tx params(total cnt, aggr cnt, out en, global out nak)
+#endif //CONFIG_USB_TX_AGGR
+ {0x00b0, 0xAD180100},
+};
+
+u32 jump_tbl[][2] =
+{
+ {296, 0x180001},
+ {137, 0x180011},
+ {303, 0x1810f9},
+ {168, 0x18186d},
+ {308, 0x181bbd},
+ {288, 0x1820c1},
+};
+
+
+uint32_t ldpc_cfg_ram[] = {
+#if 0//def CONFIG_FPGA_VERIFICATION
+ 0x00363638,
+ 0x1DF8F834,
+ 0x1DF8F834,
+ 0x1DF8F834,
+ 0x1DF8F834,
+ 0x002F2F31,
+ 0x1DF8F82C,
+ 0x1DF8F82C,
+ 0x1DF8F82C,
+ 0x1DF8F82C,
+ 0x00363639,
+ 0x1AA5F834,
+ 0x1AA5F834,
+ 0x1ADEF834,
+ 0x1ADEF834,
+ 0x003A3A3E,
+ 0x1578F436,
+ 0x1578F436,
+ 0x1578F436,
+ 0x15B6F436,
+ 0x003B3B40,
+ 0x1DF8F838,
+ 0x1DF8F838,
+ 0x1DF8F838,
+ 0x1DF8F838,
+ 0x003B3B41,
+ 0x1DC4F838,
+ 0x1DC4F838,
+ 0x1DF8F838,
+ 0x1DF8F838,
+ 0x003B3B40,
+ 0x1781F838,
+ 0x1781F838,
+ 0x1781F838,
+ 0x17C4F838,
+ 0x003B3B40,
+ 0x0E81F838,
+ 0x0E81F838,
+ 0x0E81F838,
+ 0x0E82F838,
+ 0x003F3F43,
+ 0x1A92F83D,
+ 0x1A92F83E,
+ 0x1A92F83D,
+ 0x1ADDF83D,
+ 0x00272729,
+ 0x1DF8F824,
+ 0x1DF8F824,
+ 0x1DF8F843,
+ 0x1DF8F843,
+ 0x00272729,
+ 0x1DF8F824,
+ 0x1DF8F824,
+ 0x1DF8F842,
+ 0x1DF8F842,
+ 0x00262628,
+ 0x1DF8F823,
+ 0x1DF8F823,
+ 0x1DF8F823,
+ 0x1DF8F823,
+ 0x00252528,
+ 0x1DF8F823,
+ 0x1DF8F823,
+ 0x1DF8F823,
+ 0x1DF8F823,
+ 0x00262628,
+ 0x1DF8F823,
+ 0x1DF8F823,
+ 0x1DF8F823,
+ 0x1DF8F823,
+ 0x00242427,
+ 0x1DF8F821,
+ 0x1DF8F821,
+ 0x1DF8F821,
+ 0x1DF8F821,
+ 0x00232326,
+ 0x1DF8F821,
+ 0x1DF8F820,
+ 0x1DF8F820,
+ 0x1DF8F820,
+ 0x00262628,
+ 0x1DF8F823,
+ 0x1DF8F823,
+ 0x1DF8F823,
+ 0x1DF8F823,
+ 0x00242427,
+ 0x1DF8F821,
+ 0x1DF8F821,
+ 0x1DF8F821,
+ 0x1DF8F821,
+ 0x001F1F21,
+ 0x1DF8F81D,
+ 0x1DF8F81D,
+ 0x1DF8F81D,
+ 0x1DF8F81D,
+ 0x00262643,
+ 0x1DF8F822,
+ 0x1DF8F821,
+ 0x1DF8F821,
+ 0x1DF8F821,
+ 0x0018182B,
+ 0x1DF8F816,
+ 0x1DBDF815,
+ 0x1DF8F815,
+ 0x1DF8F815,
+ 0x0018182A,
+ 0x1195F836,
+ 0x1195F815,
+ 0x1195F815,
+ 0x1196F815,
+ 0x0028282C,
+ 0x1DF8F824,
+ 0x1DF8F824,
+ 0x1DF8F824,
+ 0x1DF8F824,
+ 0x0027272C,
+ 0x1DF8F824,
+ 0x1DF8F823,
+ 0x1DF8F823,
+ 0x1DF8F823,
+ 0x0082824A,
+ 0x1ADFF841,
+ 0x1ADDF822,
+ 0x1ADEF822,
+ 0x1ADFF822,
+ 0x003E3E40,
+ 0x09D1F81D,
+ 0x095BF81D,
+ 0x095BF81D,
+ 0x095BF81D,
+ 0x0029292D,
+ 0x1DF8F825,
+ 0x1DF8F825,
+ 0x1DF8F825,
+ 0x1DF8F825,
+ 0x0028282C,
+ 0x1DF8F824,
+ 0x1DF8F824,
+ 0x1DF8F824,
+ 0x1DF8F824,
+ 0x0029292D,
+ 0x1DF8F825,
+ 0x1DF8F825,
+ 0x1DF8F825,
+ 0x1DF8F825,
+ 0x0028282E,
+ 0x1DF8F825,
+ 0x1DF8F824,
+ 0x1DF8F824,
+ 0x1DF8F824,
+ 0x0026262C,
+ 0x1DF8F823,
+ 0x1DF8F822,
+ 0x1DF8F822,
+ 0x1DF8F822,
+ 0x0028282D,
+ 0x1DF8F825,
+ 0x1DF8F824,
+ 0x1DF8F824,
+ 0x1DF8F824,
+ 0x00282852,
+ 0x1DF8F827,
+ 0x1DF8F824,
+ 0x1DF8F824,
+ 0x1DF8F824,
+ 0x0029294E,
+ 0x1DF8F823,
+ 0x1DF8F822,
+ 0x1DF8F822,
+ 0x1DF8F822,
+ 0x00212143,
+ 0x1DF8F821,
+ 0x1DECF81D,
+ 0x1DF4F81D,
+ 0x1DF8F81D,
+ 0x0086864D,
+ 0x1CF0F844,
+ 0x1CEDF823,
+ 0x1CEFF822,
+ 0x1CF0F822,
+ 0x0047474D,
+ 0x1BE8F823,
+ 0x1BE8F823,
+ 0x1BE9F822,
+ 0x1BEAF822,
+ 0x0018182F,
+ 0x14B0F83C,
+ 0x14B0F814,
+ 0x14B0F814,
+ 0x14B0F814,
+ 0x00404040,
+ 0x0AE1F81E,
+ 0x0A61F81D,
+ 0x0A61F81D,
+ 0x0A61F81D,
+ 0x002C2C40,
+ 0x09555526,
+ 0x09555512,
+ 0x09555513,
+ 0x09555512,
+ 0x00181840,
+ 0x06333329,
+ 0x06333314,
+ 0x06333314,
+ 0x06333314,
+ 0x002B2B2F,
+ 0x1DF8F828,
+ 0x1DF8F828,
+ 0x1DF8F828,
+ 0x1DF8F828,
+ 0x002B2B32,
+ 0x1DF8F829,
+ 0x1DF8F828,
+ 0x1DF8F828,
+ 0x1DF8F828,
+ 0x002A2A2F,
+ 0x1DF8F827,
+ 0x1DF8F827,
+ 0x1DF8F827,
+ 0x1DF8F827,
+ 0x002A2A57,
+ 0x1DF8F82B,
+ 0x1DF8F827,
+ 0x1DF8F827,
+ 0x1DF8F827,
+ 0x00919152,
+ 0x1DF8F84B,
+ 0x1DF8F825,
+ 0x1DF8F825,
+ 0x1DF8F825,
+ 0x004C4C51,
+ 0x1DF8F826,
+ 0x1DF8F825,
+ 0x1DF8F825,
+ 0x1DF8F825,
+ 0x00444440,
+ 0x0CF8F820,
+ 0x0C6EF81F,
+ 0x0C6EF81F,
+ 0x0C6EF81F,
+ 0x00424240,
+ 0x0D75753E,
+ 0x0D75751E,
+ 0x0D75751E,
+ 0x0D75751E,
+ 0x00191940,
+ 0x0539392E,
+ 0x05393914,
+ 0x05393914,
+ 0x05393914,
+ 0x002F2F32,
+ 0x1AA5F82C,
+ 0x1AA5F82C,
+ 0x1ADEF82C,
+ 0x1ADEF82C,
+ 0x002F2F40,
+ 0x0C6EDE2C,
+ 0x0C6EDE2C,
+ 0x0C6EDE2C,
+ 0x0C6EDE2C,
+ 0x00323240,
+ 0x053BB62E,
+ 0x053BB62E,
+ 0x053BB62E,
+ 0x053BB62E,
+ 0x00333339,
+ 0x1DC4F82F,
+ 0x1DC4F82F,
+ 0x1DF8F82F,
+ 0x1DF8F82F,
+ 0x00333340,
+ 0x0E81F82F,
+ 0x0E81F82F,
+ 0x0E81F82F,
+ 0x0E82F82F,
+ 0x00333340,
+ 0x063FC42F,
+ 0x063FC42F,
+ 0x063FC42F,
+ 0x063FC42F,
+ 0x00404040,
+ 0x063FC42F,
+ 0x063FC42F,
+ 0x063FC42F,
+ 0x063FC42F,
+ 0x00363640,
+ 0x0747DD33,
+ 0x0747DD33,
+ 0x0747DD33,
+ 0x0747DD33,
+ 0x00404040,
+ 0x0747DD33,
+ 0x0747DD33,
+ 0x0747DD33,
+ 0x0747DD33,
+ 0x00292940,
+ 0x07484825,
+ 0x07484812,
+ 0x07484812,
+ 0x07484812,
+ 0x00404040,
+ 0x07343428,
+ 0x07343414,
+ 0x07343414,
+ 0x07343414,
+ 0x00404040,
+ 0x0538382A,
+ 0x05383814,
+ 0x05383814,
+ 0x05383814,
+ 0x00404040,
+ 0x05292914,
+ 0x05292909,
+ 0x05292909,
+ 0x05292909,
+ 0x000B0B40,
+ 0x02111108,
+ 0x0211110E,
+ 0x02111108,
+ 0x02111108,
+ 0x00404040,
+ 0x063E3E2E,
+ 0x063E3E15,
+ 0x063E3E14,
+ 0x063E3E14,
+ 0x00404040,
+ 0x062E2E14,
+ 0x062E2E09,
+ 0x062E2E09,
+ 0x062E2E09,
+ 0x000B0B40,
+ 0x02131308,
+ 0x0213130F,
+ 0x02131308,
+ 0x02131308
+#else
+ 0x00767679,
+ 0x1DF8F870,
+ 0x1DF8F870,
+ 0x1DF8F870,
+ 0x1DF8F870,
+ 0x006E6E72,
+ 0x1DF8F869,
+ 0x1DF8F869,
+ 0x1DF8F869,
+ 0x1DF8F869,
+ 0x0076767B,
+ 0x1DF8F870,
+ 0x1DF8F870,
+ 0x1DF8F870,
+ 0x1DF8F870,
+ 0x007E7E85,
+ 0x1DF4F876,
+ 0x1DF4F876,
+ 0x1DF4F876,
+ 0x1DF8F876,
+ 0x0081818A,
+ 0x1DF8F87B,
+ 0x1DF8F87B,
+ 0x1DF8F87B,
+ 0x1DF8F87B,
+ 0x0081818D,
+ 0x1DF8F87B,
+ 0x1DF8F87B,
+ 0x1DF8F87B,
+ 0x1DF8F87B,
+ 0x0081818A,
+ 0x1DF8F87B,
+ 0x1DF8F87C,
+ 0x1DF8F87B,
+ 0x1DF8F87B,
+ 0x007E7E40,
+ 0x1DF8F87B,
+ 0x1DF8F87B,
+ 0x1DF8F87B,
+ 0x1DF8F87B,
+ 0x008B8B92,
+ 0x1DF8F887,
+ 0x1DF8F889,
+ 0x1DF8F887,
+ 0x1DF8F887,
+ 0x00515155,
+ 0x1DF8F84C,
+ 0x1DF8F84C,
+ 0x1DF8F889,
+ 0x1DF8F889,
+ 0x00515154,
+ 0x1DF8F84C,
+ 0x1DF8F84C,
+ 0x1DF8F888,
+ 0x1DF8F888,
+ 0x004F4F53,
+ 0x1DF8F84A,
+ 0x1DF8F84A,
+ 0x1DF8F84A,
+ 0x1DF8F84A,
+ 0x004F4F53,
+ 0x1DF8F84A,
+ 0x1DF8F84A,
+ 0x1DF8F84A,
+ 0x1DF8F84A,
+ 0x004F4F53,
+ 0x1DF8F84A,
+ 0x1DF8F84A,
+ 0x1DF8F84A,
+ 0x1DF8F84A,
+ 0x004E4E53,
+ 0x1DF8F849,
+ 0x1DF8F848,
+ 0x1DF8F848,
+ 0x1DF8F848,
+ 0x004D4D52,
+ 0x1DF8F847,
+ 0x1DF8F847,
+ 0x1DF8F847,
+ 0x1DF8F847,
+ 0x004F4F55,
+ 0x1DF8F84B,
+ 0x1DF8F84A,
+ 0x1DF8F84A,
+ 0x1DF8F84A,
+ 0x004E4E53,
+ 0x1DF8F849,
+ 0x1DF8F848,
+ 0x1DF8F848,
+ 0x1DF8F848,
+ 0x0049494D,
+ 0x1DF8F844,
+ 0x1DF8F844,
+ 0x1DF8F844,
+ 0x1DF8F844,
+ 0x0051518F,
+ 0x1DF8F849,
+ 0x1DF8F848,
+ 0x1DF8F848,
+ 0x1DF8F848,
+ 0x00424277,
+ 0x1DF8F83F,
+ 0x1DF8F83C,
+ 0x1DF8F83C,
+ 0x1DF8F83C,
+ 0x00424275,
+ 0x1DF8F89E,
+ 0x1DF8F83C,
+ 0x1DF8F83C,
+ 0x1DF8F83C,
+ 0x0055555C,
+ 0x1DF8F84C,
+ 0x1DF8F84C,
+ 0x1DF8F84C,
+ 0x1DF8F84C,
+ 0x0053535C,
+ 0x1DF8F84C,
+ 0x1DF8F84B,
+ 0x1DF8F84B,
+ 0x1DF8F84B,
+ 0x00F8F89E,
+ 0x1DF8F88C,
+ 0x1DF8F84A,
+ 0x1DF8F84A,
+ 0x1DF8F84A,
+ 0x00898940,
+ 0x18F8F846,
+ 0x18CFF845,
+ 0x18CFF844,
+ 0x18CFF844,
+ 0x0056565F,
+ 0x1DF8F84F,
+ 0x1DF8F84F,
+ 0x1DF8F84F,
+ 0x1DF8F84F,
+ 0x0055555E,
+ 0x1DF8F84E,
+ 0x1DF8F84E,
+ 0x1DF8F84E,
+ 0x1DF8F84E,
+ 0x0056565F,
+ 0x1DF8F84F,
+ 0x1DF8F84F,
+ 0x1DF8F84F,
+ 0x1DF8F84F,
+ 0x00555561,
+ 0x1DF8F850,
+ 0x1DF8F84E,
+ 0x1DF8F84E,
+ 0x1DF8F84E,
+ 0x0053535F,
+ 0x1DF8F84D,
+ 0x1DF8F84C,
+ 0x1DF8F84C,
+ 0x1DF8F84C,
+ 0x0055555F,
+ 0x1DF8F84F,
+ 0x1DF8F84E,
+ 0x1DF8F84E,
+ 0x1DF8F84E,
+ 0x005555AA,
+ 0x1DF8F854,
+ 0x1DF8F84E,
+ 0x1DF8F84E,
+ 0x1DF8F84E,
+ 0x005959A6,
+ 0x1DF8F84D,
+ 0x1DF8F84C,
+ 0x1DF8F84C,
+ 0x1DF8F84C,
+ 0x004F4F9B,
+ 0x1DF8F84E,
+ 0x1DF8F846,
+ 0x1DF8F846,
+ 0x1DF8F846,
+ 0x00F8F8A5,
+ 0x1DF8F894,
+ 0x1DF8F84C,
+ 0x1DF8F84C,
+ 0x1DF8F84C,
+ 0x009898A4,
+ 0x1DF8F84D,
+ 0x1DF8F84C,
+ 0x1DF8F84C,
+ 0x1DF8F84C,
+ 0x00464686,
+ 0x1DF8F8B3,
+ 0x1DF8F83D,
+ 0x1DF8F83D,
+ 0x1DF8F83D,
+ 0x008E8E40,
+ 0x1AF8F848,
+ 0x1ADFF848,
+ 0x1ADFF846,
+ 0x1ADFF846,
+ 0x007F7F40,
+ 0x18D2D275,
+ 0x18D2D23A,
+ 0x18D2D23A,
+ 0x18D2D239,
+ 0x00454540,
+ 0x0F868664,
+ 0x0F86863E,
+ 0x0F86863D,
+ 0x0F86863D,
+ 0x005C5C64,
+ 0x1DF8F856,
+ 0x1DF8F855,
+ 0x1DF8F855,
+ 0x1DF8F855,
+ 0x005B5B68,
+ 0x1DF8F858,
+ 0x1DF8F855,
+ 0x1DF8F855,
+ 0x1DF8F855,
+ 0x005A5A64,
+ 0x1DF8F855,
+ 0x1DF8F854,
+ 0x1DF8F854,
+ 0x1DF8F854,
+ 0x005A5AB5,
+ 0x1DF8F85B,
+ 0x1DF8F855,
+ 0x1DF8F854,
+ 0x1DF8F854,
+ 0x00F8F8B0,
+ 0x1DF8F8A3,
+ 0x1DF8F852,
+ 0x1DF8F852,
+ 0x1DF8F852,
+ 0x00A4A4AE,
+ 0x1DF8F854,
+ 0x1DF8F852,
+ 0x1DF8F852,
+ 0x1DF8F852,
+ 0x009A9A40,
+ 0x1DF8F84E,
+ 0x1DF8F84D,
+ 0x1DF8F84C,
+ 0x1DF8F84C,
+ 0x009C9C40,
+ 0x1DF8F895,
+ 0x1DF8F849,
+ 0x1DF8F84A,
+ 0x1DF8F84A,
+ 0x00494940,
+ 0x1197976F,
+ 0x11979742,
+ 0x11979741,
+ 0x11979741,
+ 0x006E6E74,
+ 0x1DF8F869,
+ 0x1DF8F869,
+ 0x1DF8F869,
+ 0x1DF8F869,
+ 0x006E6E40,
+ 0x1ADEF869,
+ 0x1ADEF869,
+ 0x1ADEF869,
+ 0x1ADEF869,
+ 0x00757540,
+ 0x0D78F86E,
+ 0x0D78F86E,
+ 0x0D78F86E,
+ 0x0D79F86E,
+ 0x00787885,
+ 0x1DF8F873,
+ 0x1DF8F873,
+ 0x1DF8F873,
+ 0x1DF8F873,
+ 0x00787840,
+ 0x1DF8F873,
+ 0x1DF8F873,
+ 0x1DF8F873,
+ 0x1DF8F873,
+ 0x00787840,
+ 0x0E81F873,
+ 0x0E81F873,
+ 0x0E81F873,
+ 0x0E82F873,
+ 0x00404040,
+ 0x0E82F873,
+ 0x0E82F873,
+ 0x0E82F873,
+ 0x0E82F873,
+ 0x00818140,
+ 0x1092F87E,
+ 0x1092F87E,
+ 0x1092F87E,
+ 0x1092F87E,
+ 0x00404040,
+ 0x1092F87E,
+ 0x1092F87E,
+ 0x1092F87E,
+ 0x1092F87E,
+ 0x00737340,
+ 0x14B2B26B,
+ 0x14B2B235,
+ 0x14B2B235,
+ 0x14B2B235,
+ 0x00404040,
+ 0x0E828260,
+ 0x0E82823D,
+ 0x0E82823C,
+ 0x0E82823C,
+ 0x00404040,
+ 0x0F8B8B66,
+ 0x0F8B8B3F,
+ 0x0F8B8B3D,
+ 0x0F8B8B3D,
+ 0x00404040,
+ 0x0B68683D,
+ 0x0B68681E,
+ 0x0B68681E,
+ 0x0B68681E,
+ 0x00222240,
+ 0x06434318,
+ 0x06434329,
+ 0x06434318,
+ 0x06434318,
+ 0x00404040,
+ 0x129D9D72,
+ 0x129D9D43,
+ 0x129D9D41,
+ 0x129D9D41,
+ 0x00404040,
+ 0x0D757542,
+ 0x0D757520,
+ 0x0D757520,
+ 0x0D757520,
+ 0x00232340,
+ 0x084C4C19,
+ 0x084C4C2C,
+ 0x084C4C19,
+ 0x084C4C19
+#endif
+};
+
+
+uint32_t agc_cfg_ram[] = {
+ 0x20000000,
+ 0x0400000E,
+ 0x3000200E,
+ 0x5B000000,
+ 0x0400004B,
+ 0x3000008E,
+ 0x32000000,
+ 0x0400007B,
+ 0x40000000,
+ 0xF8000026,
+ 0x04000011,
+ 0x4819008E,
+ 0x9C000020,
+ 0x08000191,
+ 0x38008000,
+ 0x0A000000,
+ 0x08104411,
+ 0x38018000,
+ 0x0C004641,
+ 0x08D00014,
+ 0x30000000,
+ 0x01000000,
+ 0x04000017,
+ 0x30000000,
+ 0x3C000000,
+ 0x0400001A,
+ 0x38020000,
+ 0x40000001,
+ 0x0800001D,
+ 0x3808008E,
+ 0x14000050,
+ 0x08000020,
+ 0x4000008E,
+ 0xA400007B,
+ 0x00000101,
+ 0x3000339F,
+ 0x41000700,
+ 0x04104420,
+ 0x90000000,
+ 0x49000000,
+ 0xF00E842F,
+ 0xEC0E842C,
+ 0xEC0E842C,
+ 0x04000032,
+ 0x30000000,
+ 0x48000101,
+ 0x04000032,
+ 0x30000000,
+ 0x48000202,
+ 0x04000032,
+ 0x30000000,
+ 0x46000000,
+ 0x04000011,
+ 0x58010006,
+ 0x3D040472,
+ 0xDC204439,
+ 0x081DD4D2,
+ 0x480A0006,
+ 0xDC2044DC,
+ 0x081DD43C,
+ 0x38050004,
+ 0x0EF1F1C3,
+ 0x342044DC,
+ 0x30000000,
+ 0x01000000,
+ 0x04000042,
+ 0x30000000,
+ 0x33000000,
+ 0x04104445,
+ 0x38008000,
+ 0x2200109C,
+ 0x08104448,
+ 0x38008000,
+ 0x23D4509C,
+ 0x08104417,
+ 0x9000A000,
+ 0x32000000,
+ 0x18000063,
+ 0x14000060,
+ 0x1C000051,
+ 0x10000057,
+ 0x38028000,
+ 0x0C000001,
+ 0x08D04466,
+ 0x3000200F,
+ 0x00000000,
+ 0x00000000,
+ 0x38030000,
+ 0x0C002601,
+ 0x08D0445A,
+ 0x30000000,
+ 0x3D020230,
+ 0x0400005D,
+ 0x30000000,
+ 0x3E000100,
+ 0x04000066,
+ 0x38028000,
+ 0x0C001601,
+ 0x34204466,
+ 0x38028000,
+ 0x0C000A01,
+ 0x34204466,
+ 0x38008004,
+ 0xFF000000,
+ 0x0800007B,
+ 0x3800802F,
+ 0x26000000,
+ 0x0800006C,
+ 0x380404AF,
+ 0x1F191010,
+ 0x0800006F,
+ 0x20000CAF,
+ 0x04000071,
+ 0x60000CAF,
+ 0x18700079,
+ 0x14000077,
+ 0x10000075,
+ 0x28140CAF,
+ 0x09B00084,
+ 0x280A0CAF,
+ 0x09B00084,
+ 0x28060CAF,
+ 0x09B00084,
+ 0x28048086,
+ 0x0800007D,
+ 0x38000086,
+ 0x22800000,
+ 0x04000080,
+ 0x30000000,
+ 0x0EF1F101,
+ 0x36004883,
+ 0x28020000,
+ 0x08000085,
+ 0x3802008E,
+ 0x3D040431,
+ 0x08000088,
+ 0x3805008E,
+ 0x1F241821,
+ 0x0800008B,
+ 0x3000008E,
+ 0xA0163021,
+ 0x0400008E,
+ 0x3000008E,
+ 0x0EF10012,
+ 0x34000091,
+ 0x300000CC,
+ 0x50000000,
+ 0x04000094,
+ 0x380095FE,
+ 0x32010000,
+ 0x04000097,
+ 0x50001FFE,
+ 0x5A010000,
+ 0x6DC9989B,
+ 0xFC19D4B9,
+ 0x30000186,
+ 0x3D840373,
+ 0x0400009E,
+ 0x3000008E,
+ 0x0A000000,
+ 0x040000A1,
+ 0x3000008E,
+ 0x22C00000,
+ 0x040000A4,
+ 0x9000028E,
+ 0x32010001,
+ 0x8E4000AA,
+ 0xC80000B0,
+ 0x00000000,
+ 0x00000000,
+ 0x3000008E,
+ 0x32010001,
+ 0x040000CB,
+ 0x3000008E,
+ 0x29000000,
+ 0x94045011,
+ 0x300019B6,
+ 0x32010000,
+ 0x040000B3,
+ 0x300019B6,
+ 0x3D040431,
+ 0x040000B6,
+ 0x300019B6,
+ 0x22800000,
+ 0x04000097,
+ 0x30000186,
+ 0x3D840473,
+ 0x040000BC,
+ 0x3000008E,
+ 0x29030000,
+ 0x040000BF,
+ 0x9AEE028E,
+ 0x32010100,
+ 0x7C0000C5,
+ 0xCC0000B0,
+ 0x080000B0,
+ 0x00000000,
+ 0x3000008E,
+ 0x32010100,
+ 0x040000C8,
+ 0x3000028E,
+ 0x29000000,
+ 0x94045011,
+ 0x5000038E,
+ 0x29000000,
+ 0x94045011,
+ 0xC0000035,
+ 0x38010006,
+ 0x3D040472,
+ 0x080000D2,
+ 0x30000004,
+ 0x0EF1F141,
+ 0x340000D5,
+ 0x28040004,
+ 0x080000D7,
+ 0x2808000E,
+ 0x080000D9,
+ 0x3000018E,
+ 0x0EF10052,
+ 0x340000DC,
+ 0x3000038E,
+ 0x29000000,
+ 0x94045011,
+ 0x38020000,
+ 0x32000000,
+ 0x080000E2,
+ 0x60000000,
+ 0xD80000E6,
+ 0xD40000E9,
+ 0x040000EC,
+ 0x30000000,
+ 0x0EF1F121,
+ 0x360048EF,
+ 0x30000000,
+ 0x0C002421,
+ 0x360048EF,
+ 0x30000000,
+ 0x0C000021,
+ 0x360048EF,
+ 0x28020000,
+ 0x0800007B,
+ 0x50001EFE,
+ 0x5A010000,
+ 0x6DC998F5,
+ 0xFC19D4F8,
+ 0x3000028E,
+ 0x32000040,
+ 0x040000FB,
+ 0x3AEE028E,
+ 0x32000080,
+ 0x040000FB,
+ 0x30000000,
+ 0x0EF1F101,
+ 0x360048FE,
+ 0x28020000,
+ 0x08000100,
+ 0x3802008E,
+ 0x3D040431,
+ 0x08000103,
+ 0x3805008E,
+ 0x1F241821,
+ 0x08000106,
+ 0x3000008E,
+ 0xA0163021,
+ 0x04000109,
+ 0x3000008E,
+ 0x0EF10012,
+ 0x3400010C,
+ 0x300014F6,
+ 0x32010000,
+ 0x04000114,
+ 0x20000000,
+ 0x04000111,
+ 0x300000EC,
+ 0x50000000,
+ 0x040000F1,
+ 0x300014F6,
+ 0x32030000,
+ 0x04000117,
+ 0x30001086,
+ 0x3D840473,
+ 0x0400011A,
+ 0x5000108E,
+ 0x22C00000,
+ 0x8E47C0CB,
+ 0xCB30011E,
+ 0x300019B6,
+ 0x32040000,
+ 0x04000121,
+ 0x300019B6,
+ 0x3D040431,
+ 0x04000124,
+ 0x300019B6,
+ 0x22800000,
+ 0x04000111,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x30000186,
+ 0x3D840473,
+ 0x0400012D,
+ 0x5000038E,
+ 0x29000000,
+ 0x94045011,
+ 0xC0000131,
+ 0x380C800E,
+ 0xFF000000,
+ 0x08000134,
+ 0x30000004,
+ 0x0FF1F103,
+ 0x34000137,
+ 0x28020000,
+ 0x08000139,
+ 0x3000038E,
+ 0x29000000,
+ 0x94045011,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x58010006,
+ 0x3D040472,
+ 0xDC204543,
+ 0x081DD4D2,
+ 0x480A0006,
+ 0xDC2044DC,
+ 0x081DD546,
+ 0x38050004,
+ 0x0EF1F141,
+ 0x342044DC,
+ 0x2802800E,
+ 0x080000DC,
+ 0x48000035,
+ 0x0400014A,
+ 0x7896638F,
+ 0x4100000F,
+ 0x8C00014F,
+ 0x080450C4,
+ 0x90104574,
+ 0x88C8620F,
+ 0xC000015A,
+ 0x90104574,
+ 0x08104554,
+ 0x94104557,
+ 0x3000628F,
+ 0x29000000,
+ 0x9404517A,
+ 0x3000638F,
+ 0x29000000,
+ 0x0410457A,
+ 0x3800E005,
+ 0x3D010131,
+ 0x0810455D,
+ 0xA832600F,
+ 0x90104574,
+ 0x08000154,
+ 0x94104557,
+ 0xC6104567,
+ 0xC4185563,
+ 0x5802E00F,
+ 0x0FEEEA07,
+ 0x80000174,
+ 0x3420456B,
+ 0x5802E00F,
+ 0x0EEEEA07,
+ 0x80000174,
+ 0x3420456B,
+ 0x30004000,
+ 0x33000001,
+ 0x0400016E,
+ 0x38034005,
+ 0x3D030373,
+ 0x08000171,
+ 0x30006007,
+ 0x33000000,
+ 0x04000174,
+ 0x3000608F,
+ 0x29000000,
+ 0x94045177,
+ 0x4000608F,
+ 0xA010457D,
+ 0x0410457A,
+ 0x3000608F,
+ 0x64000101,
+ 0x04104411,
+ 0x3000608F,
+ 0x64000101,
+ 0x04104580,
+ 0x3000618F,
+ 0x42000001,
+ 0x04000183,
+ 0x38028000,
+ 0x32000000,
+ 0x08104586,
+ 0x280A618F,
+ 0x08000188,
+ 0x480A618F,
+ 0xBC00018B,
+ 0x0800018E,
+ 0x3000618F,
+ 0x34000001,
+ 0x04000005,
+ 0x3000618F,
+ 0x34000000,
+ 0x04000008,
+ 0x3000008F,
+ 0x0EEAED0F,
+ 0x36000194,
+ 0x38038000,
+ 0x34000000,
+ 0x08000197,
+ 0x38028005,
+ 0x29010002,
+ 0x0800019A,
+ 0x3000028F,
+ 0x2200209C,
+ 0x0400019D,
+ 0x3000028F,
+ 0x23D4509C,
+ 0x040001A0,
+ 0x2814028F,
+ 0x080001A2,
+ 0x3000028F,
+ 0x43010201,
+ 0x040001A5,
+ 0x3000128F,
+ 0x32000100,
+ 0x040001A8,
+ 0x5AEE138F,
+ 0x4100000F,
+ 0x7C0001AC,
+ 0x080000F9,
+ 0x592C138F,
+ 0x29000000,
+ 0x8C0001B0,
+ 0x080000F9,
+ 0x2000138F,
+ 0x94045011,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000
+};
+
+uint32_t txgain_map[96] = {
+#ifdef CONFIG_FPGA_VERIFICATION
+ 0x20c0c971,
+ 0x20c0c980,
+ 0x20c0c992,
+ 0x20c0c9a6,
+ 0x20c0c9bf,
+ 0x20c0caa5,
+ 0x20c0cabd,
+ 0x20c0cba0,
+ 0x20c0cbb6,
+ 0x20c0cbea,
+ 0x20c0ccc5,
+ 0x20c0cdac,
+ 0x20c0cdd0,
+ 0x20c0ceb2,
+ 0x20c0ceff,
+ 0x20c0cfff,
+ 0x20c0c922,
+ 0x20c0c922,
+ 0x20c0c922,
+ 0x20c0c922,
+ 0x20c0c922,
+ 0x20c0c922,
+ 0x20c0c922,
+ 0x20c0c927,
+ 0x20c0c92c,
+ 0x20c0c931,
+ 0x20c0c937,
+ 0x20c0c93f,
+ 0x20c0c946,
+ 0x20c0c94f,
+ 0x20c0c959,
+ 0x20c0c964,
+ 0x20c0cbee,
+ 0x20c0cce0,
+ 0x20c0ccff,
+ 0x20c0cde2,
+ 0x20c0cdfe,
+ 0x20c0cede,
+ 0x20c0cefc,
+ 0x20c0cfd9,
+ 0x20c0cff8,
+ 0x20c0cfff,
+ 0x20c0cfff,
+ 0x20c0cfff,
+ 0x20c0cfff,
+ 0x20c0cfff,
+ 0x20c0cfff,
+ 0x20c0cfff,
+ 0x20c0c97c,
+ 0x20c0c97c,
+ 0x20c0c97c,
+ 0x20c0c97c,
+ 0x20c0c97c,
+ 0x20c0c97c,
+ 0x20c0c97c,
+ 0x20c0c98c,
+ 0x20c0ca79,
+ 0x20c0ca89,
+ 0x20c0cb74,
+ 0x20c0cb84,
+ 0x20c0cb94,
+ 0x20c0cba8,
+ 0x20c0cbbb,
+ 0x20c0cbd2,
+ 0x20c0cbee,
+ 0x20c0cce0,
+ 0x20c0ccff,
+ 0x20c0cde2,
+ 0x20c0cdfe,
+ 0x20c0cede,
+ 0x20c0cefc,
+ 0x20c0cfd9,
+ 0x20c0cff8,
+ 0x20c0cfff,
+ 0x20c0cfff,
+ 0x20c0cfff,
+ 0x20c0cfff,
+ 0x20c0cfff,
+ 0x20c0cfff,
+ 0x20c0cfff,
+ 0x20c0c97c,
+ 0x20c0c97c,
+ 0x20c0c97c,
+ 0x20c0c97c,
+ 0x20c0c97c,
+ 0x20c0c97c,
+ 0x20c0c97c,
+ 0x20c0c98c,
+ 0x20c0ca79,
+ 0x20c0ca89,
+ 0x20c0cb74,
+ 0x20c0cb84,
+ 0x20c0cb94,
+ 0x20c0cba8,
+ 0x20c0cbbb,
+ 0x20c0cbd2,
+#else
+ //11b
+ 0x00ffd780,
+ 0x00ffd872,
+ 0x00ffd880,
+ 0x00ffd972,
+ 0x00ffd980,
+ 0x00ffda75,
+ 0x00ffda86,
+ 0x00ffdb77,
+ 0x00ffdb86,
+ 0x00ffdc78,
+ 0x00ffdc89,
+ 0x00ffdd79,
+ 0x00ffdd89,
+ 0x00ffde83,
+ 0x00ffdf79,
+ 0x00ffdf8b,
+ 0x00ffd072,
+ 0x00ffd072,
+ 0x00ffd080,
+ 0x00ffd172,
+ 0x00ffd180,
+ 0x00ffd272,
+ 0x00ffd280,
+ 0x00ffd36d,
+ 0x00ffd379,
+ 0x00ffd46d,
+ 0x00ffd479,
+ 0x00ffd572,
+ 0x00ffd580,
+ 0x00ffd672,
+ 0x00ffd680,
+ 0x00ffd772,
+ //high
+ 0x00ffc87d,
+ 0x00ffc88b,
+ 0x00ffc979,
+ 0x00ffc989,
+ 0x00ffca7d,
+ 0x00ffca88,
+ 0x00ffcc5e,
+ 0x00ffcc69,
+ 0x00ffcc78,
+ 0x00ffcc85,
+ 0x00ffcd70,
+ 0x00ffcd80,
+ 0x00ffce70,
+ 0x00ffce80,
+ 0x00ffcf7d,
+ 0x00ffcf90,
+ 0x00ffc080,
+ 0x00ffc090,
+ 0x00ffc180,
+ 0x00ffc190,
+ 0x00ffc27b,
+ 0x00ffc28b,
+ 0x00ffc37b,
+ 0x00ffc390,
+ 0x00ffc485,
+ 0x00ffc495,
+ 0x00ffc579,
+ 0x00ffc589,
+ 0x00ffc679,
+ 0x00ffc689,
+ 0x00ffc780,
+ 0x00ffc790,
+ //low
+ 0x00ffc87d,
+ 0x00ffc88b,
+ 0x00ffc979,
+ 0x00ffc989,
+ 0x00ffca7d,
+ 0x00ffca88,
+ 0x00ffcc5e,
+ 0x00ffcc69,
+ 0x00ffcc78,
+ 0x00ffcc85,
+ 0x00ffcd70,
+ 0x00ffcd80,
+ 0x00ffce70,
+ 0x00ffce80,
+ 0x00ffce93,
+ 0x00ffcf90,
+ 0x00ffc080,
+ 0x00ffc090,
+ 0x00ffc180,
+ 0x00ffc190,
+ 0x00ffc27b,
+ 0x00ffc28b,
+ 0x00ffc37b,
+ 0x00ffc390,
+ 0x00ffc485,
+ 0x00ffc495,
+ 0x00ffc579,
+ 0x00ffc589,
+ 0x00ffc679,
+ 0x00ffc689,
+ 0x00ffc780,
+ 0x00ffc790,
+#endif
+};
+
+u32 patch_tbl_func[][2] =
+{
+ {0x00110054, 0x0018186D}, // same as jump_tbl idx 168
+ {0x0011005C, 0x0018186D}, // same as jump_tbl idx 168
+};
+
+
+u32 patch_tbl_rf_func[][2] =
+{
+ {0x00110bf0, 0x00180001},
+};
+
+
+u32 wifi_txgain_table_24g_8800dcdw[32] =
+{
+ 0xA4B22189, //index 0
+ 0x00007825,
+ 0xA4B2214B, //index 1
+ 0x00007825,
+ 0xA4B2214F, //index 2
+ 0x00007825,
+ 0xA4B221D5, //index 3
+ 0x00007825,
+ 0xA4B221DC, //index 4
+ 0x00007825,
+ 0xA4B221E5, //index 5
+ 0x00007825,
+ 0xAC9221E5, //index 6
+ 0x00006825,
+ 0xAC9221EF, //index 7
+ 0x00006825,
+ 0xBC9221EE, //index 8
+ 0x00006825,
+ 0xBC9221FF, //index 9
+ 0x00006825,
+ 0xBC9221FF, //index 10
+ 0x00004025,
+ 0xB792203F, //index 11
+ 0x00004026,
+ 0xDC92203F, //index 12
+ 0x00004025,
+ 0xE692203F, //index 13
+ 0x00004025,
+ 0xFF92203F, //index 14
+ 0x00004035,
+ 0xFFFE203F, //index 15
+ 0x00004832
+};
+
+u32 wifi_txgain_table_24g_1_8800dcdw[32] =
+{
+ 0x090E2011, //index 0
+ 0x00004001,
+ 0x090E2015, //index 1
+ 0x00004001,
+ 0x090E201B, //index 2
+ 0x00004001,
+ 0x110E2018, //index 3
+ 0x00004001,
+ 0x110E201E, //index 4
+ 0x00004001,
+ 0x110E2023, //index 5
+ 0x00004001,
+ 0x190E2021, //index 6
+ 0x00004001,
+ 0x190E202B, //index 7
+ 0x00004001,
+ 0x210E202B, //index 8
+ 0x00004001,
+ 0x230E2027, //index 9
+ 0x00004001,
+ 0x230E2031, //index 10
+ 0x00004001,
+ 0x240E2039, //index 11
+ 0x00004001,
+ 0x260E2039, //index 12
+ 0x00004001,
+ 0x2E0E203F, //index 13
+ 0x00004001,
+ 0x368E203F, //index 14
+ 0x00004001,
+ 0x3EF2203F, //index 15
+ 0x00004001
+};
+
+u32 wifi_rxgain_table_24g_20m_8800dcdw[64] = {
+ 0x82f282d1,//index 0
+ 0x9591a324,
+ 0x80808419,
+ 0x000000f0,
+ 0x42f282d1,//index 1
+ 0x95923524,
+ 0x80808419,
+ 0x000000f0,
+ 0x22f282d1,//index 2
+ 0x9592c724,
+ 0x80808419,
+ 0x000000f0,
+ 0x02f282d1,//index 3
+ 0x9591a324,
+ 0x80808419,
+ 0x000000f0,
+ 0x06f282d1,//index 4
+ 0x9591a324,
+ 0x80808419,
+ 0x000000f0,
+ 0x0ef29ad1,//index 5
+ 0x9591a324,
+ 0x80808419,
+ 0x000000f0,
+ 0x0ef29ad3,//index 6
+ 0x95923524,
+ 0x80808419,
+ 0x000000f0,
+ 0x0ef29ad7,//index 7
+ 0x9595a324,
+ 0x80808419,
+ 0x000000f0,
+ 0x02f282d2,//index 8
+ 0x95951124,
+ 0x80808419,
+ 0x000000f0,
+ 0x02f282f4,//index 9
+ 0x95951124,
+ 0x80808419,
+ 0x000000f0,
+ 0x02f282e6,//index 10
+ 0x9595a324,
+ 0x80808419,
+ 0x000000f0,
+ 0x02f282e6,//index 11
+ 0x9599a324,
+ 0x80808419,
+ 0x000000f0,
+ 0x02f282e6,//index 12
+ 0x959da324,
+ 0x80808419,
+ 0x000000f0,
+ 0x02f282e6,//index 13
+ 0x959f5924,
+ 0x80808419,
+ 0x000000f0,
+ 0x06f282e6,//index 14
+ 0x959f5924,
+ 0x80808419,
+ 0x000000f0,
+ 0x0ef29ae6,//index 15
+ 0x959f592c,//////0x959f5924, //loft [35:34]=3
+ 0x80808419,
+ 0x000000f0
+};
+
+u32 wifi_rxgain_table_24g_40m_8800dcdw[64] = {
+ 0x83428151,//index 0
+ 0x9631a328,
+ 0x80808419,
+ 0x000000f0,
+ 0x43428151,//index 1
+ 0x96323528,
+ 0x80808419,
+ 0x000000f0,
+ 0x23428151,//index 2
+ 0x9632c728,
+ 0x80808419,
+ 0x000000f0,
+ 0x03428151,//index 3
+ 0x9631a328,
+ 0x80808419,
+ 0x000000f0,
+ 0x07429951,//index 4
+ 0x9631a328,
+ 0x80808419,
+ 0x000000f0,
+ 0x0f42d151,//index 5
+ 0x9631a328,
+ 0x80808419,
+ 0x000000f0,
+ 0x0f42d153,//index 6
+ 0x96323528,
+ 0x80808419,
+ 0x000000f0,
+ 0x0f42d157,//index 7
+ 0x9635a328,
+ 0x80808419,
+ 0x000000f0,
+ 0x03428152,//index 8
+ 0x96351128,
+ 0x80808419,
+ 0x000000f0,
+ 0x03428174,//index 9
+ 0x96351128,
+ 0x80808419,
+ 0x000000f0,
+ 0x03428166,//index 10
+ 0x9635a328,
+ 0x80808419,
+ 0x000000f0,
+ 0x03428166,//index 11
+ 0x9639a328,
+ 0x80808419,
+ 0x000000f0,
+ 0x03428166,//index 12
+ 0x963da328,
+ 0x80808419,
+ 0x000000f0,
+ 0x03428166,//index 13
+ 0x963f5928,
+ 0x80808419,
+ 0x000000f0,
+ 0x07429966,//index 14
+ 0x963f5928,
+ 0x80808419,
+ 0x000000f0,
+ 0x0f42d166,//index 15
+ 0x963f5928,
+ 0x80808419,
+ 0x000000f0
+};
+
+//adap test
+u32 adaptivity_patch_tbl[][2] = {
+ {0x000C, 0x0000320A}, //linkloss_thd
+ {0x009C, 0x00000000}, //ac_param_conf
+ {0x0128, 0xF6140001}, //tx_adaptivity_en
+};
+//adap test
+
+int aicwf_patch_table_load(struct rwnx_hw *rwnx_hw, char *filename)
+{
+ int err = 0;
+ unsigned int i = 0, size;
+ u32 *dst = NULL;
+ u8 *describle;
+ u32 fmacfw_patch_tbl_8800dc_u02_describe_size = 124;
+ u32 fmacfw_patch_tbl_8800dc_u02_describe_base;//read from patch_tbl
+
+ /* Copy the file on the Embedded side */
+ AICWFDBG(LOGINFO, "### Upload %s \n", filename);
+
+ size = rwnx_request_firmware_common(rwnx_hw, &dst, filename);
+ if (!dst) {
+ AICWFDBG(LOGERROR, "No such file or directory\n");
+ return -1;
+ }
+ if (size <= 0) {
+ AICWFDBG(LOGERROR, "wrong size of firmware file\n");
+ dst = NULL;
+ err = -1;
+ }
+
+ AICWFDBG(LOGINFO, "tbl size = %d \n",size);
+
+ fmacfw_patch_tbl_8800dc_u02_describe_base = dst[0];
+ AICWFDBG(LOGINFO, "FMACFW_PATCH_TBL_8800DC_U02_DESCRIBE_BASE = %x \n",fmacfw_patch_tbl_8800dc_u02_describe_base);
+
+ if (!err && (i < size)) {
+ err = rwnx_send_dbg_mem_block_write_req(rwnx_hw, fmacfw_patch_tbl_8800dc_u02_describe_base, fmacfw_patch_tbl_8800dc_u02_describe_size + 4, dst);
+ if(err){
+ printk("write describe information fail \n");
+ }
+
+ describle = kzalloc(fmacfw_patch_tbl_8800dc_u02_describe_size, GFP_KERNEL);
+ memcpy(describle, &dst[1], fmacfw_patch_tbl_8800dc_u02_describe_size);
+ AICWFDBG(LOGINFO, "%s", describle);
+ kfree(describle);
+ describle = NULL;
+ }
+
+ if (!err && (i < size)) {
+ for (i =(128/4); i < (size/4); i +=2) {
+ AICWFDBG(LOGERROR, "patch_tbl: %x %x\n", dst[i], dst[i+1]);
+ err = rwnx_send_dbg_mem_write_req(rwnx_hw, dst[i], dst[i+1]);
+ }
+ if (err) {
+ AICWFDBG(LOGERROR, "bin upload fail: %x, err:%d\r\n", dst[i], err);
+ }
+ }
+
+ if (dst) {
+ rwnx_release_firmware_common(&dst);
+ }
+
+ return err;
+
+}
+
+//adap test
+extern int get_adap_test(void);
+//adap test
+
+void aicwf_patch_config_8800dc(struct rwnx_hw *rwnx_hw)
+{
+ #ifdef CONFIG_ROM_PATCH_EN
+ int ret = 0;
+ int cnt = 0;
+
+//adap test
+ int adap_test = 0;
+ int adap_patch_num = 0;
+
+ adap_test = get_adap_test();
+//adap test
+
+ if (testmode == 0) {
+ const u32 cfg_base = 0x10164;
+ struct dbg_mem_read_cfm cfm;
+ int i;
+ u32 wifisetting_cfg_addr;
+ u32 ldpc_cfg_addr;
+ u32 agc_cfg_addr;
+ u32 txgain_cfg_addr;
+ u32 jump_tbl_addr = 0;
+
+ u32 patch_tbl_wifisetting_num = sizeof(patch_tbl_wifisetting)/sizeof(u32)/2;
+ u32 ldpc_cfg_size = sizeof(ldpc_cfg_ram);
+ u32 agc_cfg_size = sizeof(agc_cfg_ram);
+ u32 txgain_cfg_size = sizeof(txgain_map);
+ u32 jump_tbl_size = 0;
+ u32 patch_tbl_func_num = 0;
+
+ array2_tbl_t jump_tbl_base = NULL;
+ array2_tbl_t patch_tbl_func_base = NULL;
+
+ if (chip_sub_id == 0) {
+ jump_tbl_base = jump_tbl;
+ jump_tbl_size = sizeof(jump_tbl)/2;
+ patch_tbl_func_base = patch_tbl_func;
+ patch_tbl_func_num = sizeof(patch_tbl_func)/sizeof(u32)/2;
+ }
+
+ //struct dbg_mem_read_cfm cfm;
+ //int i;
+
+ if ((ret = rwnx_send_dbg_mem_read_req(rwnx_hw, cfg_base, &cfm))) {
+ AICWFDBG(LOGERROR, "setting base[0x%x] rd fail: %d\n", cfg_base, ret);
+ }
+ wifisetting_cfg_addr = cfm.memdata;
+
+ if(chip_sub_id == 0){
+ if ((ret = rwnx_send_dbg_mem_read_req(rwnx_hw, cfg_base + 4, &cfm))) {
+ AICWFDBG(LOGERROR, "setting base[0x%x] rd fail: %d\n", cfg_base + 4, ret);
+ }
+ jump_tbl_addr = cfm.memdata;
+ }
+
+ if ((ret = rwnx_send_dbg_mem_read_req(rwnx_hw, cfg_base + 8, &cfm))) {
+ AICWFDBG(LOGERROR, "setting base[0x%x] rd fail: %d\n", cfg_base + 8, ret);
+ }
+ ldpc_cfg_addr = cfm.memdata;
+
+ if ((ret = rwnx_send_dbg_mem_read_req(rwnx_hw, cfg_base + 0xc, &cfm))) {
+ AICWFDBG(LOGERROR, "setting base[0x%x] rd fail: %d\n", cfg_base + 0xc, ret);
+ }
+ agc_cfg_addr = cfm.memdata;
+
+ if ((ret = rwnx_send_dbg_mem_read_req(rwnx_hw, cfg_base + 0x10, &cfm))) {
+ AICWFDBG(LOGERROR, "setting base[0x%x] rd fail: %d\n", cfg_base + 0x10, ret);
+ }
+ txgain_cfg_addr = cfm.memdata;
+
+ AICWFDBG(LOGINFO, "wifisetting_cfg_addr=%x, ldpc_cfg_addr=%x, agc_cfg_addr=%x, txgain_cfg_addr=%x\n", wifisetting_cfg_addr, ldpc_cfg_addr, agc_cfg_addr, txgain_cfg_addr);
+
+ for (cnt = 0; cnt < patch_tbl_wifisetting_num; cnt++) {
+ if ((ret = rwnx_send_dbg_mem_write_req(rwnx_hw, wifisetting_cfg_addr + patch_tbl_wifisetting[cnt][0], patch_tbl_wifisetting[cnt][1]))) {
+ AICWFDBG(LOGERROR, "wifisetting %x write fail\n", patch_tbl_wifisetting[cnt][0]);
+ }
+ }
+
+//adap test
+ if(adap_test){
+ adap_patch_num = sizeof(adaptivity_patch_tbl)/sizeof(u32)/2;
+ for(cnt = 0; cnt < adap_patch_num; cnt++)
+ {
+ if((ret = rwnx_send_dbg_mem_write_req(rwnx_hw, wifisetting_cfg_addr + adaptivity_patch_tbl[cnt][0], adaptivity_patch_tbl[cnt][1]))) {
+ AICWFDBG(LOGERROR, "%x write fail\n", wifisetting_cfg_addr + adaptivity_patch_tbl[cnt][0]);
+ }
+ }
+ }
+//adap test
+
+ if (ldpc_cfg_size > 512) {// > 0.5KB data
+ for (i = 0; i < (ldpc_cfg_size - 512); i += 512) {//each time write 0.5KB
+ ret = rwnx_send_dbg_mem_block_write_req(rwnx_hw, ldpc_cfg_addr + i, 512, ldpc_cfg_ram + i / 4);
+ if (ret) {
+ AICWFDBG(LOGERROR, "ldpc upload fail: %x, err:%d\r\n", ldpc_cfg_addr + i, ret);
+ break;
+ }
+ }
+ }
+
+ if (!ret && (i < ldpc_cfg_size)) {// < 0.5KB data
+ ret = rwnx_send_dbg_mem_block_write_req(rwnx_hw, ldpc_cfg_addr + i, ldpc_cfg_size - i, ldpc_cfg_ram + i / 4);
+ if (ret) {
+ AICWFDBG(LOGERROR, "ldpc upload fail: %x, err:%d\r\n", ldpc_cfg_addr + i, ret);
+ }
+ }
+
+ if (agc_cfg_size > 512) {// > 0.5KB data
+ for (i = 0; i < (agc_cfg_size - 512); i += 512) {//each time write 0.5KB
+ ret = rwnx_send_dbg_mem_block_write_req(rwnx_hw, agc_cfg_addr + i, 512, agc_cfg_ram + i / 4);
+ if (ret) {
+ AICWFDBG(LOGERROR, "agc upload fail: %x, err:%d\r\n", agc_cfg_addr + i, ret);
+ break;
+ }
+ }
+ }
+
+ if (!ret && (i < agc_cfg_size)) {// < 0.5KB data
+ ret = rwnx_send_dbg_mem_block_write_req(rwnx_hw, agc_cfg_addr + i, agc_cfg_size - i, agc_cfg_ram + i / 4);
+ if (ret) {
+ AICWFDBG(LOGERROR, "agc upload fail: %x, err:%d\r\n", agc_cfg_addr + i, ret);
+ }
+ }
+
+ #if !defined(CONFIG_FPGA_VERIFICATION)
+ ret = rwnx_send_dbg_mem_block_write_req(rwnx_hw, txgain_cfg_addr, txgain_cfg_size, txgain_map);
+ if (ret) {
+ AICWFDBG(LOGERROR, "txgain upload fail: %x, err:%d\r\n", txgain_cfg_addr, ret);
+ }
+
+ if(chip_sub_id == 0 ){
+ for (cnt = 0; cnt < jump_tbl_size/4; cnt+=1) {
+ AICWFDBG(LOGDEBUG, "%x = %x\n", jump_tbl_base[cnt][0]*4+jump_tbl_addr, jump_tbl_base[cnt][1]);
+ if ((ret = rwnx_send_dbg_mem_write_req(rwnx_hw, jump_tbl_base[cnt][0]*4+jump_tbl_addr, jump_tbl_base[cnt][1]))) {
+ AICWFDBG(LOGERROR, "%x write fail\n", jump_tbl_addr+8*cnt);
+ }
+ }
+ for (cnt = 0; cnt < patch_tbl_func_num; cnt++) {
+ if ((ret = rwnx_send_dbg_mem_write_req(rwnx_hw, patch_tbl_func_base[cnt][0], patch_tbl_func_base[cnt][1]))) {
+ AICWFDBG(LOGERROR, "patch_tbl_func %x write fail\n", patch_tbl_func_base[cnt][0]);
+ }
+ }
+ }
+ else{
+ ret = aicwf_patch_table_load(rwnx_hw, RWNX_MAC_PATCH_TABLE_8800DC_U02);
+ if(ret){
+ printk("patch_tbl upload fail: err:%d\r\n", ret);
+ }
+#ifdef CONFIG_FOR_IPCAM
+ if ((ret = rwnx_send_dbg_mem_write_req(rwnx_hw, 0x00111944, 0x00000101))) {
+ AICWFDBG(LOGERROR, "patch_tbl_func %x write fail\n", patch_tbl_func_base[cnt][0]);
+ }
+#endif
+ }
+
+ #endif
+ } else {
+ if (chip_sub_id == 0) {
+ u32 patch_tbl_rf_func_num = sizeof(patch_tbl_rf_func)/sizeof(u32)/2;
+ for (cnt = 0; cnt < patch_tbl_rf_func_num; cnt++) {
+ if ((ret = rwnx_send_dbg_mem_write_req(rwnx_hw, patch_tbl_rf_func[cnt][0], patch_tbl_rf_func[cnt][1]))) {
+ AICWFDBG(LOGERROR, "patch_tbl_rf_func %x write fail\n", patch_tbl_rf_func[cnt][0]);
+ }
+ }
+ }
+ }
+ #endif
+}
+
+
+
+int aicwf_set_rf_config_8800dc(struct rwnx_hw *rwnx_hw, struct mm_set_rf_calib_cfm *cfm){
+ int ret = 0;
+
+ if ((ret = rwnx_send_txpwr_lvl_req(rwnx_hw))) {
+ return -1;
+ }
+
+ if ((ret = rwnx_send_txpwr_ofst_req(rwnx_hw))) {
+ return -1;
+ }
+
+
+ if (testmode == FW_NORMAL_MODE) {
+ if ((ret = rwnx_send_rf_config_req(rwnx_hw, 0, 1, (u8_l *)wifi_txgain_table_24g_8800dcdw, 128)))
+ return -1;
+
+ if ((ret = rwnx_send_rf_config_req(rwnx_hw, 16, 1, (u8_l *)wifi_txgain_table_24g_1_8800dcdw, 128)))
+ return -1;
+
+ if ((ret = rwnx_send_rf_config_req(rwnx_hw, 0, 0, (u8_l *)wifi_rxgain_table_24g_20m_8800dcdw, 256)))
+ return -1;
+
+ if ((ret = rwnx_send_rf_config_req(rwnx_hw, 32, 0, (u8_l *)wifi_rxgain_table_24g_40m_8800dcdw, 256)))
+ return -1;
+
+ if ((ret = rwnx_send_rf_calib_req(rwnx_hw, cfm))) {
+ return -1;
+ }
+ } else if (testmode == FW_RFTEST_MODE) {
+#ifdef CONFIG_DPD
+ if (is_file_exist(FW_DPDRESULT_NAME_8800DC) == 1) {
+ AICWFDBG(LOGINFO, "%s load dpd bin\n", __func__);
+ ret = aicwf_dpd_result_load_8800dc(rwnx_hw);
+ if (ret) {
+ AICWFDBG(LOGINFO, "load dpd bin fail: %d\n", ret);
+ return ret;
+ }
+ ret = rwnx_send_rf_calib_req(rwnx_hw, cfm);
+ if (ret) {
+ AICWFDBG(LOGINFO, "rf calib req fail: %d\n", ret);
+ return ret;
+ }
+ }
+#endif
+ }
+
+ return 0 ;
+}
+
+extern char aic_fw_path[200];
+
+int aicwf_plat_patch_load_8800dc(struct rwnx_hw *rwnx_hw){
+ int ret = 0;
+
+ if (testmode == 0 || testmode == 4) {
+#if !defined(CONFIG_FPGA_VERIFICATION)
+ if (chip_sub_id == 0) {
+ ret = rwnx_plat_bin_fw_upload_2(rwnx_hw, ROM_FMAC_PATCH_ADDR, RWNX_MAC_PATCH_NAME2_8800DC);
+ } else if (chip_sub_id == 1) {
+ ret = rwnx_plat_bin_fw_upload_2(rwnx_hw, ROM_FMAC_PATCH_ADDR, RWNX_MAC_PATCH_NAME2_8800DC_U02);
+ } else {
+ printk("unsupported id: %d\n", chip_sub_id);
+ }
+#endif
+ } else {
+ if (chip_sub_id == 0) {
+ ret = rwnx_plat_bin_fw_upload_2(rwnx_hw, ROM_FMAC_PATCH_ADDR, RWNX_MAC_RF_PATCH_NAME_8800DC);
+ }
+ if (!ret) {
+ ret = rwnx_plat_bin_fw_upload_2(rwnx_hw, RAM_LMAC_FW_ADDR, RWNX_MAC_FW_RF_BASE_NAME_8800DC);
+ }
+ }
+
+ return ret;
+}
+
+int aicwf_misc_ram_init_8800dc(struct rwnx_hw *rwnx_hw)
+{
+ int ret = 0;
+ const uint32_t cfg_base = 0x10164;
+ struct dbg_mem_read_cfm cfm;
+ uint32_t misc_ram_addr;
+ uint32_t misc_ram_size = 12;
+ int i;
+ // init misc ram
+ ret = rwnx_send_dbg_mem_read_req(rwnx_hw, cfg_base + 0x14, &cfm);
+ if (ret) {
+ AICWFDBG(LOGERROR, "rf misc ram[0x%x] rd fail: %d\n", cfg_base + 0x14, ret);
+ return ret;
+ }
+ misc_ram_addr = cfm.memdata;
+ AICWFDBG(LOGERROR, "misc_ram_addr=%x\n", misc_ram_addr);
+ for (i = 0; i < (misc_ram_size / 4); i++) {
+ ret = rwnx_send_dbg_mem_write_req(rwnx_hw, misc_ram_addr + i * 4, 0);
+ if (ret) {
+ AICWFDBG(LOGERROR, "rf misc ram[0x%x] wr fail: %d\n", misc_ram_addr + i * 4, ret);
+ return ret;
+ }
+ }
+ return ret;
+}
+
+#ifdef CONFIG_DPD
+int aicwf_dpd_calib_8800dc(struct rwnx_hw *rwnx_hw, uint32_t *dpd_res)
+{
+ int ret = 0;
+ uint32_t fw_addr, boot_type;
+ ret = aicwf_plat_patch_load_8800dc(rwnx_hw);
+ if (ret) {
+ AICWFDBG(LOGINFO, "load patch bin fail: %d\n", ret);
+ return ret;
+ }
+ ret = rwnx_plat_bin_fw_upload_2(rwnx_hw, ROM_FMAC_CALIB_ADDR, RWNX_MAC_CALIB_NAME_8800DC_U02);
+ if (ret) {
+ AICWFDBG(LOGINFO, "load calib bin fail: %d\n", ret);
+ return ret;
+ }
+ /* fw start */
+ fw_addr = 0x00130009;
+ boot_type = HOST_START_APP_FNCALL;
+ AICWFDBG(LOGINFO, "Start app: %08x, %d\n", fw_addr, boot_type);
+ ret = rwnx_send_dbg_start_app_req(rwnx_hw, fw_addr, boot_type);
+ if (ret) {
+ AICWFDBG(LOGINFO, "start app fail: %d\n", ret);
+ return ret;
+ }
+ { // read dpd res
+ const uint32_t cfg_base = 0x10164;
+ struct dbg_mem_read_cfm cfm;
+ uint32_t misc_ram_addr;
+ uint32_t misc_ram_size = DPD_RESULT_SIZE_8800DC;
+ int i;
+ ret = rwnx_send_dbg_mem_read_req(rwnx_hw, cfg_base + 0x14, &cfm);
+ if (ret) {
+ AICWFDBG(LOGERROR, "rf misc ram[0x%x] rd fail: %d\n", cfg_base + 0x14, ret);
+ return ret;
+ }
+ misc_ram_addr = cfm.memdata;
+ for (i = 0; i < (misc_ram_size / 4); i++) {
+ ret = rwnx_send_dbg_mem_read_req(rwnx_hw, misc_ram_addr + i * 4, &cfm);
+ if (ret) {
+ AICWFDBG(LOGERROR, "rf misc ram[0x%x] rd fail: %d\n", misc_ram_addr + i * 4, ret);
+ return ret;
+ }
+ dpd_res[i] = cfm.memdata;
+ }
+ }
+ return ret;
+}
+
+int aicwf_dpd_result_load_8800dc(struct rwnx_hw *rwnx_hw)
+{
+ int ret = 0;
+ uint32_t cfg_base = 0x10164;
+ struct dbg_mem_read_cfm cfm;
+ uint32_t misc_ram_addr;
+ if (testmode == FW_RFTEST_MODE) {
+ cfg_base = RAM_LMAC_FW_ADDR + 0x0164;
+ }
+ if ((ret = rwnx_send_dbg_mem_read_req(rwnx_hw, cfg_base + 0x14, &cfm))) {
+ AICWFDBG(LOGERROR, "rf misc ram[0x%x] rd fail: %d\n", cfg_base + 0x14, ret);
+ return ret;
+ }
+ misc_ram_addr = cfm.memdata;
+ ret = rwnx_plat_bin_fw_upload_2(rwnx_hw, misc_ram_addr, FW_DPDRESULT_NAME_8800DC);
+ if (ret) {
+ AICWFDBG(LOGINFO, "load calib bin fail: %d\n", ret);
+ return ret;
+ }
+ return ret;
+}
+
+#define FW_PATH_MAX_LEN 200
+extern char aic_fw_path[FW_PATH_MAX_LEN];
+
+int aicwf_dpd_result_write_8800dc(void *buf, int buf_len)
+{
+ AICWFDBG(LOGINFO, "%s\n", __func__);
+ int sum = 0, len = 0;
+ char *path = NULL;
+ struct file *fp = NULL;
+ loff_t pos = 0;
+ mm_segment_t fs;
+
+ path = __getname();
+ if (!path) {
+ AICWFDBG(LOGINFO, "get path fail\n");
+ return -1;
+ }
+
+ len = snprintf(path, FW_PATH_MAX_LEN, "%s/%s", aic_fw_path, FW_DPDRESULT_NAME_8800DC);
+ //AICWFDBG(LOGINFO, "%s\n", path);
+
+ fp = filp_open(path, O_RDWR | O_APPEND | O_CREAT, 0644);
+ if (IS_ERR(fp)) {
+ AICWFDBG(LOGINFO, "fp open fial\n");
+ __putname(path);
+ fp = NULL;
+ return -1;
+ }
+
+ fs = get_fs();
+ set_fs(KERNEL_DS);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0)
+ sum = kernel_write(fp, buf, buf_len, &pos);
+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
+ sum = kernel_write(fp, (char *)buf, buf_len, pos);
+#else
+ sum = vfs_write(fp, (char *)buf, buf_len, &pos);
+#endif
+
+ set_fs(fs);
+ __putname(path);
+ filp_close(fp, NULL);
+ fp = NULL;
+
+ return 0;
+}
+#endif
+
+int rwnx_plat_userconfig_load_8800dc(struct rwnx_hw *rwnx_hw){
+ int size;
+ u32 *dst=NULL;
+ char *filename = FW_USERCONFIG_NAME_8800DC;
+
+ AICWFDBG(LOGINFO, "userconfig file path:%s \r\n", filename);
+
+ /* load file */
+ size = rwnx_request_firmware_common(rwnx_hw, &dst, filename);
+ if (size <= 0) {
+ AICWFDBG(LOGERROR, "wrong size of firmware file\n");
+ dst = NULL;
+ return 0;
+ }
+
+ /* Copy the file on the Embedded side */
+ AICWFDBG(LOGINFO, "### Load file done: %s, size=%d\n", filename, size);
+
+ rwnx_plat_userconfig_parsing((char *)dst, size);
+
+ rwnx_release_firmware_common(&dst);
+
+ AICWFDBG(LOGINFO, "userconfig download complete\n\n");
+ return 0;
+
+}
+
+int rwnx_plat_userconfig_load_8800dw(struct rwnx_hw *rwnx_hw){
+ int size;
+ u32 *dst=NULL;
+ char *filename = FW_USERCONFIG_NAME_8800DW;
+
+ AICWFDBG(LOGINFO, "userconfig file path:%s \r\n", filename);
+
+ /* load file */
+ size = rwnx_request_firmware_common(rwnx_hw, &dst, filename);
+ if (size <= 0) {
+ AICWFDBG(LOGERROR, "wrong size of firmware file\n");
+ dst = NULL;
+ return 0;
+ }
+
+ /* Copy the file on the Embedded side */
+ AICWFDBG(LOGINFO, "### Load file done: %s, size=%d\n", filename, size);
+
+ rwnx_plat_userconfig_parsing((char *)dst, size);
+
+ rwnx_release_firmware_common(&dst);
+
+ AICWFDBG(LOGINFO, "userconfig download complete\n\n");
+ return 0;
+
+}
+
+
+void system_config_8800dc(struct rwnx_hw *rwnx_hw){
+ int syscfg_num;
+ int ret, cnt;
+ const u32 mem_addr = 0x40500000;
+ struct dbg_mem_read_cfm rd_mem_addr_cfm;
+
+ ret = rwnx_send_dbg_mem_read_req(rwnx_hw, mem_addr, &rd_mem_addr_cfm);
+ if (ret) {
+ AICWFDBG(LOGERROR, "%x rd fail: %d\n", mem_addr, ret);
+ return;
+ }
+ chip_id = (u8)(rd_mem_addr_cfm.memdata >> 16);
+ //printk("%x=%x\n", rd_mem_addr_cfm.memaddr, rd_mem_addr_cfm.memdata);
+ if (((rd_mem_addr_cfm.memdata >> 25) & 0x01UL) == 0x00UL) {
+ chip_mcu_id = 1;
+ }
+
+ ret = rwnx_send_dbg_mem_read_req(rwnx_hw, 0x00000020, &rd_mem_addr_cfm);
+ if (ret) {
+ AICWFDBG(LOGERROR, "[0x00000020] rd fail: %d\n", ret);
+ return;
+ }
+ chip_sub_id = (u8)(rd_mem_addr_cfm.memdata);
+ //printk("%x=%x\n", rd_mem_addr_cfm.memaddr, rd_mem_addr_cfm.memdata);
+ AICWFDBG(LOGINFO, "chip_id=%x, chip_sub_id=%x\n", chip_id, chip_sub_id);
+
+
+ ret = rwnx_send_dbg_mem_read_req(rwnx_hw, 0x40500010, &rd_mem_addr_cfm);
+ printk("[0x40500010]=%x\n", rd_mem_addr_cfm.memdata);
+ if (ret) {
+ printk("[0x40500010] rd fail: %d\n", ret);
+ return;
+ }
+
+ syscfg_num = sizeof(syscfg_tbl_8800dc) / sizeof(u32) / 2;
+
+ for (cnt = 0; cnt < syscfg_num; cnt++) {
+ ret = rwnx_send_dbg_mem_write_req(rwnx_hw, syscfg_tbl_8800dc[cnt][0], syscfg_tbl_8800dc[cnt][1]);
+ if (ret) {
+ AICWFDBG(LOGERROR, "%x write fail: %d\n", syscfg_tbl_8800dc[cnt][0], ret);
+ return;
+ }
+ }
+
+ syscfg_num = sizeof(syscfg_tbl_masked_8800dc) / sizeof(u32) / 3;
+
+
+ for (cnt = 0; cnt < syscfg_num; cnt++) {
+ if (syscfg_tbl_masked_8800dc[cnt][0] == 0x00000000) {
+ break;
+ } else if (syscfg_tbl_masked_8800dc[cnt][0] == 0x70001000) {
+ if (chip_mcu_id == 0) {
+ syscfg_tbl_masked_8800dc[cnt][1] |= ((0x1 << 8) | (0x1 << 15)); // mask
+ syscfg_tbl_masked_8800dc[cnt][2] |= ((0x1 << 8) | (0x1 << 15));
+ }
+ }
+
+ ret = rwnx_send_dbg_mem_mask_write_req(rwnx_hw,
+ syscfg_tbl_masked_8800dc[cnt][0], syscfg_tbl_masked_8800dc[cnt][1], syscfg_tbl_masked_8800dc[cnt][2]);
+ if (ret) {
+ AICWFDBG(LOGERROR, "%x mask write fail: %d\n", syscfg_tbl_masked_8800dc[cnt][0], ret);
+ return;
+ }
+ }
+
+ if (chip_sub_id == 0) {
+ syscfg_num = sizeof(syscfg_tbl_masked_8800dc_u01) / sizeof(u32) / 3;
+ for (cnt = 0; cnt < syscfg_num; cnt++) {
+ ret = rwnx_send_dbg_mem_mask_write_req(rwnx_hw,
+ syscfg_tbl_masked_8800dc_u01[cnt][0], syscfg_tbl_masked_8800dc_u01[cnt][1], syscfg_tbl_masked_8800dc_u01[cnt][2]);
+ if (ret) {
+ AICWFDBG(LOGERROR, "%x mask write fail: %d\n", syscfg_tbl_masked_8800dc_u01[cnt][0], ret);
+ return;
+ }
+ }
+ }
+
+}
+
diff --git a/drivers/net/wireless/aic8800/aic8800_fdrv/aicwf_compat_8800dc.h b/drivers/net/wireless/aic8800/aic8800_fdrv/aicwf_compat_8800dc.h
new file mode 100644
index 000000000000..8a9184f750d1
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic8800_fdrv/aicwf_compat_8800dc.h
@@ -0,0 +1,16 @@
+#include <linux/types.h>
+
+#define DPD_RESULT_SIZE_8800DC 1880
+int aicwf_patch_table_load(struct rwnx_hw *rwnx_hw, char *filename);
+void aicwf_patch_config_8800dc(struct rwnx_hw *rwnx_hw);
+int aicwf_set_rf_config_8800dc(struct rwnx_hw *rwnx_hw, struct mm_set_rf_calib_cfm *cfm);
+int aicwf_misc_ram_init_8800dc(struct rwnx_hw *rwnx_hw);
+#ifdef CONFIG_DPD
+int aicwf_dpd_calib_8800dc(struct rwnx_hw *rwnx_hw, uint32_t *dpd_res);
+int aicwf_dpd_result_load_8800dc(struct rwnx_hw *rwnx_hw);
+int aicwf_dpd_result_write_8800dc(void *buf, int buf_len);
+#endif
+int aicwf_plat_patch_load_8800dc(struct rwnx_hw *rwnx_hw);
+int rwnx_plat_userconfig_load_8800dc(struct rwnx_hw *rwnx_hw);
+int rwnx_plat_userconfig_load_8800dw(struct rwnx_hw *rwnx_hw);
+void system_config_8800dc(struct rwnx_hw *rwnx_hw);
diff --git a/drivers/net/wireless/aic8800/aic8800_fdrv/aicwf_debug.h b/drivers/net/wireless/aic8800/aic8800_fdrv/aicwf_debug.h
new file mode 100644
index 000000000000..12fdca688b18
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic8800_fdrv/aicwf_debug.h
@@ -0,0 +1,52 @@
+
+
+#define RWNX_FN_ENTRY_STR ">>> %s()\n", __func__
+
+
+
+/* message levels */
+#define LOGERROR 0x0001
+#define LOGINFO 0x0002
+#define LOGTRACE 0x0004
+#define LOGDEBUG 0x0008
+#define LOGDATA 0x0010
+
+extern int aicwf_dbg_level;
+void rwnx_data_dump(char* tag, void* data, unsigned long len);
+
+#define AICWF_LOG "AICWFDBG("
+
+#define AICWFDBG(level, args, arg...) \
+do { \
+ if (aicwf_dbg_level & level) { \
+ printk(AICWF_LOG#level")\t" args, ##arg); \
+ } \
+} while (0)
+
+#define RWNX_DBG(fmt, ...) \
+do { \
+ if (aicwf_dbg_level & LOGTRACE) { \
+ printk(AICWF_LOG"LOGTRACE)\t"fmt , ##__VA_ARGS__); \
+ } \
+} while (0)
+
+
+
+#if 0
+#define RWNX_DBG(fmt, ...) \
+ do { \
+ if (aicwf_dbg_level & LOGTRACE) { \
+ printk(AICWF_LOG"LOGTRACE"")\t" fmt, ##__VA_ARGS__); \
+ } \
+ } while (0)
+#define AICWFDBG(args, level) \
+do { \
+ if (aicwf_dbg_level & level) { \
+ printk(AICWF_LOG"(%s)\t" ,#level); \
+ printf args; \
+ } \
+} while (0)
+#endif
+
+
+
diff --git a/drivers/net/wireless/aic8800/aic8800_fdrv/aicwf_rx_prealloc.h b/drivers/net/wireless/aic8800/aic8800_fdrv/aicwf_rx_prealloc.h
new file mode 100644
index 000000000000..a19bbe9a4578
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic8800_fdrv/aicwf_rx_prealloc.h
@@ -0,0 +1,27 @@
+
+#ifndef _AICWF_RX_PREALLOC_H_
+#define _AICWF_RX_PREALLOC_H_
+
+#ifdef CONFIG_PREALLOC_RX_SKB
+
+struct rx_buff {
+ struct list_head queue;
+ unsigned char *data;
+ u32 len;
+ uint8_t *start;
+ uint8_t *end;
+ uint8_t *read;
+};
+
+struct aicwf_rx_buff_list {
+ struct list_head rxbuff_list;
+ atomic_t rxbuff_list_len;
+};
+
+extern struct rx_buff *aicwf_prealloc_rxbuff_alloc(spinlock_t *lock);
+extern void aicwf_prealloc_rxbuff_free(struct rx_buff *rxbuff, spinlock_t *lock);
+extern int aicwf_prealloc_init(void);
+extern void aicwf_prealloc_exit(void);
+extern int aicwf_rxbuff_size_get(void);
+#endif
+#endif /* _AICWF_RX_PREALLOC_H_ */ \ No newline at end of file
diff --git a/drivers/net/wireless/aic8800/aic8800_fdrv/aicwf_sdio.c b/drivers/net/wireless/aic8800/aic8800_fdrv/aicwf_sdio.c
new file mode 100644
index 000000000000..1a96f4523f36
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic8800_fdrv/aicwf_sdio.c
@@ -0,0 +1,1251 @@
+/**
+ * aicwf_sdmmc.c
+ *
+ * SDIO function declarations
+ *
+ * Copyright (C) AICSemi 2018-2020
+ */
+#include <linux/interrupt.h>
+#include <linux/completion.h>
+#include <linux/mmc/sdio.h>
+#include <linux/mmc/sdio_ids.h>
+#include <linux/mmc/sdio_func.h>
+#include <linux/mmc/card.h>
+#include <linux/mmc/host.h>
+#include <linux/semaphore.h>
+#include <linux/debugfs.h>
+#include <linux/kthread.h>
+#include "aicwf_txrxif.h"
+#include "aicwf_sdio.h"
+#include "sdio_host.h"
+#include "rwnx_defs.h"
+#include "rwnx_platform.h"
+#ifdef CONFIG_INGENIC_T20
+#include "mach/jzmmc.h"
+#endif /* CONFIG_INGENIC_T20 */
+extern uint8_t scanning;
+
+#ifdef CONFIG_PLATFORM_ALLWINNER
+void platform_wifi_power_off(void);
+#endif
+
+int aicwf_sdio_readb(struct aic_sdio_dev *sdiodev, uint regaddr, u8 *val)
+{
+ int ret;
+ sdio_claim_host(sdiodev->func);
+ *val = sdio_readb(sdiodev->func, regaddr, &ret);
+ sdio_release_host(sdiodev->func);
+ return ret;
+}
+
+int aicwf_sdio_writeb(struct aic_sdio_dev *sdiodev, uint regaddr, u8 val)
+{
+ int ret;
+ sdio_claim_host(sdiodev->func);
+ sdio_writeb(sdiodev->func, val, regaddr, &ret);
+ sdio_release_host(sdiodev->func);
+ return ret;
+}
+
+int aicwf_sdio_flow_ctrl(struct aic_sdio_dev *sdiodev)
+{
+ int ret = -1;
+ u8 fc_reg = 0;
+ u32 count = 0;
+
+ while (true) {
+ ret = aicwf_sdio_readb(sdiodev, SDIOWIFI_FLOW_CTRL_REG, &fc_reg);
+ if (ret) {
+ return -1;
+ }
+
+ if ((fc_reg & SDIOWIFI_FLOWCTRL_MASK_REG) != 0) {
+ ret = fc_reg & SDIOWIFI_FLOWCTRL_MASK_REG;
+ return ret;
+ } else {
+ if (count >= FLOW_CTRL_RETRY_COUNT) {
+ ret = -fc_reg;
+ break;
+ }
+ count++;
+ if (count < 30)
+ udelay(200);
+ else if(count < 40)
+ mdelay(1);
+ else
+ mdelay(10);
+ }
+ }
+
+ return ret;
+}
+
+int aicwf_sdio_send_pkt(struct aic_sdio_dev *sdiodev, u8 *buf, uint count)
+{
+ int ret = 0;
+
+ sdio_claim_host(sdiodev->func);
+ ret = sdio_writesb(sdiodev->func, 7, buf, count);
+ sdio_release_host(sdiodev->func);
+
+ return ret;
+}
+
+int aicwf_sdio_recv_pkt(struct aic_sdio_dev *sdiodev, struct sk_buff *skbbuf,
+ u32 size)
+{
+ int ret;
+
+ if ((!skbbuf) || (!size)) {
+ return -EINVAL;;
+ }
+
+ sdio_claim_host(sdiodev->func);
+ ret = sdio_readsb(sdiodev->func, skbbuf->data, 8, size);
+ sdio_release_host(sdiodev->func);
+
+ if (ret < 0) {
+ return ret;
+ }
+ skbbuf->len = size;
+
+ return ret;
+}
+
+static int aicwf_sdio_probe(struct sdio_func *func,
+ const struct sdio_device_id *id)
+{
+ struct mmc_host *host;
+ struct aic_sdio_dev *sdiodev;
+ struct aicwf_bus *bus_if;
+ int err = -ENODEV;
+
+ sdio_dbg("%s:%d\n", __func__, func->num);
+ host = func->card->host;
+ if(func->num != 1) {
+ return err;
+ }
+
+ bus_if = kzalloc(sizeof(struct aicwf_bus), GFP_KERNEL);
+ if (!bus_if) {
+ sdio_err("alloc bus fail\n");
+ return -ENOMEM;
+ }
+
+ sdiodev = kzalloc(sizeof(struct aic_sdio_dev), GFP_KERNEL);
+ if (!sdiodev) {
+ sdio_err("alloc sdiodev fail\n");
+ kfree(bus_if);
+ return -ENOMEM;
+ }
+
+ sdiodev->func = func;
+ sdiodev->bus_if = bus_if;
+ bus_if->bus_priv.sdio = sdiodev;
+ dev_set_drvdata(&func->dev, bus_if);
+ sdiodev->dev = &func->dev;
+ err = aicwf_sdio_func_init(sdiodev);
+ if (err < 0) {
+ sdio_err("sdio func init fail\n");
+ goto fail;
+ }
+
+ aicwf_sdio_bus_init(sdiodev);
+ host->caps |= MMC_CAP_NONREMOVABLE;
+ aicwf_rwnx_sdio_platform_init(sdiodev);
+ aicwf_hostif_ready();
+
+ return 0;
+fail:
+ aicwf_sdio_func_deinit(sdiodev);
+ dev_set_drvdata(&func->dev, NULL);
+ kfree(sdiodev);
+ kfree(bus_if);
+ return err;
+}
+
+static void aicwf_sdio_remove(struct sdio_func *func)
+{
+ struct mmc_host *host;
+ struct aicwf_bus *bus_if = NULL;
+ struct aic_sdio_dev *sdiodev = NULL;
+
+ sdio_dbg("%s\n", __func__);
+ host = func->card->host;
+ host->caps &= ~MMC_CAP_NONREMOVABLE;
+ bus_if = dev_get_drvdata(&func->dev);
+ if (!bus_if) {
+ return;
+ }
+
+ sdiodev = bus_if->bus_priv.sdio;
+ if (!sdiodev) {
+ return;
+ }
+ sdiodev->bus_if->state = BUS_DOWN_ST;
+ aicwf_sdio_release(sdiodev);
+ aicwf_sdio_func_deinit(sdiodev);
+ dev_set_drvdata(&sdiodev->func->dev, NULL);
+ kfree(sdiodev);
+ kfree(bus_if);
+ sdio_dbg("%s done\n", __func__);
+#ifdef CONFIG_PLATFORM_ALLWINNER
+ platform_wifi_power_off();
+#endif
+}
+
+static int aicwf_sdio_suspend(struct device *dev)
+{
+ int ret = 0;
+ struct aicwf_bus *bus_if = dev_get_drvdata(dev);
+ struct aic_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
+ mmc_pm_flag_t sdio_flags;
+ struct rwnx_vif *rwnx_vif, *tmp;
+
+ sdio_dbg("%s\n", __func__);
+ list_for_each_entry_safe(rwnx_vif, tmp, &sdiodev->rwnx_hw->vifs, list) {
+ if(rwnx_vif->ndev)
+ netif_device_detach(rwnx_vif->ndev);
+ }
+
+ sdio_flags = sdio_get_host_pm_caps(sdiodev->func);
+ if (!(sdio_flags & MMC_PM_KEEP_POWER)) {
+ return -EINVAL;
+ }
+ ret = sdio_set_host_pm_flags(sdiodev->func, MMC_PM_KEEP_POWER);
+ if (ret) {
+ return ret;
+ }
+
+ while (sdiodev->state == SDIO_ACTIVE_ST) {
+ if (down_interruptible(&sdiodev->tx_priv->txctl_sema)){
+ continue;
+ }
+ aicwf_sdio_pwr_stctl(sdiodev, SDIO_SLEEP_ST);
+ up(&sdiodev->tx_priv->txctl_sema);
+ break;
+ }
+
+ return 0;
+}
+
+static int aicwf_sdio_resume(struct device *dev)
+{
+ struct aicwf_bus *bus_if = dev_get_drvdata(dev);
+ struct aic_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
+ struct rwnx_vif *rwnx_vif, *tmp;
+
+ sdio_dbg("%s\n", __func__);
+ list_for_each_entry_safe(rwnx_vif, tmp, &sdiodev->rwnx_hw->vifs, list) {
+ if(rwnx_vif->ndev)
+ netif_device_attach(rwnx_vif->ndev);
+ }
+
+ return 0;
+}
+
+static const struct sdio_device_id aicwf_sdmmc_ids[] = {
+ {SDIO_DEVICE_CLASS(SDIO_CLASS_WLAN)},
+ { },
+};
+
+MODULE_DEVICE_TABLE(sdio, aicwf_sdmmc_ids);
+
+static const struct dev_pm_ops aicwf_sdio_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(aicwf_sdio_suspend, aicwf_sdio_resume)
+};
+
+static struct sdio_driver aicwf_sdio_driver = {
+ .probe = aicwf_sdio_probe,
+ .remove = aicwf_sdio_remove,
+ .name = AICWF_SDIO_NAME,
+ .id_table = aicwf_sdmmc_ids,
+ .drv = {
+ .pm = &aicwf_sdio_pm_ops,
+ },
+};
+
+#ifdef CONFIG_NANOPI_M4
+ extern int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq);
+ extern unsigned aic_max_freqs;
+ extern struct mmc_host* aic_host_drv;
+ extern int __mmc_claim_host(struct mmc_host *host, atomic_t *abort);
+ extern void mmc_release_host(struct mmc_host *host);
+#endif
+#ifdef CONFIG_PLATFORM_ALLWINNER
+extern void sunxi_mmc_rescan_card(unsigned ids);
+extern void sunxi_wlan_set_power(int on);
+extern int sunxi_wlan_get_bus_index(void);
+
+int platform_wifi_power_on(void)
+{
+ int ret=0;
+ int wlan_bus_index=sunxi_wlan_get_bus_index();
+ if(wlan_bus_index < 0)
+ return wlan_bus_index;
+
+ sunxi_wlan_set_power(1);
+ mdelay(1000);
+ sunxi_mmc_rescan_card(wlan_bus_index);
+
+ printk("platform_wifi_power_on");
+
+ return ret;
+}
+
+void platform_wifi_power_off(void)
+{
+ int wlan_bus_index = sunxi_wlan_get_bus_index();
+ if(wlan_bus_index < 0) {
+ printk("no wlan_bus_index\n");
+ return ;
+ }
+ printk("power_off\n");
+ sunxi_wlan_set_power(0);
+ mdelay(100);
+ //sunxi_mmc_rescan_card(wlan_bus_index);
+
+ printk("platform_wifi_power_off");
+}
+#endif
+void aicwf_sdio_register(void)
+{
+#ifdef CONFIG_PLATFORM_NANOPI
+ extern_wifi_set_enable(0);
+ mdelay(200);
+ extern_wifi_set_enable(1);
+ mdelay(200);
+ sdio_reinit();
+#endif /*CONFIG_PLATFORM_NANOPI*/
+
+#ifdef CONFIG_INGENIC_T20
+ jzmmc_manual_detect(1, 1);
+#endif /* CONFIG_INGENIC_T20 */
+
+#ifdef CONFIG_NANOPI_M4
+ if(aic_host_drv->card == NULL){
+ __mmc_claim_host(aic_host_drv,NULL);
+ printk("aic: >>>mmc_rescan_try_freq\n");
+ mmc_rescan_try_freq(aic_host_drv,aic_max_freqs);
+ mmc_release_host(aic_host_drv);
+ }
+#endif
+#ifdef CONFIG_PLATFORM_ALLWINNER
+ platform_wifi_power_on();
+#endif
+ if (sdio_register_driver(&aicwf_sdio_driver)) {
+
+ } else {
+ //may add mmc_rescan here
+ }
+}
+
+void aicwf_sdio_exit(void)
+{
+ if(g_rwnx_plat && g_rwnx_plat->enabled)
+ rwnx_platform_deinit(g_rwnx_plat->sdiodev->rwnx_hw);
+
+ sdio_unregister_driver(&aicwf_sdio_driver);
+
+#ifdef CONFIG_PLATFORM_NANOPI
+ extern_wifi_set_enable(0);
+#endif /*CONFIG_PLATFORM_NANOPI*/
+ kfree(g_rwnx_plat);
+}
+
+int aicwf_sdio_wakeup(struct aic_sdio_dev *sdiodev)
+{
+ int ret = 0;
+ int read_retry;
+ int write_retry = 20;
+
+ if (sdiodev->state == SDIO_SLEEP_ST) {
+ down(&sdiodev->pwrctl_wakeup_sema);
+ if (sdiodev->rwnx_hw->vif_started) {
+ if(sdiodev->state == SDIO_ACTIVE_ST) {
+ up(&sdiodev->pwrctl_wakeup_sema);
+ return 0;
+ }
+ sdio_dbg("w\n");
+ while(write_retry) {
+ ret = aicwf_sdio_writeb(sdiodev, SDIOWIFI_WAKEUP_REG, 1);
+ if (ret) {
+ txrx_err("sdio wakeup fail\n");
+ ret = -1;
+ } else {
+ read_retry=10;
+ while (read_retry) {
+ u8 val;
+ ret = aicwf_sdio_readb(sdiodev, SDIOWIFI_SLEEP_REG, &val);
+ if (ret==0 && val&0x10) {
+ break;
+ }
+ read_retry--;
+ udelay(200);
+ }
+ if(read_retry != 0)
+ break;
+ }
+ sdio_dbg("write retry: %d \n",write_retry);
+ write_retry--;
+ udelay(100);
+ }
+ }
+
+ sdiodev->state = SDIO_ACTIVE_ST;
+ aicwf_sdio_pwrctl_timer(sdiodev, sdiodev->active_duration);
+ up(&sdiodev->pwrctl_wakeup_sema);
+ }
+ return ret;
+}
+
+extern u8 dhcped;
+int aicwf_sdio_sleep_allow(struct aic_sdio_dev *sdiodev)
+{
+ int ret = 0;
+ struct aicwf_bus *bus_if = sdiodev->bus_if;
+ struct rwnx_hw *rwnx_hw = sdiodev->rwnx_hw;
+ struct rwnx_vif *rwnx_vif, *tmp;
+
+ if (bus_if->state == BUS_DOWN_ST) {
+ ret = aicwf_sdio_writeb(sdiodev, SDIOWIFI_SLEEP_REG, 0x10);
+ if (ret) {
+ sdio_err("Write sleep fail!\n");
+ }
+ aicwf_sdio_pwrctl_timer(sdiodev, 0);
+ return ret;
+ }
+
+ list_for_each_entry_safe(rwnx_vif, tmp, &rwnx_hw->vifs, list) {
+ if((rwnx_hw->avail_idx_map & BIT(rwnx_vif->drv_vif_index)) == 0) {
+ switch(RWNX_VIF_TYPE(rwnx_vif)) {
+ case NL80211_IFTYPE_P2P_CLIENT:
+ rwnx_hw->is_p2p_alive = 1;
+ return ret;
+ case NL80211_IFTYPE_AP:
+ return ret;
+ case NL80211_IFTYPE_P2P_GO:
+ rwnx_hw->is_p2p_alive = 1;
+ return ret;
+ default:
+ break;
+ }
+ }
+ }
+
+ sdio_info("sleep: %d, %d\n", sdiodev->state, scanning);
+ if (sdiodev->state == SDIO_ACTIVE_ST && !scanning && !rwnx_hw->is_p2p_alive \
+ && !rwnx_hw->is_p2p_connected && dhcped) {
+ down(&sdiodev->pwrctl_wakeup_sema);
+ if (rwnx_hw->vif_started) {
+ sdio_dbg("s\n");
+ ret = aicwf_sdio_writeb(sdiodev, SDIOWIFI_SLEEP_REG, 0x10);
+ if (ret)
+ sdio_err("Write sleep fail!\n");
+ }
+ sdiodev->state = SDIO_SLEEP_ST;
+ aicwf_sdio_pwrctl_timer(sdiodev, 0);
+ up(&sdiodev->pwrctl_wakeup_sema);
+ }
+ else{
+ aicwf_sdio_pwrctl_timer(sdiodev, sdiodev->active_duration);
+ }
+
+ return ret;
+}
+
+int aicwf_sdio_pwr_stctl(struct aic_sdio_dev *sdiodev, uint target)
+{
+ int ret = 0;
+
+ if (sdiodev->bus_if->state == BUS_DOWN_ST) {
+ return -1;
+ }
+
+ if (sdiodev->state == target) {
+ if (target == SDIO_ACTIVE_ST) {
+ aicwf_sdio_pwrctl_timer(sdiodev, sdiodev->active_duration);
+ }
+ return ret;
+ }
+
+ switch (target) {
+ case SDIO_ACTIVE_ST:
+ aicwf_sdio_wakeup(sdiodev);
+ break;
+ case SDIO_SLEEP_ST:
+ aicwf_sdio_sleep_allow(sdiodev);
+ break;
+ }
+
+ return ret;
+}
+
+int aicwf_sdio_txpkt(struct aic_sdio_dev *sdiodev, struct sk_buff *pkt)
+{
+ int ret = 0;
+ u8 *frame;
+ u32 len = 0;
+ struct aicwf_bus *bus_if = dev_get_drvdata(sdiodev->dev);
+
+ if (bus_if->state == BUS_DOWN_ST) {
+ sdio_dbg("tx bus is down!\n");
+ return -EINVAL;
+ }
+
+ frame = (u8 *) (pkt->data);
+ len = pkt->len;
+ len = (len + SDIOWIFI_FUNC_BLOCKSIZE - 1) / SDIOWIFI_FUNC_BLOCKSIZE * SDIOWIFI_FUNC_BLOCKSIZE;
+ ret = aicwf_sdio_send_pkt(sdiodev, pkt->data, len);
+ if (ret)
+ sdio_err("aicwf_sdio_send_pkt fail%d\n", ret);
+
+ return ret;
+}
+
+static int aicwf_sdio_intr_get_len_bytemode(struct aic_sdio_dev *sdiodev, u8 *byte_len)
+{
+ int ret = 0;
+
+ if (!byte_len)
+ return -EBADE;
+
+ if (sdiodev->bus_if->state == BUS_DOWN_ST) {
+ *byte_len = 0;
+ } else {
+ ret = aicwf_sdio_readb(sdiodev, SDIOWIFI_BYTEMODE_LEN_REG, byte_len);
+ sdiodev->rx_priv->data_len = (*byte_len)*4;
+ }
+
+ return ret;
+}
+
+static void aicwf_sdio_bus_stop(struct device *dev)
+{
+ struct aicwf_bus *bus_if = dev_get_drvdata(dev);
+ struct aic_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
+ int ret;
+
+ aicwf_sdio_pwrctl_timer(sdiodev, 0);
+ if(timer_pending(&sdiodev->rwnx_hw->p2p_alive_timer)){
+ ret = del_timer(&sdiodev->rwnx_hw->p2p_alive_timer);}
+ sdio_dbg("%s\n",__func__);
+ if (sdiodev->pwrctl_tsk) {
+ complete(&sdiodev->pwrctrl_trgg);
+ kthread_stop(sdiodev->pwrctl_tsk);
+ sdiodev->pwrctl_tsk = NULL;
+ }
+
+ sdio_dbg("%s:pwrctl stopped\n",__func__);
+
+ bus_if->state = BUS_DOWN_ST;
+ ret = down_interruptible(&sdiodev->tx_priv->txctl_sema);
+ if (ret)
+ sdio_err("down txctl_sema fail\n");
+
+ aicwf_sdio_pwr_stctl(sdiodev, SDIO_SLEEP_ST);
+ if (!ret)
+ up(&sdiodev->tx_priv->txctl_sema);
+ aicwf_frame_queue_flush(&sdiodev->tx_priv->txq);
+ sdio_dbg("exit %s\n",__func__);
+}
+
+struct sk_buff *aicwf_sdio_readframes(struct aic_sdio_dev *sdiodev)
+{
+ int ret = 0;
+ u32 size = 0;
+ struct sk_buff *skb = NULL;
+ struct aicwf_bus *bus_if = dev_get_drvdata(sdiodev->dev);
+
+ if (bus_if->state == BUS_DOWN_ST) {
+ sdio_dbg("bus down\n");
+ return NULL;
+ }
+
+ size = sdiodev->rx_priv->data_len;
+ skb = __dev_alloc_skb(size, GFP_KERNEL);
+ if (!skb) {
+ return NULL;
+ }
+
+ ret = aicwf_sdio_recv_pkt(sdiodev, skb, size);
+ if (ret) {
+ dev_kfree_skb(skb);
+ skb = NULL;
+ }
+
+ return skb;
+}
+
+static int aicwf_sdio_tx_msg(struct aic_sdio_dev *sdiodev)
+{
+ int err = 0;
+ u16 len;
+ u8 *payload = sdiodev->tx_priv->cmd_buf;
+ u16 payload_len = sdiodev->tx_priv->cmd_len;
+ u8 adjust_str[4] = {0, 0, 0, 0};
+ int adjust_len = 0;
+ int buffer_cnt = 0;
+ u8 retry = 0;
+
+ len = payload_len;
+ if ((len % TX_ALIGNMENT) != 0) {
+ adjust_len = roundup(len, TX_ALIGNMENT);
+ memcpy(payload+payload_len, adjust_str, (adjust_len - len));
+ payload_len += (adjust_len - len);
+ }
+ len = payload_len;
+
+ //link tail is necessary
+ if ((len % SDIOWIFI_FUNC_BLOCKSIZE) != 0) {
+ memset(payload+payload_len, 0, TAIL_LEN);
+ payload_len += TAIL_LEN;
+ len = (payload_len/SDIOWIFI_FUNC_BLOCKSIZE + 1) * SDIOWIFI_FUNC_BLOCKSIZE;
+ } else{
+ len = payload_len;
+ }
+ buffer_cnt = aicwf_sdio_flow_ctrl(sdiodev);
+ while ((buffer_cnt <= 0 || (buffer_cnt > 0 && len > (buffer_cnt * BUFFER_SIZE))) && retry < 5) {
+ retry++;
+ buffer_cnt = aicwf_sdio_flow_ctrl(sdiodev);
+ }
+
+ down(&sdiodev->tx_priv->cmd_txsema);
+ if (buffer_cnt > 0 && len <= (buffer_cnt * BUFFER_SIZE)) {
+ err = aicwf_sdio_send_pkt(sdiodev, payload, len);
+ if (err) {
+ sdio_err("aicwf_sdio_send_pkt fail%d\n", err);
+ }
+ } else {
+ sdio_err("tx msg fc retry fail\n");
+ up(&sdiodev->tx_priv->cmd_txsema);
+ return -1;
+ }
+
+ sdiodev->tx_priv->cmd_txstate = false;
+ if (!err)
+ sdiodev->tx_priv->cmd_tx_succ= true;
+ else
+ sdiodev->tx_priv->cmd_tx_succ= false;
+
+ up(&sdiodev->tx_priv->cmd_txsema);
+
+ return err;
+}
+
+static void aicwf_sdio_tx_process(struct aic_sdio_dev *sdiodev)
+{
+ int err = 0;
+
+ if (sdiodev->bus_if->state == BUS_DOWN_ST) {
+ sdio_err("Bus is down\n");
+ return;
+ }
+
+ aicwf_sdio_pwr_stctl(sdiodev, SDIO_ACTIVE_ST);
+
+ //config
+ sdio_info("send cmd\n");
+ if (sdiodev->tx_priv->cmd_txstate) {
+ if (down_interruptible(&sdiodev->tx_priv->txctl_sema)) {
+ txrx_err("txctl down bus->txctl_sema fail\n");
+ return;
+ }
+ if (sdiodev->state != SDIO_ACTIVE_ST) {
+ txrx_err("state err\n");
+ up(&sdiodev->tx_priv->txctl_sema);
+ txrx_err("txctl up bus->txctl_sema fail\n");
+ return;
+ }
+
+ err = aicwf_sdio_tx_msg(sdiodev);
+ up(&sdiodev->tx_priv->txctl_sema);
+ if (waitqueue_active(&sdiodev->tx_priv->cmd_txdone_wait))
+ wake_up(&sdiodev->tx_priv->cmd_txdone_wait);
+ }
+
+ //data
+ sdio_info("send data\n");
+ if (down_interruptible(&sdiodev->tx_priv->txctl_sema)) {
+ txrx_err("txdata down bus->txctl_sema\n");
+ return;
+ }
+
+ if (sdiodev->state != SDIO_ACTIVE_ST) {
+ txrx_err("sdio state err\n");
+ up(&sdiodev->tx_priv->txctl_sema);
+ return;
+ }
+
+ sdiodev->tx_priv->fw_avail_bufcnt = aicwf_sdio_flow_ctrl(sdiodev);
+ while (!aicwf_is_framequeue_empty(&sdiodev->tx_priv->txq)) {
+ aicwf_sdio_send(sdiodev->tx_priv);
+ if(sdiodev->tx_priv->cmd_txstate)
+ break;
+ }
+
+ up(&sdiodev->tx_priv->txctl_sema);
+}
+
+static int aicwf_sdio_bus_txdata(struct device *dev, struct sk_buff *pkt)
+{
+ uint prio;
+ int ret = -EBADE;
+ struct aicwf_bus *bus_if = dev_get_drvdata(dev);
+ struct aic_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
+
+ prio = (pkt->priority & 0x7);
+ spin_lock_bh(&sdiodev->tx_priv->txqlock);
+ if (!aicwf_frame_enq(sdiodev->dev, &sdiodev->tx_priv->txq, pkt, prio)) {
+ aicwf_dev_skb_free(pkt);
+ spin_unlock_bh(&sdiodev->tx_priv->txqlock);
+ return -ENOSR;
+ } else {
+ ret = 0;
+ }
+
+ if (bus_if->state != BUS_UP_ST) {
+ sdio_err("bus_if stopped\n");
+ spin_unlock_bh(&sdiodev->tx_priv->txqlock);
+ return -1;
+ }
+
+ atomic_inc(&sdiodev->tx_priv->tx_pktcnt);
+ spin_unlock_bh(&sdiodev->tx_priv->txqlock);
+ complete(&bus_if->bustx_trgg);
+
+ return ret;
+}
+
+static int aicwf_sdio_bus_txmsg(struct device *dev, u8 *msg, uint msglen)
+{
+// int ret = -1;
+ struct aicwf_bus *bus_if = dev_get_drvdata(dev);
+ struct aic_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
+
+ down(&sdiodev->tx_priv->cmd_txsema);
+ sdiodev->tx_priv->cmd_txstate = true;
+ sdiodev->tx_priv->cmd_tx_succ = false;
+ sdiodev->tx_priv->cmd_buf = msg;
+ sdiodev->tx_priv->cmd_len = msglen;
+ up(&sdiodev->tx_priv->cmd_txsema);
+
+ if (bus_if->state != BUS_UP_ST) {
+ sdio_err("bus has stop\n");
+ return -1;
+ }
+
+ complete(&bus_if->bustx_trgg);
+ #if 0
+ if (sdiodev->tx_priv->cmd_txstate) {
+ int timeout = msecs_to_jiffies(CMD_TX_TIMEOUT);
+ ret = wait_event_interruptible_timeout(sdiodev->tx_priv->cmd_txdone_wait, \
+ !(sdiodev->tx_priv->cmd_txstate), timeout);
+ }
+
+ if (!sdiodev->tx_priv->cmd_txstate && sdiodev->tx_priv->cmd_tx_succ) {
+ ret = 0;
+ } else {
+ sdio_err("send faild:%d, %d,%x\n", sdiodev->tx_priv->cmd_txstate, sdiodev->tx_priv->cmd_tx_succ, ret);
+ ret = -EIO;
+ }
+
+ return ret;
+ #endif
+ return 0;
+}
+
+int aicwf_sdio_send(struct aicwf_tx_priv *tx_priv)
+{
+ struct sk_buff *pkt;
+ struct aic_sdio_dev *sdiodev = tx_priv->sdiodev;
+ u32 aggr_len = 0;
+ int retry_times = 0;
+ int max_retry_times = 5;
+
+ aggr_len = (tx_priv->tail - tx_priv->head);
+ if(((atomic_read(&tx_priv->aggr_count) == 0) && (aggr_len != 0))
+ || ((atomic_read(&tx_priv->aggr_count) != 0) && (aggr_len == 0))) {
+ if (aggr_len > 0)
+ aicwf_sdio_aggrbuf_reset(tx_priv);
+ goto done;
+ }
+
+ if (tx_priv->fw_avail_bufcnt <= 0) { //flow control failed
+ tx_priv->fw_avail_bufcnt = aicwf_sdio_flow_ctrl(sdiodev);
+ while(tx_priv->fw_avail_bufcnt <=0 && retry_times < max_retry_times) {
+ retry_times++;
+ tx_priv->fw_avail_bufcnt = aicwf_sdio_flow_ctrl(sdiodev);
+ }
+ if(tx_priv->fw_avail_bufcnt <= 0) {
+ sdio_err("fc retry %d fail\n", tx_priv->fw_avail_bufcnt);
+ goto done;
+ }
+ }
+
+ if (atomic_read(&tx_priv->aggr_count) == tx_priv->fw_avail_bufcnt) {
+ if (atomic_read(&tx_priv->aggr_count) > 0) {
+ tx_priv->fw_avail_bufcnt -= atomic_read(&tx_priv->aggr_count);
+ aicwf_sdio_aggr_send(tx_priv); //send and check the next pkt;
+ }
+ } else {
+ spin_lock_bh(&sdiodev->tx_priv->txqlock);
+ pkt = aicwf_frame_dequeue(&sdiodev->tx_priv->txq);
+ if (pkt == NULL) {
+ sdio_err("txq no pkt\n");
+ spin_unlock_bh(&sdiodev->tx_priv->txqlock);
+ goto done;
+ }
+ atomic_dec(&sdiodev->tx_priv->tx_pktcnt);
+ spin_unlock_bh(&sdiodev->tx_priv->txqlock);
+
+ if(tx_priv==NULL || tx_priv->tail==NULL || pkt==NULL)
+ txrx_err("null error\n");
+ if (aicwf_sdio_aggr(tx_priv, pkt)) {
+ aicwf_sdio_aggrbuf_reset(tx_priv);
+ sdio_err("add aggr pkts failed!\n");
+ goto done;
+ }
+
+ //when aggr finish or there is cmd to send, just send this aggr pkt to fw
+ if ((int)atomic_read(&sdiodev->tx_priv->tx_pktcnt) == 0 || sdiodev->tx_priv->cmd_txstate) { //no more pkt send it!
+ tx_priv->fw_avail_bufcnt -= atomic_read(&tx_priv->aggr_count);
+ aicwf_sdio_aggr_send(tx_priv);
+ } else
+ goto done;
+ }
+
+done:
+ return 0;
+}
+
+int aicwf_sdio_aggr(struct aicwf_tx_priv *tx_priv, struct sk_buff *pkt)
+{
+ struct rwnx_txhdr *txhdr = (struct rwnx_txhdr *)pkt->data;
+ u8 *start_ptr = tx_priv->tail;
+ u8 sdio_header[4];
+ u8 adjust_str[4] = {0, 0, 0, 0};
+ u32 curr_len = 0;
+ int allign_len = 0;
+
+ sdio_header[0] =((pkt->len - sizeof(struct rwnx_txhdr) + sizeof(struct txdesc_api)) & 0xff);
+ sdio_header[1] =(((pkt->len - sizeof(struct rwnx_txhdr) + sizeof(struct txdesc_api)) >> 8)&0x0f);
+ sdio_header[2] = 0x01; //data
+ sdio_header[3] = 0; //reserved
+
+ memcpy(tx_priv->tail, (u8 *)&sdio_header, sizeof(sdio_header));
+ tx_priv->tail += sizeof(sdio_header);
+ //payload
+ memcpy(tx_priv->tail, (u8 *)(long)&txhdr->sw_hdr->desc, sizeof(struct txdesc_api));
+ tx_priv->tail += sizeof(struct txdesc_api); //hostdesc
+ memcpy(tx_priv->tail, (u8 *)((u8 *)txhdr + txhdr->sw_hdr->headroom), pkt->len-txhdr->sw_hdr->headroom);
+ tx_priv->tail += (pkt->len - txhdr->sw_hdr->headroom);
+
+ //word alignment
+ curr_len = tx_priv->tail - tx_priv->head;
+ if (curr_len & (TX_ALIGNMENT - 1)) {
+ allign_len = roundup(curr_len, TX_ALIGNMENT)-curr_len;
+ memcpy(tx_priv->tail, adjust_str, allign_len);
+ tx_priv->tail += allign_len;
+ }
+
+ start_ptr[0] = ((tx_priv->tail - start_ptr - 4) & 0xff);
+ start_ptr[1] = (((tx_priv->tail - start_ptr - 4)>>8) & 0x0f);
+ tx_priv->aggr_buf->dev = pkt->dev;
+
+ if(!txhdr->sw_hdr->need_cfm) {
+ kmem_cache_free(txhdr->sw_hdr->rwnx_vif->rwnx_hw->sw_txhdr_cache, txhdr->sw_hdr);
+ skb_pull(pkt, txhdr->sw_hdr->headroom);
+ consume_skb(pkt);
+ }
+
+ atomic_inc(&tx_priv->aggr_count);
+ return 0;
+}
+
+void aicwf_sdio_aggr_send(struct aicwf_tx_priv *tx_priv)
+{
+ struct sk_buff *tx_buf = tx_priv->aggr_buf;
+ int ret = 0;
+ int curr_len = 0;
+
+ //link tail is necessary
+ curr_len = tx_priv->tail - tx_priv->head;
+ if ((curr_len % TXPKT_BLOCKSIZE) != 0) {
+ memset(tx_priv->tail, 0, TAIL_LEN);
+ tx_priv->tail += TAIL_LEN;
+ }
+
+ tx_buf->len = tx_priv->tail - tx_priv->head;
+ ret = aicwf_sdio_txpkt(tx_priv->sdiodev, tx_buf);
+ if (ret < 0) {
+ sdio_err("fail to send aggr pkt!\n");
+ }
+
+ aicwf_sdio_aggrbuf_reset(tx_priv);
+}
+
+void aicwf_sdio_aggrbuf_reset(struct aicwf_tx_priv *tx_priv)
+{
+ struct sk_buff *aggr_buf = tx_priv->aggr_buf;
+
+ tx_priv->tail = tx_priv->head;
+ aggr_buf->len = 0;
+ atomic_set(&tx_priv->aggr_count, 0);
+}
+
+static int aicwf_sdio_bus_start(struct device *dev)
+{
+ struct aicwf_bus *bus_if = dev_get_drvdata(dev);
+ struct aic_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
+ int ret = 0;
+
+#if 1
+ sdio_claim_host(sdiodev->func);
+ sdio_claim_irq(sdiodev->func, aicwf_sdio_hal_irqhandler);
+#else
+ //since we have func2 we don't register irq handler
+ sdio_claim_irq(sdiodev->func, NULL);
+ sdiodev->func->irq_handler = (sdio_irq_handler_t *)aicwf_sdio_hal_irqhandler;
+#endif
+ //enable sdio interrupt
+ ret = aicwf_sdio_writeb(sdiodev, SDIOWIFI_INTR_CONFIG_REG, 0x07);
+ sdio_release_host(sdiodev->func);
+
+ if (ret != 0)
+ sdio_err("intr register failed:%d\n", ret);
+
+ bus_if->state = BUS_UP_ST;
+
+ return ret;
+}
+
+int sdio_bustx_thread(void *data)
+{
+ struct aicwf_bus *bus = (struct aicwf_bus *) data;
+ struct aic_sdio_dev *sdiodev = bus->bus_priv.sdio;
+
+ while (1) {
+ if(kthread_should_stop()) {
+ sdio_err("sdio bustx thread stop\n");
+ break;
+ }
+ if (!wait_for_completion_interruptible(&bus->bustx_trgg)) {
+ if(sdiodev->bus_if->state == BUS_DOWN_ST)
+ break;
+
+ if((int)(atomic_read(&sdiodev->tx_priv->tx_pktcnt) > 0) || (sdiodev->tx_priv->cmd_txstate == true))
+ aicwf_sdio_tx_process(sdiodev);
+ }
+ }
+
+ return 0;
+}
+
+int sdio_busrx_thread(void *data)
+{
+ struct aicwf_rx_priv *rx_priv = (struct aicwf_rx_priv *)data;
+ struct aicwf_bus *bus_if = rx_priv->sdiodev->bus_if;
+
+ while (1) {
+ if(kthread_should_stop()) {
+ sdio_err("sdio busrx thread stop\n");
+ break;
+ }
+ if (!wait_for_completion_interruptible(&bus_if->busrx_trgg)) {
+ if(bus_if->state == BUS_DOWN_ST)
+ break;
+ aicwf_process_rxframes(rx_priv);
+ }
+ }
+
+ return 0;
+}
+
+static int aicwf_sdio_pwrctl_thread(void *data)
+{
+ struct aic_sdio_dev *sdiodev = (struct aic_sdio_dev *) data;
+
+ while (1) {
+ if(kthread_should_stop()) {
+ sdio_err("sdio pwrctl thread stop\n");
+ break;
+ }
+ if (!wait_for_completion_interruptible(&sdiodev->pwrctrl_trgg)) {
+ if(sdiodev->bus_if->state == BUS_DOWN_ST)
+ break;
+ if (sdiodev->state == SDIO_ACTIVE_ST) {
+ if((int)(atomic_read(&sdiodev->tx_priv->tx_pktcnt) <= 0) && (sdiodev->tx_priv->cmd_txstate == false) && \
+ atomic_read(&sdiodev->rx_priv->rx_cnt)==0)
+ aicwf_sdio_pwr_stctl(sdiodev, SDIO_SLEEP_ST);
+ else
+ aicwf_sdio_pwrctl_timer(sdiodev, sdiodev->active_duration);
+ }
+ } else {
+ continue;
+ }
+ }
+
+ return 0;
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4,14,0)
+static void aicwf_sdio_bus_pwrctl(ulong data)
+#else
+static void aicwf_sdio_bus_pwrctl(struct timer_list *t)
+#endif
+{
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4,14,0)
+ struct aic_sdio_dev *sdiodev = (struct aic_sdio_dev *) data;
+#else
+ struct aic_sdio_dev *sdiodev = from_timer(sdiodev, t, timer);
+#endif
+
+ if (sdiodev->bus_if->state == BUS_DOWN_ST) {
+ sdio_err("bus down\n");
+ return;
+ }
+
+ if (sdiodev->pwrctl_tsk){
+ complete(&sdiodev->pwrctrl_trgg);
+ }
+}
+
+
+static void aicwf_sdio_enq_rxpkt(struct aic_sdio_dev *sdiodev, struct sk_buff *pkt)
+{
+ struct aicwf_rx_priv* rx_priv = sdiodev->rx_priv;
+ unsigned long flags = 0;
+
+ spin_lock_irqsave(&rx_priv->rxqlock, flags);
+ if (!aicwf_rxframe_enqueue(sdiodev->dev, &rx_priv->rxq, pkt)) {
+ spin_unlock_irqrestore(&rx_priv->rxqlock, flags);
+ aicwf_dev_skb_free(pkt);
+ return;
+ }
+ spin_unlock_irqrestore(&rx_priv->rxqlock, flags);
+
+ atomic_inc(&rx_priv->rx_cnt);
+}
+
+#define SDIO_OTHER_INTERRUPT (0x1ul << 7)
+
+void aicwf_sdio_hal_irqhandler(struct sdio_func *func)
+{
+ struct aicwf_bus *bus_if = dev_get_drvdata(&func->dev);
+ struct aic_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
+ u8 intstatus = 0;
+ u8 byte_len = 0;
+ struct sk_buff *pkt = NULL;
+ int ret;
+
+ if (!bus_if || bus_if->state == BUS_DOWN_ST) {
+ sdio_err("bus err\n");
+ return;
+ }
+
+ ret = aicwf_sdio_readb(sdiodev, SDIOWIFI_BLOCK_CNT_REG, &intstatus);
+ while(ret || (intstatus & SDIO_OTHER_INTERRUPT)) {
+ sdio_err("ret=%d, intstatus=%x\r\n",ret, intstatus);
+ ret = aicwf_sdio_readb(sdiodev, SDIOWIFI_BLOCK_CNT_REG, &intstatus);
+ }
+ sdiodev->rx_priv->data_len = intstatus * SDIOWIFI_FUNC_BLOCKSIZE;
+
+ if (intstatus > 0) {
+ if(intstatus < 64) {
+ pkt = aicwf_sdio_readframes(sdiodev);
+ } else {
+ aicwf_sdio_intr_get_len_bytemode(sdiodev, &byte_len);//byte_len must<= 128
+ sdio_info("byte mode len=%d\r\n", byte_len);
+ pkt = aicwf_sdio_readframes(sdiodev);
+ }
+ } else {
+ #ifndef CONFIG_PLATFORM_ALLWINNER
+ sdio_err("Interrupt but no data\n");
+ #endif
+ }
+
+ if (pkt)
+ aicwf_sdio_enq_rxpkt(sdiodev, pkt);
+
+ complete(&bus_if->busrx_trgg);
+}
+
+void aicwf_sdio_pwrctl_timer(struct aic_sdio_dev *sdiodev, uint duration)
+{
+ uint timeout;
+
+ if (sdiodev->bus_if->state == BUS_DOWN_ST && duration)
+ return;
+
+ spin_lock_bh(&sdiodev->pwrctl_lock);
+ if (!duration) {
+ if (timer_pending(&sdiodev->timer))
+ del_timer_sync(&sdiodev->timer);
+ } else {
+ sdiodev->active_duration = duration;
+ timeout = msecs_to_jiffies(sdiodev->active_duration);
+ mod_timer(&sdiodev->timer, jiffies + timeout);
+ }
+ spin_unlock_bh(&sdiodev->pwrctl_lock);
+}
+
+static struct aicwf_bus_ops aicwf_sdio_bus_ops = {
+ .stop = aicwf_sdio_bus_stop,
+ .start = aicwf_sdio_bus_start,
+ .txdata = aicwf_sdio_bus_txdata,
+ .txmsg = aicwf_sdio_bus_txmsg,
+};
+
+void aicwf_sdio_release(struct aic_sdio_dev *sdiodev)
+{
+ struct aicwf_bus *bus_if;
+ struct aicwf_rx_priv* rx_priv = NULL;
+ int ret;
+ sdio_dbg("%s\n", __func__);
+
+ bus_if = dev_get_drvdata(sdiodev->dev);
+ bus_if->state = BUS_DOWN_ST;
+
+ sdio_claim_host(sdiodev->func);
+ //disable sdio interrupt
+ ret = aicwf_sdio_writeb(sdiodev, SDIOWIFI_INTR_CONFIG_REG, 0x0);
+ if (ret < 0) {
+ sdio_err("reg:%d write failed!\n", SDIOWIFI_INTR_CONFIG_REG);
+ }
+ sdio_release_irq(sdiodev->func);
+ sdio_release_host(sdiodev->func);
+
+ if (sdiodev->dev)
+ aicwf_bus_deinit(sdiodev->dev);
+
+ aicwf_tx_deinit(sdiodev->tx_priv);
+ rx_priv = sdiodev->rx_priv;
+ if(rx_priv != NULL)
+ aicwf_rx_deinit(rx_priv);
+ rwnx_cmd_mgr_deinit(&sdiodev->cmd_mgr);
+ sdio_dbg("exit %s\n", __func__);
+}
+
+int aicwf_sdio_func_init(struct aic_sdio_dev *sdiodev)
+{
+ struct mmc_host *host;
+ u8 block_bit0 = 0x1;
+ u8 byte_mode_disable = 0x1;//1: no byte mode
+ int ret = 0;
+ host = sdiodev->func->card->host;
+
+ sdio_claim_host(sdiodev->func);
+ ret = sdio_set_block_size(sdiodev->func, SDIOWIFI_FUNC_BLOCKSIZE);
+ if (ret < 0) {
+ sdio_err("set blocksize fail %d\n", ret);
+ sdio_release_host(sdiodev->func);
+ return ret;
+ }
+ ret = sdio_enable_func(sdiodev->func);
+ if (ret < 0) {
+ sdio_release_host(sdiodev->func);
+ sdio_err("enable func fail %d.\n", ret);
+ }
+
+ host->ios.clock = 60000000;
+ host->ops->set_ios(host, &host->ios);
+ sdio_release_host(sdiodev->func);
+ sdio_dbg("Set SDIO Clock %d MHz\n",host->ios.clock/1000000);
+
+ ret = aicwf_sdio_writeb(sdiodev, SDIOWIFI_REGISTER_BLOCK, block_bit0);
+ if (ret < 0) {
+ sdio_err("reg:%d write failed!\n", SDIOWIFI_REGISTER_BLOCK);
+ return ret;
+ }
+
+ //1: no byte mode
+ ret = aicwf_sdio_writeb(sdiodev, SDIOWIFI_BYTEMODE_ENABLE_REG, byte_mode_disable);
+ if (ret < 0) {
+ sdio_err("reg:%d write failed!\n", SDIOWIFI_BYTEMODE_ENABLE_REG);
+ return ret;
+ }
+
+ return ret;
+}
+
+
+void aicwf_sdio_func_deinit(struct aic_sdio_dev *sdiodev)
+{
+ sdio_claim_host(sdiodev->func);
+ sdio_disable_func(sdiodev->func);
+ sdio_release_host(sdiodev->func);
+}
+
+void *aicwf_sdio_bus_init(struct aic_sdio_dev *sdiodev)
+{
+ int ret;
+ struct aicwf_bus *bus_if;
+ struct aicwf_rx_priv *rx_priv;
+ struct aicwf_tx_priv *tx_priv;
+
+ spin_lock_init(&sdiodev->pwrctl_lock);
+ sema_init(&sdiodev->pwrctl_wakeup_sema, 1);
+
+ bus_if = sdiodev->bus_if;
+ bus_if->dev = sdiodev->dev;
+ bus_if->ops = &aicwf_sdio_bus_ops;
+ bus_if->state = BUS_DOWN_ST;
+ sdiodev->state = SDIO_SLEEP_ST;
+ sdiodev->active_duration = SDIOWIFI_PWR_CTRL_INTERVAL;
+
+ rx_priv = aicwf_rx_init(sdiodev);
+ if(!rx_priv) {
+ sdio_err("rx init fail\n");
+ goto fail;
+ }
+ sdiodev->rx_priv = rx_priv;
+
+ tx_priv = aicwf_tx_init(sdiodev);
+ if(!tx_priv) {
+ sdio_err("tx init fail\n");
+ goto fail;
+ }
+ sdiodev->tx_priv = tx_priv;
+ aicwf_frame_queue_init(&tx_priv->txq, 8, TXQLEN);
+ spin_lock_init(&tx_priv->txqlock);
+ sema_init(&tx_priv->txctl_sema,1);
+ sema_init(&tx_priv->cmd_txsema, 1);
+ init_waitqueue_head(&tx_priv->cmd_txdone_wait);
+ atomic_set(&tx_priv->tx_pktcnt, 0);
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4,14,0)
+ init_timer(&sdiodev->timer);
+ sdiodev->timer.data = (ulong) sdiodev;
+ sdiodev->timer.function = aicwf_sdio_bus_pwrctl;
+#else
+ timer_setup(&sdiodev->timer, aicwf_sdio_bus_pwrctl, 0);
+#endif
+ init_completion(&sdiodev->pwrctrl_trgg);
+#ifdef AICWF_SDIO_SUPPORT
+ sdiodev->pwrctl_tsk = kthread_run(aicwf_sdio_pwrctl_thread, sdiodev, "aicwf_pwrctl");
+#endif
+ if (IS_ERR(sdiodev->pwrctl_tsk)) {
+ sdiodev->pwrctl_tsk = NULL;
+ }
+
+ ret = aicwf_bus_init(0, sdiodev->dev);
+ if (ret < 0) {
+ sdio_err("bus init fail\n");
+ goto fail;
+ }
+
+ ret = aicwf_bus_start(bus_if);
+ if (ret != 0) {
+ sdio_err("bus start fail\n");
+ goto fail;
+ }
+
+ return sdiodev;
+
+fail:
+ aicwf_sdio_release(sdiodev);
+ return NULL;
+}
+
+
diff --git a/drivers/net/wireless/aic8800/aic8800_fdrv/aicwf_sdio.h b/drivers/net/wireless/aic8800/aic8800_fdrv/aicwf_sdio.h
new file mode 100644
index 000000000000..02c4237ecd91
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic8800_fdrv/aicwf_sdio.h
@@ -0,0 +1,99 @@
+/**
+ * aicwf_sdio.h
+ *
+ * SDIO function declarations
+ *
+ * Copyright (C) AICSemi 2018-2020
+ */
+
+#ifndef _AICWF_SDMMC_H_
+#define _AICWF_SDMMC_H_
+
+#ifdef AICWF_SDIO_SUPPORT
+#include <linux/skbuff.h>
+#include <linux/if_ether.h>
+#include <linux/ieee80211.h>
+#include <linux/semaphore.h>
+#include "rwnx_cmds.h"
+#define AICWF_SDIO_NAME "aicwf_sdio"
+#define SDIOWIFI_FUNC_BLOCKSIZE 512
+
+#define SDIO_VENDOR_ID_AIC 0x8800
+#define SDIO_DEVICE_ID_AIC 0x0001
+#define SDIOWIFI_BYTEMODE_LEN_REG 0x02
+#define SDIOWIFI_INTR_CONFIG_REG 0x04
+#define SDIOWIFI_SLEEP_REG 0x05
+#define SDIOWIFI_WAKEUP_REG 0x09
+#define SDIOWIFI_FLOW_CTRL_REG 0x0A
+#define SDIOWIFI_REGISTER_BLOCK 0x0B
+#define SDIOWIFI_BYTEMODE_ENABLE_REG 0x11
+#define SDIOWIFI_BLOCK_CNT_REG 0x12
+#define SDIOWIFI_FLOWCTRL_MASK_REG 0x7F
+
+#define SDIOWIFI_PWR_CTRL_INTERVAL 30
+#define FLOW_CTRL_RETRY_COUNT 50
+#define BUFFER_SIZE 1536
+#define TAIL_LEN 4
+#define TXQLEN (2048*4)
+
+#define SDIO_SLEEP_ST 0
+#define SDIO_ACTIVE_ST 1
+
+typedef enum {
+ SDIO_TYPE_DATA = 0X00,
+ SDIO_TYPE_CFG = 0X10,
+ SDIO_TYPE_CFG_CMD_RSP = 0X11,
+ SDIO_TYPE_CFG_DATA_CFM = 0X12
+} sdio_type;
+
+struct rwnx_hw;
+
+
+struct aic_sdio_dev {
+ struct rwnx_hw *rwnx_hw;
+ struct sdio_func *func;
+ struct device *dev;
+ struct aicwf_bus *bus_if;
+ struct rwnx_cmd_mgr cmd_mgr;
+
+ struct aicwf_rx_priv *rx_priv;
+ struct aicwf_tx_priv *tx_priv;
+ u32 state;
+
+ //for sdio pwr ctrl
+ struct timer_list timer;
+ uint active_duration;
+ struct completion pwrctrl_trgg;
+ struct task_struct *pwrctl_tsk;
+ spinlock_t pwrctl_lock;
+ struct semaphore pwrctl_wakeup_sema;
+};
+int aicwf_sdio_writeb(struct aic_sdio_dev *sdiodev, uint regaddr, u8 val);
+void aicwf_sdio_hal_irqhandler(struct sdio_func *func);
+void aicwf_sdio_pwrctl_timer(struct aic_sdio_dev *sdiodev, uint duration);
+int aicwf_sdio_pwr_stctl(struct aic_sdio_dev *sdiodev, uint target);
+int aicwf_sdio_func_init(struct aic_sdio_dev *sdiodev);
+void aicwf_sdio_func_deinit(struct aic_sdio_dev *sdiodev);
+int aicwf_sdio_flow_ctrl(struct aic_sdio_dev *sdiodev);
+int aicwf_sdio_recv_pkt(struct aic_sdio_dev *sdiodev, struct sk_buff *skbbuf, u32 size);
+int aicwf_sdio_send_pkt(struct aic_sdio_dev *sdiodev, u8 *buf, uint count);
+void *aicwf_sdio_bus_init(struct aic_sdio_dev *sdiodev);
+void aicwf_sdio_release(struct aic_sdio_dev *sdiodev);
+void aicwf_sdio_exit(void);
+void aicwf_sdio_register(void);
+int aicwf_sdio_txpkt(struct aic_sdio_dev *sdiodev, struct sk_buff *pkt);
+int sdio_bustx_thread(void *data);
+int sdio_busrx_thread(void *data);
+int aicwf_sdio_aggr(struct aicwf_tx_priv *tx_priv, struct sk_buff *pkt);
+int aicwf_sdio_send(struct aicwf_tx_priv *tx_priv);
+void aicwf_sdio_aggr_send(struct aicwf_tx_priv *tx_priv);
+void aicwf_sdio_aggrbuf_reset(struct aicwf_tx_priv* tx_priv);
+extern void aicwf_hostif_ready(void);
+#ifdef CONFIG_PLATFORM_NANOPI
+extern void extern_wifi_set_enable(int is_on);
+extern void sdio_reinit(void);
+#endif /*CONFIG_PLATFORM_NANOPI*/
+
+#endif /* AICWF_SDIO_SUPPORT */
+
+#endif /*_AICWF_SDMMC_H_*/
diff --git a/drivers/net/wireless/aic8800/aic8800_fdrv/aicwf_txrxif.c b/drivers/net/wireless/aic8800/aic8800_fdrv/aicwf_txrxif.c
new file mode 100644
index 000000000000..34b91d596717
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic8800_fdrv/aicwf_txrxif.c
@@ -0,0 +1,1227 @@
+/**
+ * aicwf_bus.c
+ *
+ * bus function declarations
+ *
+ * Copyright (C) AICSemi 2018-2020
+ */
+
+#include <linux/kthread.h>
+#include <linux/netdevice.h>
+#include <linux/printk.h>
+#include <linux/interrupt.h>
+#include <linux/sched.h>
+#include <linux/completion.h>
+#include <linux/semaphore.h>
+#include <linux/debugfs.h>
+#include <linux/atomic.h>
+#include <linux/vmalloc.h>
+#include "lmac_msg.h"
+#include "aicwf_txrxif.h"
+#include "rwnx_platform.h"
+#include "rwnx_defs.h"
+#include "rwnx_msg_rx.h"
+#include "rwnx_rx.h"
+#include "aicwf_rx_prealloc.h"
+#ifdef AICWF_SDIO_SUPPORT
+#include "sdio_host.h"
+#endif
+
+#ifdef CONFIG_RX_TASKLET
+int aicwf_tasklet_rxframes(struct aicwf_rx_priv *rx_priv);
+#endif
+#ifdef CONFIG_TX_TASKLET
+void aicwf_tasklet_tx_process(struct aic_usb_dev *usb_dev);
+#endif
+extern bool aicwf_usb_rx_aggr;
+
+#ifdef CONFIG_PREALLOC_RX_SKB
+void aicwf_rxframe_queue_init_2(struct rx_frame_queue *pq, int max_len)
+{
+ //int prio;
+
+ memset(pq, 0, offsetof(struct rx_frame_queue, queuelist) + (sizeof(struct list_head)));
+ pq->qmax = (u16)max_len;
+ INIT_LIST_HEAD(&pq->queuelist);
+#if 0
+ memset(pq, 0, offsetof(struct rx_frame_queue, queuelist) + (sizeof(struct list_head) * num_prio));
+ pq->num_prio = (u16)num_prio;
+ pq->qmax = (u16)max_len;
+
+ for (prio = 0; prio < num_prio; prio++) {
+ INIT_LIST_HEAD(&pq->queuelist[prio]);
+ }
+#endif
+}
+
+//extern struct aic_sdio_dev *g_sdiodev;
+void rxbuff_queue_flush(struct aicwf_rx_priv* rx_priv)
+{
+
+ //int prio;
+ struct rx_frame_queue *pq = &rx_priv->rxq;
+ struct list_head *pos;
+ struct list_head *n;
+ struct list_head *head;
+ struct rx_buff *tempbuf = NULL;
+
+ head = &pq->queuelist;
+ list_for_each_safe(pos, n, head) {
+ tempbuf = list_entry(pos, struct rx_buff, queue);
+ list_del_init(&tempbuf->queue);
+#if 0
+ rxbuff_free(tempbuf);
+#else
+ aicwf_prealloc_rxbuff_free(tempbuf, &rx_priv->rxbuff_lock);
+#endif
+ pq->qcnt--;
+ }
+}
+#endif
+
+int aicwf_bus_init(uint bus_hdrlen, struct device *dev)
+{
+ int ret = 0;
+ struct aicwf_bus *bus_if;
+
+ if (!dev) {
+ AICWFDBG(LOGERROR, "device not found\n");
+ return -1;
+ }
+ bus_if = dev_get_drvdata(dev);
+ #if defined CONFIG_USB_SUPPORT && defined CONFIG_USB_NO_TRANS_DMA_MAP
+ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35))
+ bus_if->cmd_buf = usb_alloc_coherent(bus_if->bus_priv.usb->udev, CMD_BUF_MAX, (in_interrupt() ? GFP_ATOMIC : GFP_KERNEL), &bus_if->bus_priv.usb->cmd_dma_trans_addr);
+ #else
+ bus_if->cmd_buf = usb_buffer_alloc(bus_if->bus_priv.usb->udev, CMD_BUF_MAX, (in_interrupt() ? GFP_ATOMIC : GFP_KERNEL), &bus_if->bus_priv.usb->cmd_dma_trans_addr);
+ #endif
+ #else
+ bus_if->cmd_buf = kzalloc(CMD_BUF_MAX, GFP_KERNEL);
+ #endif
+ if(!bus_if->cmd_buf) {
+ ret = -ENOMEM;
+ AICWFDBG(LOGERROR, "proto_attach failed\n");
+ goto fail;
+ }
+ memset(bus_if->cmd_buf, '\0', CMD_BUF_MAX);
+
+ init_completion(&bus_if->bustx_trgg);
+ init_completion(&bus_if->busrx_trgg);
+#ifdef CONFIG_USB_MSG_IN_EP
+ if(bus_if->bus_priv.usb->chipid != PRODUCT_ID_AIC8801 &&
+ bus_if->bus_priv.usb->chipid != PRODUCT_ID_AIC8800D81){
+ init_completion(&bus_if->msg_busrx_trgg);
+ }
+#endif
+
+#ifdef AICWF_SDIO_SUPPORT
+ bus_if->bustx_thread = kthread_run(sdio_bustx_thread, (void *)bus_if, "aicwf_bustx_thread");
+ bus_if->busrx_thread = kthread_run(sdio_busrx_thread, (void *)bus_if->bus_priv.sdio->rx_priv, "aicwf_busrx_thread");
+#endif
+#ifdef AICWF_USB_SUPPORT
+ bus_if->busrx_thread = kthread_run(usb_busrx_thread, (void *)bus_if->bus_priv.usb->rx_priv, "aicwf_busrx_thread");
+ bus_if->bustx_thread = kthread_run(usb_bustx_thread, (void *)bus_if, "aicwf_bustx_thread");
+
+#if 1
+ //waiting for rx/tx thread init finish
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 14, 0)
+ while(bus_if->busrx_thread->__state != TASK_INTERRUPTIBLE ||
+ bus_if->bustx_thread->__state != TASK_INTERRUPTIBLE)
+#else
+ while(bus_if->busrx_thread->state != TASK_INTERRUPTIBLE ||
+ bus_if->bustx_thread->state != TASK_INTERRUPTIBLE)
+#endif
+ {
+ AICWFDBG(LOGINFO, "%s waiting for rx/tx thread init finish \r\n", __func__);
+ msleep(100);
+ }
+ //waiting for rx/tx thread Initialization finish
+#endif
+
+#ifdef CONFIG_USB_MSG_IN_EP
+ if(bus_if->bus_priv.usb->chipid != PRODUCT_ID_AIC8801 &&
+ bus_if->bus_priv.usb->chipid != PRODUCT_ID_AIC8800D81){
+ bus_if->msg_busrx_thread = kthread_run(usb_msg_busrx_thread, (void *)bus_if->bus_priv.usb->rx_priv, "aicwf_msg_busrx_thread");
+ }
+#endif
+
+#ifdef CONFIG_RX_TASKLET//AIDEN tasklet
+ AICWFDBG(LOGINFO, "%s use tasklet for rx \r\n", __func__);
+ tasklet_init(&((bus_if->bus_priv.usb)->recv_tasklet),
+ (void(*)(unsigned long))aicwf_tasklet_rxframes,
+ (unsigned long)(bus_if->bus_priv.usb)->rx_priv);
+#endif
+
+#ifdef CONFIG_TX_TASKLET//AIDEN tasklet
+ AICWFDBG(LOGINFO, "%s use tasklet for tx \r\n", __func__);
+ tasklet_init(&((bus_if->bus_priv.usb)->xmit_tasklet),
+ (void(*)(unsigned long))aicwf_tasklet_tx_process,
+ (unsigned long)(bus_if->bus_priv.usb));
+#endif
+
+
+#endif
+
+ if (IS_ERR(bus_if->bustx_thread)) {
+ bus_if->bustx_thread = NULL;
+ AICWFDBG(LOGERROR, "aicwf_bustx_thread run fail\n");
+ goto fail;
+ }
+
+ if (IS_ERR(bus_if->busrx_thread)) {
+ bus_if->busrx_thread = NULL;
+ AICWFDBG(LOGERROR, "aicwf_bustx_thread run fail\n");
+ goto fail;
+ }
+
+#ifdef CONFIG_USB_MSG_IN_EP
+ if(bus_if->bus_priv.usb->chipid != PRODUCT_ID_AIC8801 &&
+ bus_if->bus_priv.usb->chipid != PRODUCT_ID_AIC8800D81){
+ if (IS_ERR(bus_if->msg_busrx_thread)) {
+ bus_if->msg_busrx_thread = NULL;
+ txrx_err("aicwf_msg_busrx_thread run fail\n");
+ goto fail;
+ }
+ }
+#endif
+
+
+ return ret;
+fail:
+ aicwf_bus_deinit(dev);
+
+ return ret;
+}
+
+void aicwf_usb_cancel_all_urbs(struct aic_usb_dev *usb_dev);
+
+void aicwf_bus_deinit(struct device *dev)
+{
+ struct aicwf_bus *bus_if;
+#ifdef AICWF_USB_SUPPORT
+ struct aic_usb_dev *usb;
+#endif
+#ifdef AICWF_SDIO_SUPPORT
+ struct aic_sdio_dev *sdiodev;
+#endif
+
+ if (!dev) {
+ txrx_err("device not found\n");
+ return;
+ }
+ AICWFDBG(LOGINFO, "%s Enter\r\n", __func__);
+
+ bus_if = dev_get_drvdata(dev);
+ aicwf_bus_stop(bus_if);
+
+#ifdef AICWF_USB_SUPPORT
+ usb = bus_if->bus_priv.usb;
+ aicwf_usb_cancel_all_urbs(usb);//AIDEN
+
+ if(g_rwnx_plat && g_rwnx_plat->enabled){
+ rwnx_platform_deinit(usb->rwnx_hw);
+ }else{
+ AICWFDBG(LOGINFO, "%s g_rwnx_plat->enabled is false\r\n", __func__);
+ }
+
+#endif
+#ifdef AICWF_SDIO_SUPPORT
+ sdiodev =bus_if->bus_priv.sdio;
+ if(g_rwnx_plat && g_rwnx_plat->enabled)
+ rwnx_platform_deinit(sdiodev->rwnx_hw);
+#endif
+
+ if (bus_if->cmd_buf) {
+ #if defined CONFIG_USB_SUPPORT && defined CONFIG_USB_NO_TRANS_DMA_MAP
+ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35))
+ usb_free_coherent(bus_if->bus_priv.usb->udev, CMD_BUF_MAX, bus_if->cmd_buf, bus_if->bus_priv.usb->cmd_dma_trans_addr);
+ #else
+ usb_buffer_free(bus_if->bus_priv.usb->udev, CMD_BUF_MAX, bus_if->cmd_buf, bus_if->bus_priv.usb->cmd_dma_trans_addr);
+ #endif
+ #else
+ kfree(bus_if->cmd_buf);
+ #endif
+ bus_if->cmd_buf = NULL;
+ }
+
+ if (bus_if->bustx_thread) {
+ AICWFDBG(LOGINFO, "%s stop to bustx_thread!! \r\n", __func__);
+
+
+ complete_all(&bus_if->bustx_trgg);
+ kthread_stop(bus_if->bustx_thread);
+ bus_if->bustx_thread = NULL;
+ }
+
+#ifdef CONFIG_TX_TASKLET//AIDEN tasklet
+ tasklet_kill(&usb->xmit_tasklet);
+#endif
+
+ AICWFDBG(LOGINFO, "%s Exit \n", __func__);
+}
+
+void aicwf_frame_tx(void *dev, struct sk_buff *skb)
+{
+#ifdef AICWF_SDIO_SUPPORT
+ struct aic_sdio_dev *sdiodev = (struct aic_sdio_dev *)dev;
+ aicwf_bus_txdata(sdiodev->bus_if, skb);
+#else
+ struct aic_usb_dev *usbdev = (struct aic_usb_dev *)dev;
+ struct rwnx_txhdr *txhdr = NULL;
+ unsigned long flags;
+
+ if (!usbdev->state) {
+ txrx_err("down\n");
+ spin_lock_irqsave(&usbdev->tx_flow_lock, flags);
+ if(!usbdev->tbusy)
+ aicwf_usb_tx_flowctrl(usbdev->rwnx_hw, true);
+ spin_unlock_irqrestore(&usbdev->tx_flow_lock, flags);
+ txhdr = (struct rwnx_txhdr *)skb->data;
+ kmem_cache_free(usbdev->rwnx_hw->sw_txhdr_cache, txhdr->sw_hdr);
+ dev_kfree_skb(skb);
+ return;
+ }
+ aicwf_bus_txdata(usbdev->bus_if, skb);
+#endif
+}
+
+struct aicwf_tx_priv* aicwf_tx_init(void *arg)
+{
+ struct aicwf_tx_priv* tx_priv;
+
+ tx_priv = kzalloc(sizeof(struct aicwf_tx_priv), GFP_KERNEL);
+ if (!tx_priv)
+ return NULL;
+
+#ifdef AICWF_SDIO_SUPPORT
+ tx_priv->sdiodev = (struct aic_sdio_dev *)arg;
+#else
+ tx_priv->usbdev = (struct aic_usb_dev *)arg;
+#endif
+
+ atomic_set(&tx_priv->aggr_count, 0);
+ tx_priv->aggr_buf = dev_alloc_skb(MAX_AGGR_TXPKT_LEN);
+ if(!tx_priv->aggr_buf) {
+ txrx_err("Alloc bus->txdata_buf failed!\n");
+ kfree(tx_priv);
+ return NULL;
+ }
+ tx_priv->head = tx_priv->aggr_buf->data;
+ tx_priv->tail = tx_priv->aggr_buf->data;
+
+ return tx_priv;
+}
+
+void aicwf_tx_deinit(struct aicwf_tx_priv* tx_priv)
+{
+ if (tx_priv && tx_priv->aggr_buf)
+ dev_kfree_skb(tx_priv->aggr_buf);
+
+ kfree(tx_priv);
+}
+
+static bool aicwf_another_ptk(struct sk_buff *skb)
+{
+ u8 *data;
+ u16 aggr_len = 0;
+
+ if(skb->data == NULL || skb->len == 0) {
+ return false;
+ }
+ data = skb->data;
+ aggr_len = (*skb->data | (*(skb->data + 1) << 8));
+ if(aggr_len == 0) {
+ return false;
+ }
+
+ return true;
+}
+
+#if 0//AIDEN
+void rwnx_frame_parser(char* tag, char* data, unsigned long len);
+#endif
+
+#ifdef CONFIG_RX_TASKLET
+int aicwf_tasklet_rxframes(struct aicwf_rx_priv *rx_priv)
+{
+
+ int ret = 0;
+ unsigned long flags = 0;
+ struct sk_buff *skb = NULL; /* Packet for event or data frames */
+ u16 pkt_len = 0;
+ //struct sk_buff *skb_inblock = NULL;
+ u16 aggr_len = 0, adjust_len = 0;
+ u8 *data = NULL;
+ u8_l *msg = NULL;
+
+ while (1) {
+ //aicwf_usb_rx_submit_all_urb_(rx_priv->usbdev);
+ spin_lock_irqsave(&rx_priv->rxqlock, flags);
+ if(aicwf_is_framequeue_empty(&rx_priv->rxq)) {
+ usb_info("no more rxdata\n");
+ spin_unlock_irqrestore(&rx_priv->rxqlock,flags);
+ break;
+ }
+ skb = aicwf_frame_dequeue(&rx_priv->rxq);
+ spin_unlock_irqrestore(&rx_priv->rxqlock, flags);
+ if (skb == NULL) {
+ txrx_err("skb_error\r\n");
+ break;
+ }
+ data = skb->data;
+ pkt_len = (*skb->data | (*(skb->data + 1) << 8));
+ //printk("p:%d, s:%d , %x\n", pkt_len, skb->len, data[2]);
+ if (pkt_len > 1600) {
+ dev_kfree_skb(skb);
+ atomic_dec(&rx_priv->rx_cnt);
+ continue;
+ }
+
+ if((skb->data[2] & USB_TYPE_CFG) != USB_TYPE_CFG) { // type : data
+#if 0
+ aggr_len = pkt_len + RX_HWHRD_LEN;
+ if (aggr_len & (RX_ALIGNMENT - 1))
+ adjust_len = roundup(aggr_len, RX_ALIGNMENT);
+ else
+ adjust_len = aggr_len;
+
+ skb_inblock = __dev_alloc_skb(aggr_len + CCMP_OR_WEP_INFO, GFP_ATOMIC);//8 is for ccmp mic or wep icv
+ if(skb_inblock == NULL){
+ txrx_err("no more space! skip!\n");
+ skb_pull(skb, adjust_len);
+ continue;
+ }
+
+ skb_put(skb_inblock, aggr_len);
+ memcpy(skb_inblock->data, data, aggr_len);
+#if 0//AIDEN
+ rwnx_frame_parser((char*)__func__, skb_inblock->data + 60, aggr_len - 60);
+#endif
+#endif
+ rwnx_rxdataind_aicwf(rx_priv->usbdev->rwnx_hw, skb, (void *)rx_priv);
+ ///TODO: here need to add rx data process
+ //skb_pull(skb, adjust_len);
+ }
+ else { // type : config
+ aggr_len = pkt_len;
+ if (aggr_len & (RX_ALIGNMENT - 1))
+ adjust_len = roundup(aggr_len, RX_ALIGNMENT);
+ else
+ adjust_len = aggr_len;
+
+ msg = kmalloc(aggr_len+4, GFP_ATOMIC);
+ if(msg == NULL){
+ txrx_err("no more space for msg!\n");
+ aicwf_dev_skb_free(skb);
+ return -EBADE;
+ }
+ memcpy(msg, data, aggr_len + 4);
+ if((*(msg + 2) & 0x7f) == USB_TYPE_CFG_CMD_RSP)
+ rwnx_rx_handle_msg(rx_priv->usbdev->rwnx_hw, (struct ipc_e2a_msg *)(msg + 4));
+
+ if((*(msg + 2) & 0x7f) == USB_TYPE_CFG_DATA_CFM)
+ aicwf_usb_host_tx_cfm_handler(&(rx_priv->usbdev->rwnx_hw->usb_env), (u32 *)(msg + 4));
+ skb_pull(skb, adjust_len+4);
+ kfree(msg);
+ dev_kfree_skb(skb);
+ }
+
+ //dev_kfree_skb(skb);
+ atomic_dec(&rx_priv->rx_cnt);
+ }
+
+ return ret;
+ }
+#endif
+
+int aicwf_process_rxframes(struct aicwf_rx_priv *rx_priv)
+{
+#ifdef AICWF_SDIO_SUPPORT
+ int ret = 0;
+ unsigned long flags = 0;
+ struct sk_buff *skb = NULL;
+ u16 pkt_len = 0;
+ struct sk_buff *skb_inblock = NULL;
+ u16 aggr_len = 0, adjust_len = 0;
+ u8 *data = NULL;
+ u8_l *msg = NULL;
+
+ while (1) {
+ spin_lock_irqsave(&rx_priv->rxqlock, flags);
+ if(aicwf_is_framequeue_empty(&rx_priv->rxq)) {
+ spin_unlock_irqrestore(&rx_priv->rxqlock,flags);
+ break;
+ }
+ skb = aicwf_frame_dequeue(&rx_priv->rxq);
+ spin_unlock_irqrestore(&rx_priv->rxqlock,flags);
+ if (skb == NULL) {
+ txrx_err("skb_error\r\n");
+ break;
+ }
+ while(aicwf_another_ptk(skb)) {
+ data = skb->data;
+ pkt_len = (*skb->data | (*(skb->data + 1) << 8));
+
+ if((skb->data[2] & SDIO_TYPE_CFG) != SDIO_TYPE_CFG) { // type : data
+ aggr_len = pkt_len + RX_HWHRD_LEN;
+
+ if (aggr_len & (RX_ALIGNMENT - 1))
+ adjust_len = roundup(aggr_len, RX_ALIGNMENT);
+ else
+ adjust_len = aggr_len;
+
+ skb_inblock = __dev_alloc_skb(aggr_len + CCMP_OR_WEP_INFO, GFP_KERNEL);
+ if(skb_inblock == NULL){
+ txrx_err("no more space! skip\n");
+ skb_pull(skb, adjust_len);
+ continue;
+ }
+
+ skb_put(skb_inblock, aggr_len);
+ memcpy(skb_inblock->data, data, aggr_len);
+ rwnx_rxdataind_aicwf(rx_priv->sdiodev->rwnx_hw, skb_inblock, (void *)rx_priv);
+ skb_pull(skb, adjust_len);
+ }
+ else { // type : config
+ aggr_len = pkt_len;
+
+ if (aggr_len & (RX_ALIGNMENT - 1))
+ adjust_len = roundup(aggr_len, RX_ALIGNMENT);
+ else
+ adjust_len = aggr_len;
+
+ msg = kmalloc(aggr_len+4, GFP_KERNEL);
+ if(msg == NULL){
+ txrx_err("no more space for msg!\n");
+ aicwf_dev_skb_free(skb);
+ return -EBADE;
+ }
+
+ memcpy(msg, data, aggr_len + 4);
+ if((*(msg + 2) & 0x7f) == SDIO_TYPE_CFG_CMD_RSP)
+ rwnx_rx_handle_msg(rx_priv->sdiodev->rwnx_hw, (struct ipc_e2a_msg *)(msg + 4));
+
+ if((*(msg + 2) & 0x7f) == SDIO_TYPE_CFG_DATA_CFM)
+ aicwf_sdio_host_tx_cfm_handler(&(rx_priv->sdiodev->rwnx_hw->sdio_env), (u32 *)(msg + 4));
+ skb_pull(skb, adjust_len+4);
+ kfree(msg);
+ }
+ }
+
+ dev_kfree_skb(skb);
+ atomic_dec(&rx_priv->rx_cnt);
+ }
+
+ aicwf_sdio_pwr_stctl(rx_priv->sdiodev, SDIO_ACTIVE_ST);
+
+ return ret;
+#else //AICWF_USB_SUPPORT
+ int ret = 0;
+ unsigned long flags = 0;
+#ifndef CONFIG_PREALLOC_RX_SKB
+ struct sk_buff *skb = NULL;/* Packet for event or data frames */
+#else
+ struct sk_buff *skb_inblock = NULL;
+#endif
+ u16 pkt_len = 0;
+ u16 aggr_len = 0, adjust_len = 0;
+ u8 *data = NULL;
+ u8_l *msg = NULL;
+//#ifdef CONFIG_USB_RX_AGGR
+ struct sk_buff *skb_inblock = NULL;
+ u8 cnt = 0 ;
+//#endif
+#ifdef CONFIG_PREALLOC_RX_SKB
+ struct rx_buff *buffer = NULL;
+ while (1) {
+ spin_lock_irqsave(&rx_priv->rxqlock, flags);
+ if (!rx_priv->rxq.qcnt) {
+ usb_info("no more rxdata\n");
+ spin_unlock_irqrestore(&rx_priv->rxqlock,flags);
+ break;
+ }
+ buffer = rxbuff_dequeue(&rx_priv->rxq);
+ spin_unlock_irqrestore(&rx_priv->rxqlock, flags);
+
+ if (buffer == NULL) {
+ txrx_err("skb_error\r\n");
+ ASSERT_ERR(1);
+ break;
+ }
+ data = buffer->data;
+ pkt_len = (*data | (*(data + 1) << 8));
+ //printk("p:%d, s:%d , %x\n", pkt_len, skb->len, data[2]);
+ if (pkt_len > 1600) {
+ aicwf_prealloc_rxbuff_free(buffer, &rx_priv->rxbuff_lock);
+ atomic_dec(&rx_priv->rx_cnt);
+ continue;
+ }
+
+ if((data[2] & USB_TYPE_CFG) != USB_TYPE_CFG) { // type : data
+ skb_inblock = __dev_alloc_skb(pkt_len + RX_HWHRD_LEN + CCMP_OR_WEP_INFO, GFP_KERNEL);
+ if (skb_inblock == NULL) {
+ txrx_err("no more space! skip\n");
+ aicwf_prealloc_rxbuff_free(buffer, &rx_priv->rxbuff_lock);
+ atomic_dec(&rx_priv->rx_cnt);
+ continue;
+ }
+
+ skb_put(skb_inblock, pkt_len + RX_HWHRD_LEN);
+ memcpy(skb_inblock->data, data, pkt_len + RX_HWHRD_LEN);
+ rwnx_rxdataind_aicwf(rx_priv->usbdev->rwnx_hw, skb_inblock, (void *)rx_priv);
+ }
+ else { // type : config
+ aggr_len = pkt_len;
+ if (aggr_len & (RX_ALIGNMENT - 1))
+ adjust_len = roundup(aggr_len, RX_ALIGNMENT);
+ else
+ adjust_len = aggr_len;
+
+ msg = kmalloc(aggr_len+4, GFP_KERNEL);
+ if(msg == NULL){
+ txrx_err("no more space for msg!\n");
+ aicwf_prealloc_rxbuff_free(buffer, &rx_priv->rxbuff_lock);
+ return -EBADE;
+ }
+ memcpy(msg, data, aggr_len + 4);
+
+ if(((*(msg + 2) & 0x7f) == USB_TYPE_CFG_CMD_RSP) && (rx_priv->usbdev->bus_if->state != (int)USB_DOWN_ST))
+ rwnx_rx_handle_msg(rx_priv->usbdev->rwnx_hw, (struct ipc_e2a_msg *)(msg + 4));
+
+ if((*(msg + 2) & 0x7f) == USB_TYPE_CFG_DATA_CFM)
+ aicwf_usb_host_tx_cfm_handler(&(rx_priv->usbdev->rwnx_hw->usb_env), (u32 *)(msg + 4));
+
+ if ((*(msg + 2) & 0x7f) == USB_TYPE_CFG_PRINT)
+ rwnx_rx_handle_print(rx_priv->usbdev->rwnx_hw, msg + 4, aggr_len);
+
+ kfree(msg);
+ }
+
+ aicwf_prealloc_rxbuff_free(buffer, &rx_priv->rxbuff_lock);
+ atomic_dec(&rx_priv->rx_cnt);
+ }
+
+#else
+ if(aicwf_usb_rx_aggr){
+ while (1) {
+ spin_lock_irqsave(&rx_priv->rxqlock, flags);
+ if(aicwf_is_framequeue_empty(&rx_priv->rxq)) {
+ usb_info("no more rxdata\n");
+ spin_unlock_irqrestore(&rx_priv->rxqlock,flags);
+ break;
+ }
+ skb = aicwf_frame_dequeue(&rx_priv->rxq);
+ spin_unlock_irqrestore(&rx_priv->rxqlock, flags);
+
+ if (skb == NULL) {
+ txrx_err("skb_error\r\n");
+ ASSERT_ERR(1);
+ break;
+ }
+ while(aicwf_another_ptk(skb)) {
+ cnt++;
+ if (cnt > 30) {
+ //printk("%s err, break %d\n", __func__, cnt);
+ //break;
+ }
+ data = skb->data;
+ pkt_len = (*skb->data | (*(skb->data + 1) << 8));
+ //printk("p:%d, s:%d , %x\n", pkt_len, skb->len, data[2]);
+ if (pkt_len > 1600) {
+ dev_kfree_skb(skb);
+ atomic_dec(&rx_priv->rx_cnt);
+ continue;
+ }
+
+ if((skb->data[2] & USB_TYPE_CFG) != USB_TYPE_CFG) { // type : data
+ aggr_len = pkt_len + RX_HWHRD_LEN;
+ adjust_len = aggr_len;
+ skb_inblock = __dev_alloc_skb(aggr_len + CCMP_OR_WEP_INFO, GFP_KERNEL);//8 is for ccmp mic or wep icv
+ if(skb_inblock == NULL){
+ txrx_err("no more space! skip!\n");
+ skb_pull(skb, adjust_len);
+ continue;
+ }
+
+ skb_put(skb_inblock, aggr_len);
+ memcpy(skb_inblock->data, data, aggr_len);
+ rwnx_rxdataind_aicwf(rx_priv->usbdev->rwnx_hw, skb_inblock, (void *)rx_priv);
+
+ ///TODO: here need to add rx data process
+
+ skb_pull(skb, adjust_len);
+
+ }
+ else { // type : config
+ aggr_len = pkt_len;
+ if (aggr_len & (RX_ALIGNMENT - 1))
+ adjust_len = roundup(aggr_len, RX_ALIGNMENT);
+ else
+ adjust_len = aggr_len;
+
+ msg = kmalloc(aggr_len+4, GFP_KERNEL);
+ if(msg == NULL){
+ txrx_err("no more space for msg!\n");
+ aicwf_dev_skb_free(skb);
+ return -EBADE;
+ }
+ memcpy(msg, data, aggr_len + 4);
+
+ if(((*(msg + 2) & 0x7f) == USB_TYPE_CFG_CMD_RSP) && (rx_priv->usbdev->bus_if->state != (int)USB_DOWN_ST))
+ rwnx_rx_handle_msg(rx_priv->usbdev->rwnx_hw, (struct ipc_e2a_msg *)(msg + 4));
+
+ if((*(msg + 2) & 0x7f) == USB_TYPE_CFG_DATA_CFM)
+ aicwf_usb_host_tx_cfm_handler(&(rx_priv->usbdev->rwnx_hw->usb_env), (u32 *)(msg + 4));
+
+ if ((*(msg + 2) & 0x7f) == USB_TYPE_CFG_PRINT)
+ rwnx_rx_handle_print(rx_priv->usbdev->rwnx_hw, msg + 4, aggr_len);
+
+
+ skb_pull(skb, adjust_len+4);
+ kfree(msg);
+
+ }
+ }
+ dev_kfree_skb(skb);
+ atomic_dec(&rx_priv->rx_cnt);
+ }
+ }else{
+ while (1) {
+ spin_lock_irqsave(&rx_priv->rxqlock, flags);
+ if(aicwf_is_framequeue_empty(&rx_priv->rxq)) {
+ usb_info("no more rxdata\n");
+ spin_unlock_irqrestore(&rx_priv->rxqlock,flags);
+ break;
+ }
+ skb = aicwf_frame_dequeue(&rx_priv->rxq);
+ spin_unlock_irqrestore(&rx_priv->rxqlock, flags);
+
+ if (skb == NULL) {
+ txrx_err("skb_error\r\n");
+ ASSERT_ERR(1);
+ break;
+ }
+ data = skb->data;
+ pkt_len = (*skb->data | (*(skb->data + 1) << 8));
+ //printk("p:%d, s:%d , %x\n", pkt_len, skb->len, data[2]);
+ if (pkt_len > 1600) {
+ dev_kfree_skb(skb);
+ atomic_dec(&rx_priv->rx_cnt);
+ continue;
+ }
+
+ if((skb->data[2] & USB_TYPE_CFG) != USB_TYPE_CFG) { // type : data
+ rwnx_rxdataind_aicwf(rx_priv->usbdev->rwnx_hw, skb, (void *)rx_priv);
+ }
+ else { // type : config
+ aggr_len = pkt_len;
+ if (aggr_len & (RX_ALIGNMENT - 1))
+ adjust_len = roundup(aggr_len, RX_ALIGNMENT);
+ else
+ adjust_len = aggr_len;
+
+ msg = kmalloc(aggr_len+4, GFP_KERNEL);
+ if(msg == NULL){
+ txrx_err("no more space for msg!\n");
+ aicwf_dev_skb_free(skb);
+ return -EBADE;
+ }
+ memcpy(msg, data, aggr_len + 4);
+
+ if(((*(msg + 2) & 0x7f) == USB_TYPE_CFG_CMD_RSP) && (rx_priv->usbdev->bus_if->state != (int)USB_DOWN_ST))
+ rwnx_rx_handle_msg(rx_priv->usbdev->rwnx_hw, (struct ipc_e2a_msg *)(msg + 4));
+
+ if((*(msg + 2) & 0x7f) == USB_TYPE_CFG_DATA_CFM)
+ aicwf_usb_host_tx_cfm_handler(&(rx_priv->usbdev->rwnx_hw->usb_env), (u32 *)(msg + 4));
+
+ if ((*(msg + 2) & 0x7f) == USB_TYPE_CFG_PRINT)
+ rwnx_rx_handle_print(rx_priv->usbdev->rwnx_hw, msg + 4, aggr_len);
+
+ skb_pull(skb, adjust_len+4);
+ kfree(msg);
+ dev_kfree_skb(skb);
+ }
+ atomic_dec(&rx_priv->rx_cnt);
+ }
+ }
+#endif
+
+ return ret;
+#endif //AICWF_SDIO_SUPPORT
+}
+
+
+#ifdef CONFIG_USB_MSG_IN_EP
+int aicwf_process_msg_rxframes(struct aicwf_rx_priv *rx_priv)
+{
+ int ret = 0;
+ unsigned long flags = 0;
+ struct sk_buff *skb = NULL; /* Packet for event or data frames */
+ u16 pkt_len = 0;
+ struct sk_buff *skb_inblock = NULL;
+ u16 aggr_len = 0, adjust_len = 0;
+ u8 *data = NULL;
+ u8_l *msg = NULL;
+
+ while (1) {
+ spin_lock_irqsave(&rx_priv->msg_rxqlock, flags);
+ if(aicwf_is_framequeue_empty(&rx_priv->msg_rxq)) {
+ usb_info("no more rxmsg\n");
+ spin_unlock_irqrestore(&rx_priv->msg_rxqlock,flags);
+ break;
+ }
+ skb = aicwf_frame_dequeue(&rx_priv->msg_rxq);
+ spin_unlock_irqrestore(&rx_priv->msg_rxqlock, flags);
+ if (skb == NULL) {
+ txrx_err("skb_error\r\n");
+ break;
+ }
+ data = skb->data;
+ pkt_len = (*skb->data | (*(skb->data + 1) << 8));
+ //printk("p:%d, s:%d , %x\n", pkt_len, skb->len, data[2]);
+#if 0 //amsdu > 1600
+ if (pkt_len > 1600) {
+ dev_kfree_skb(skb);
+ atomic_dec(&rx_priv->rx_cnt);
+ continue;
+ }
+#endif
+
+ if((skb->data[2] & USB_TYPE_CFG) != USB_TYPE_CFG) { // type : data
+ aggr_len = pkt_len + RX_HWHRD_LEN;
+ if (aggr_len & (RX_ALIGNMENT - 1))
+ adjust_len = roundup(aggr_len, RX_ALIGNMENT);
+ else
+ adjust_len = aggr_len;
+
+ skb_inblock = __dev_alloc_skb(aggr_len + CCMP_OR_WEP_INFO, GFP_KERNEL);//8 is for ccmp mic or wep icv
+ if(skb_inblock == NULL){
+ txrx_err("no more space! skip!\n");
+ skb_pull(skb, adjust_len);
+ continue;
+ }
+
+ skb_put(skb_inblock, aggr_len);
+ memcpy(skb_inblock->data, data, aggr_len);
+#if 0//AIDEN
+ rwnx_frame_parser((char*)__func__, skb_inblock->data + 60, aggr_len - 60);
+#endif
+ rwnx_rxdataind_aicwf(rx_priv->usbdev->rwnx_hw, skb_inblock, (void *)rx_priv);
+ ///TODO: here need to add rx data process
+
+ skb_pull(skb, adjust_len);
+ }
+ else { // type : config
+ aggr_len = pkt_len;
+ if (aggr_len & (RX_ALIGNMENT - 1))
+ adjust_len = roundup(aggr_len, RX_ALIGNMENT);
+ else
+ adjust_len = aggr_len;
+
+ msg = kmalloc(aggr_len+4, GFP_KERNEL);
+ if(msg == NULL){
+ txrx_err("no more space for msg!\n");
+ aicwf_dev_skb_free(skb);
+ return -EBADE;
+ }
+ memcpy(msg, data, aggr_len + 4);
+
+ if(((*(msg + 2) & 0x7f) == USB_TYPE_CFG_CMD_RSP) && (rx_priv->usbdev->bus_if->state != (int)USB_DOWN_ST))
+ rwnx_rx_handle_msg(rx_priv->usbdev->rwnx_hw, (struct ipc_e2a_msg *)(msg + 4));
+
+ if((*(msg + 2) & 0x7f) == USB_TYPE_CFG_DATA_CFM)
+ aicwf_usb_host_tx_cfm_handler(&(rx_priv->usbdev->rwnx_hw->usb_env), (u32 *)(msg + 4));
+
+ if ((*(msg + 2) & 0x7f) == USB_TYPE_CFG_PRINT)
+ rwnx_rx_handle_print(rx_priv->usbdev->rwnx_hw, msg + 4, aggr_len);
+
+
+ skb_pull(skb, adjust_len+4);
+ kfree(msg);
+ }
+
+ dev_kfree_skb(skb);
+ atomic_dec(&rx_priv->msg_rx_cnt);
+ }
+
+ return ret;
+}
+#endif
+
+
+#ifdef AICWF_RX_REORDER
+static struct recv_msdu *aicwf_rxframe_queue_init(struct list_head *q, int qsize)
+{
+ int i;
+ struct recv_msdu *req, *reqs;
+
+ reqs = vmalloc(qsize*sizeof(struct recv_msdu));
+ if (reqs == NULL)
+ return NULL;
+
+ req = reqs;
+ for (i = 0; i < qsize; i++) {
+ INIT_LIST_HEAD(&req->rxframe_list);
+ list_add(&req->rxframe_list, q);
+ req->len = 0;
+ req++;
+ }
+
+ return reqs;
+}
+#endif
+struct aicwf_rx_priv *aicwf_rx_init(void *arg)
+{
+ struct aicwf_rx_priv* rx_priv;
+ rx_priv = kzalloc(sizeof(struct aicwf_rx_priv), GFP_KERNEL);
+ if (!rx_priv)
+ return NULL;
+
+#ifdef AICWF_SDIO_SUPPORT
+ rx_priv->sdiodev = (struct aic_sdio_dev *)arg;
+#else
+ rx_priv->usbdev = (struct aic_usb_dev *)arg;
+#endif
+
+#ifdef CONFIG_PREALLOC_RX_SKB
+ aicwf_rxframe_queue_init_2(&rx_priv->rxq, MAX_RXQLEN);
+#else
+ aicwf_frame_queue_init(&rx_priv->rxq, 1, MAX_RXQLEN);
+#endif
+ spin_lock_init(&rx_priv->rxqlock);
+#ifdef CONFIG_PREALLOC_RX_SKB
+ spin_lock_init(&rx_priv->rxbuff_lock);
+ //aicwf_prealloc_init();
+#endif
+ atomic_set(&rx_priv->rx_cnt, 0);
+
+#ifdef CONFIG_USB_MSG_IN_EP
+ if(rx_priv->usbdev->chipid != PRODUCT_ID_AIC8801 &&
+ rx_priv->usbdev->chipid != PRODUCT_ID_AIC8800D81){
+ aicwf_frame_queue_init(&rx_priv->msg_rxq, 1, MAX_RXQLEN);
+ spin_lock_init(&rx_priv->msg_rxqlock);
+ atomic_set(&rx_priv->msg_rx_cnt, 0);
+ }
+#endif
+
+
+#ifdef AICWF_RX_REORDER
+ INIT_LIST_HEAD(&rx_priv->rxframes_freequeue);
+ spin_lock_init(&rx_priv->freeq_lock);
+ rx_priv->recv_frames = aicwf_rxframe_queue_init(&rx_priv->rxframes_freequeue, MAX_REORD_RXFRAME);
+ if (!rx_priv->recv_frames) {
+ txrx_err("no enough buffer for free recv frame queue!\n");
+ kfree(rx_priv);
+ return NULL;
+ }
+ spin_lock_init(&rx_priv->stas_reord_lock);
+ INIT_LIST_HEAD(&rx_priv->stas_reord_list);
+#endif
+
+ return rx_priv;
+}
+
+#ifdef AICWF_RX_REORDER
+static void aicwf_recvframe_queue_deinit(struct list_head *q)
+{
+ struct recv_msdu *req, *next;
+
+ list_for_each_entry_safe(req, next, q, rxframe_list) {
+ list_del_init(&req->rxframe_list);
+ }
+}
+#endif
+void aicwf_rx_deinit(struct aicwf_rx_priv* rx_priv)
+{
+#ifdef AICWF_RX_REORDER
+ struct reord_ctrl_info *reord_info, *tmp;
+
+ AICWFDBG(LOGINFO, "%s Enter\n", __func__);
+
+ list_for_each_entry_safe(reord_info, tmp,
+ &rx_priv->stas_reord_list, list) {
+ reord_deinit_sta(rx_priv, reord_info);
+ }
+
+#endif
+ AICWFDBG(LOGINFO, "stio rx thread\n");
+#ifdef AICWF_SDIO_SUPPORT
+ if (rx_priv->sdiodev->bus_if->busrx_thread) {
+ complete_all(&rx_priv->sdiodev->bus_if->busrx_trgg);
+ kthread_stop(rx_priv->sdiodev->bus_if->busrx_thread);
+ rx_priv->sdiodev->bus_if->busrx_thread = NULL;
+ }
+#endif
+#ifdef AICWF_USB_SUPPORT
+
+#ifdef CONFIG_RX_TASKLET//AIDEN tasklet
+ tasklet_kill(&rx_priv->usbdev->recv_tasklet);
+#endif
+ if (rx_priv->usbdev->bus_if->busrx_thread) {
+ complete_all(&rx_priv->usbdev->bus_if->busrx_trgg);
+ kthread_stop(rx_priv->usbdev->bus_if->busrx_thread);
+ rx_priv->usbdev->bus_if->busrx_thread = NULL;
+ }
+#ifdef CONFIG_USB_MSG_IN_EP
+ if(rx_priv->usbdev->chipid != PRODUCT_ID_AIC8801 &&
+ rx_priv->usbdev->chipid != PRODUCT_ID_AIC8800D81){
+ if (rx_priv->usbdev->bus_if->msg_busrx_thread) {
+ complete(&rx_priv->usbdev->bus_if->msg_busrx_trgg);
+ kthread_stop(rx_priv->usbdev->bus_if->msg_busrx_thread);
+ rx_priv->usbdev->bus_if->msg_busrx_thread = NULL;
+ }
+ }
+#endif
+
+#endif
+
+#ifdef CONFIG_USB_MSG_IN_EP
+ if(rx_priv->usbdev->chipid != PRODUCT_ID_AIC8801 &&
+ rx_priv->usbdev->chipid != PRODUCT_ID_AIC8800D81){
+ aicwf_frame_queue_flush(&rx_priv->msg_rxq);
+ }
+#endif
+
+#ifdef CONFIG_PREALLOC_RX_SKB
+ rxbuff_queue_flush(rx_priv);
+#else
+ aicwf_frame_queue_flush(&rx_priv->rxq);
+#endif
+
+#ifdef AICWF_RX_REORDER
+ aicwf_recvframe_queue_deinit(&rx_priv->rxframes_freequeue);
+ if (rx_priv->recv_frames)
+ vfree(rx_priv->recv_frames);
+#endif
+
+#ifdef CONFIG_PREALLOC_RX_SKB
+ //aicwf_prealloc_exit();
+#endif
+ kfree(rx_priv);
+ AICWFDBG(LOGINFO, "%s Exit\n", __func__);
+
+}
+
+bool aicwf_rxframe_enqueue(struct device *dev, struct frame_queue *q, struct sk_buff *pkt)
+{
+ return aicwf_frame_enq(dev, q, pkt, 0);
+}
+
+
+void aicwf_dev_skb_free(struct sk_buff *skb)
+{
+ if (!skb)
+ return;
+
+ dev_kfree_skb_any(skb);
+}
+
+static struct sk_buff *aicwf_frame_queue_penq(struct frame_queue *pq, int prio, struct sk_buff *p)
+{
+ struct sk_buff_head *q;
+
+ if (pq->queuelist[prio].qlen >= pq->qmax)
+ return NULL;
+
+ q = &pq->queuelist[prio];
+ __skb_queue_tail(q, p);
+ pq->qcnt++;
+ if (pq->hi_prio < prio)
+ pq->hi_prio = (u16)prio;
+
+ return p;
+}
+
+void aicwf_frame_queue_flush(struct frame_queue *pq)
+{
+ int prio;
+ struct sk_buff_head *q;
+ struct sk_buff *p, *next;
+
+ for (prio = 0; prio < pq->num_prio; prio++)
+ {
+ q = &pq->queuelist[prio];
+ skb_queue_walk_safe(q, p, next) {
+ skb_unlink(p, q);
+ aicwf_dev_skb_free(p);
+ pq->qcnt--;
+ }
+ }
+}
+
+void aicwf_frame_queue_init(struct frame_queue *pq, int num_prio, int max_len)
+{
+ int prio;
+
+ memset(pq, 0, offsetof(struct frame_queue, queuelist) + (sizeof(struct sk_buff_head) * num_prio));
+ pq->num_prio = (u16)num_prio;
+ pq->qmax = (u16)max_len;
+
+ for (prio = 0; prio < num_prio; prio++) {
+ skb_queue_head_init(&pq->queuelist[prio]);
+ }
+}
+
+struct sk_buff *aicwf_frame_queue_peek_tail(struct frame_queue *pq, int *prio_out)
+{
+ int prio;
+
+ if (pq->qcnt == 0)
+ return NULL;
+
+ for (prio = 0; prio < pq->hi_prio; prio++)
+ if (!skb_queue_empty(&pq->queuelist[prio]))
+ break;
+
+ if (prio_out)
+ *prio_out = prio;
+
+ return skb_peek_tail(&pq->queuelist[prio]);
+}
+
+bool aicwf_is_framequeue_empty(struct frame_queue *pq)
+{
+ int prio, len = 0;
+
+ for (prio = 0; prio <= pq->hi_prio; prio++)
+ len += pq->queuelist[prio].qlen;
+
+ if(len > 0)
+ return false;
+ else
+ return true;
+}
+
+struct sk_buff *aicwf_frame_dequeue(struct frame_queue *pq)
+{
+ struct sk_buff_head *q;
+ struct sk_buff *p;
+ int prio;
+
+ if (pq->qcnt == 0)
+ return NULL;
+
+ while ((prio = pq->hi_prio) > 0 && skb_queue_empty(&pq->queuelist[prio]))
+ pq->hi_prio--;
+
+ q = &pq->queuelist[prio];
+ p = __skb_dequeue(q);
+ if (p == NULL)
+ return NULL;
+
+ pq->qcnt--;
+
+ return p;
+}
+
+static struct sk_buff *aicwf_skb_dequeue_tail(struct frame_queue *pq, int prio)
+{
+ struct sk_buff_head *q = &pq->queuelist[prio];
+ struct sk_buff *p = skb_dequeue_tail(q);
+
+ if (!p)
+ return NULL;
+
+ pq->qcnt--;
+
+ return p;
+}
+
+bool aicwf_frame_enq(struct device *dev, struct frame_queue *q, struct sk_buff *pkt, int prio)
+{
+ struct sk_buff *p = NULL;
+ int prio_modified = -1;
+
+ if (q->queuelist[prio].qlen < q->qmax && q->qcnt < q->qmax) {
+ aicwf_frame_queue_penq(q, prio, pkt);
+ return true;
+ }
+ if (q->queuelist[prio].qlen >= q->qmax) {
+ prio_modified = prio;
+ } else if (q->qcnt >= q->qmax) {
+ p = aicwf_frame_queue_peek_tail(q, &prio_modified);
+ if (prio_modified > prio)
+ return false;
+ }
+
+ if (prio_modified >= 0) {
+ if (prio_modified == prio)
+ return false;
+
+ p = aicwf_skb_dequeue_tail(q, prio_modified);
+ aicwf_dev_skb_free(p);
+
+ p = aicwf_frame_queue_penq(q, prio_modified, pkt);
+ if (p == NULL)
+ txrx_err("failed\n");
+ }
+
+ return p != NULL;
+}
+
+#ifdef CONFIG_PREALLOC_RX_SKB
+void rxbuff_free(struct rx_buff *rxbuff)
+{
+ kfree(rxbuff->data);
+ kfree(rxbuff);
+}
+
+struct rx_buff *rxbuff_queue_penq(struct rx_frame_queue *pq, struct rx_buff *p)
+{
+
+ struct list_head *q;
+ if (pq->qcnt >= pq->qmax)
+ return NULL;
+
+ q = &pq->queuelist;
+ list_add_tail(&p->queue,q);
+
+ pq->qcnt++;
+
+ return p;
+}
+
+struct rx_buff *rxbuff_dequeue(struct rx_frame_queue *pq)
+{
+ struct rx_buff *p = NULL;
+
+ if (pq->qcnt == 0) {
+ printk("%s %d, rxq is empty\n", __func__, __LINE__);
+ return NULL;
+ }
+
+ if(list_empty(&pq->queuelist)) {
+ printk("%s %d, rxq is empty\n", __func__, __LINE__);
+ return NULL;
+ } else {
+ p = list_first_entry(&pq->queuelist, struct rx_buff, queue);
+ list_del_init(&p->queue);
+ pq->qcnt--;
+ }
+
+ return p;
+}
+
+bool aicwf_rxbuff_enqueue(struct device *dev, struct rx_frame_queue *rxq, struct rx_buff *pkt)
+{
+// struct rx_buff *p = NULL;
+
+ if ((rxq == NULL) || (pkt == NULL)) {
+ printk("%s %d, rxq or pkt is NULL\n", __func__, __LINE__);
+ return false;
+ }
+
+ if (rxq->qcnt < rxq->qmax) {
+ if (rxbuff_queue_penq(rxq, pkt)) {
+ return true;
+ } else {
+ printk("%s %d, rxbuff enqueue fail\n", __func__, __LINE__);
+ return false;
+ }
+ } else {
+ printk("%s %d, rxq or pkt is full\n", __func__, __LINE__);
+ return false;
+ }
+}
+#endif
+
diff --git a/drivers/net/wireless/aic8800/aic8800_fdrv/aicwf_txrxif.h b/drivers/net/wireless/aic8800/aic8800_fdrv/aicwf_txrxif.h
new file mode 100644
index 000000000000..5523988068bc
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic8800_fdrv/aicwf_txrxif.h
@@ -0,0 +1,291 @@
+/**
+ * aicwf_txrxif.h
+ *
+ * bus function declarations
+ *
+ * Copyright (C) AICSemi 2018-2020
+ */
+
+#ifndef _AICWF_TXRXIF_H_
+#define _AICWF_TXRXIF_H_
+
+#include <linux/skbuff.h>
+#include <linux/sched.h>
+#include "ipc_shared.h"
+#include "aicwf_rx_prealloc.h"
+#ifdef AICWF_SDIO_SUPPORT
+#include "aicwf_sdio.h"
+#else
+#include "aicwf_usb.h"
+#endif
+
+#define CMD_BUF_MAX 1536
+#define DATA_BUF_MAX 2048
+#define TXPKT_BLOCKSIZE 512
+#define MAX_AGGR_TXPKT_LEN (1536*64)
+#define CMD_TX_TIMEOUT 5000
+#define TX_ALIGNMENT 4
+
+#ifdef CONFIG_USB_TX_AGGR
+#define MAX_USB_AGGR_TXPKT_LEN (1536*15)
+#endif
+
+#define RX_HWHRD_LEN 60 //58->60 word allined
+#define CCMP_OR_WEP_INFO 8
+#define MAX_RXQLEN 2000
+#define RX_ALIGNMENT 4
+
+#define DEBUG_ERROR_LEVEL 0
+#define DEBUG_DEBUG_LEVEL 1
+#define DEBUG_INFO_LEVEL 2
+
+#define DBG_LEVEL DEBUG_DEBUG_LEVEL
+
+#define txrx_err(fmt, ...) pr_err("txrx_err:<%s,%d>: " fmt, __func__, __LINE__, ##__VA_ARGS__)
+#define sdio_err(fmt, ...) pr_err("sdio_err:<%s,%d>: " fmt, __func__, __LINE__, ##__VA_ARGS__)
+#define usb_err(fmt, ...) pr_err("usb_err:<%s,%d>: " fmt, __func__, __LINE__, ##__VA_ARGS__)
+#if DBG_LEVEL >= DEBUG_DEBUG_LEVEL
+#define txrx_dbg(fmt, ...) printk("txrx: " fmt, ##__VA_ARGS__)
+#define sdio_dbg(fmt, ...) printk("aicsdio: " fmt, ##__VA_ARGS__)
+#define usb_dbg(fmt, ...) printk("aicusb: " fmt, ##__VA_ARGS__)
+#else
+#define txrx_dbg(fmt, ...)
+#define sdio_dbg(fmt, ...)
+#define usb_dbg(fmt, ...)
+#endif
+#if DBG_LEVEL >= DEBUG_INFO_LEVEL
+#define txrx_info(fmt, ...) printk("aicsdio: " fmt, ##__VA_ARGS__)
+#define sdio_info(fmt, ...) printk("aicsdio: " fmt, ##__VA_ARGS__)
+#define usb_info(fmt, ...) printk("aicusb: " fmt, ##__VA_ARGS__)
+#else
+#define txrx_info(fmt, ...)
+#define sdio_info(fmt, ...)
+#define usb_info(fmt, ...)
+#endif
+
+enum aicwf_bus_state {
+ BUS_DOWN_ST,
+ BUS_UP_ST
+};
+
+struct aicwf_bus_ops {
+ int (*start) (struct device * dev);
+ void (*stop) (struct device * dev);
+ int (*txdata) (struct device * dev, struct sk_buff * skb);
+ int (*txmsg) (struct device * dev, u8 * msg, uint len);
+};
+
+struct frame_queue {
+ u16 num_prio;
+ u16 hi_prio;
+ u16 qmax; /* max number of queued frames */
+ u16 qcnt;
+ struct sk_buff_head queuelist[8];
+};
+
+#ifdef CONFIG_PREALLOC_RX_SKB
+struct rx_frame_queue {
+ u16 qmax; /* max number of queued frames */
+ u16 qcnt;
+ struct list_head queuelist;
+};
+#endif
+
+struct aicwf_bus {
+ union {
+ struct aic_sdio_dev *sdio;
+ struct aic_usb_dev *usb;
+ } bus_priv;
+ struct device *dev;
+ struct aicwf_bus_ops *ops;
+ enum aicwf_bus_state state;
+ u8 *cmd_buf;
+ struct completion bustx_trgg;
+ struct completion busrx_trgg;
+#ifdef CONFIG_USB_MSG_IN_EP
+ struct completion msg_busrx_trgg;
+#endif
+
+ struct task_struct *bustx_thread;
+ struct task_struct *busrx_thread;
+#ifdef CONFIG_USB_MSG_IN_EP
+ struct task_struct *msg_busrx_thread;
+#endif
+
+};
+
+struct aicwf_tx_priv {
+#ifdef AICWF_SDIO_SUPPORT
+ struct aic_sdio_dev *sdiodev;
+ int fw_avail_bufcnt;
+ //for cmd tx
+ u8 *cmd_buf;
+ uint cmd_len;
+ bool cmd_txstate;
+ bool cmd_tx_succ;
+ struct semaphore cmd_txsema;
+ wait_queue_head_t cmd_txdone_wait;
+ //for data tx
+ atomic_t tx_pktcnt;
+
+ struct frame_queue txq;
+ spinlock_t txqlock;
+ struct semaphore txctl_sema;
+#endif
+#ifdef AICWF_USB_SUPPORT
+ struct aic_usb_dev *usbdev;
+#ifdef CONFIG_USB_TX_AGGR
+ int fw_avail_bufcnt;
+
+ //for data tx
+ atomic_t tx_pktcnt;
+
+ struct frame_queue txq;
+ spinlock_t txqlock;
+ spinlock_t txdlock;
+#endif
+#endif//AICWF_USB_SUPPORT
+ struct sk_buff *aggr_buf;
+ atomic_t aggr_count;
+ u8 *head;
+ u8 *tail;
+};
+
+
+#ifdef AICWF_RX_REORDER
+#define MAX_REORD_RXFRAME 250
+#define REORDER_UPDATE_TIME 500//50
+#define AICWF_REORDER_WINSIZE 64
+//SN_LESS(a, b) a-b<0 is ture
+#define SN_LESS(a, b) (((a-b)&0x800)!=0)
+#define SN_EQUAL(a, b) (a == b)
+
+struct reord_ctrl {
+ struct aicwf_rx_priv *rx_priv;
+ u8 enable;
+ u16 ind_sn;
+ u8 wsize_b;
+ spinlock_t reord_list_lock;
+ struct list_head reord_list;
+ struct timer_list reord_timer;
+ struct work_struct reord_timer_work;
+};
+
+struct reord_ctrl_info {
+ u8 mac_addr[6];
+ struct reord_ctrl preorder_ctrl[8];
+ struct list_head list;
+};
+
+struct recv_msdu {
+ struct sk_buff *pkt;
+ u8 tid;
+ u8 forward;
+ u16 seq_num;
+ uint len;
+ u8 *rx_data;
+ //for pending rx reorder list
+ struct list_head reord_pending_list;
+ //for total frame list, when rxframe from busif, dequeue, when submit frame to net, enqueue
+ struct list_head rxframe_list;
+ struct reord_ctrl *preorder_ctrl;
+};
+#endif
+
+struct aicwf_rx_priv {
+#ifdef AICWF_SDIO_SUPPORT
+ struct aic_sdio_dev *sdiodev;
+#endif
+#ifdef AICWF_USB_SUPPORT
+ struct aic_usb_dev *usbdev;
+#endif
+
+ void *rwnx_vif;
+ atomic_t rx_cnt;
+ u32 data_len;
+ spinlock_t rxqlock;
+#ifdef CONFIG_PREALLOC_RX_SKB
+ struct rx_frame_queue rxq;
+#else
+ struct frame_queue rxq;
+#endif
+
+#ifdef CONFIG_USB_MSG_IN_EP
+ atomic_t msg_rx_cnt;
+ spinlock_t msg_rxqlock;
+ struct frame_queue msg_rxq;
+#endif
+
+
+#ifdef AICWF_RX_REORDER
+ spinlock_t freeq_lock;
+ struct list_head rxframes_freequeue;
+ struct list_head stas_reord_list;
+ spinlock_t stas_reord_lock;
+ struct recv_msdu *recv_frames;
+#endif
+
+#ifdef CONFIG_PREALLOC_RX_SKB
+ spinlock_t rxbuff_lock;
+#endif
+
+};
+
+static inline int aicwf_bus_start(struct aicwf_bus *bus)
+{
+ return bus->ops->start(bus->dev);
+}
+
+static inline void aicwf_bus_stop(struct aicwf_bus *bus)
+{
+ bus->ops->stop(bus->dev);
+}
+
+static inline int aicwf_bus_txdata(struct aicwf_bus *bus, struct sk_buff *skb)
+{
+ return bus->ops->txdata(bus->dev, skb);
+}
+
+static inline int aicwf_bus_txmsg(struct aicwf_bus *bus, u8 *msg, uint len)
+{
+ return bus->ops->txmsg(bus->dev, msg, len);
+}
+
+static inline void aicwf_sched_timeout(u32 millisec)
+{
+ ulong timeout = 0, expires = 0;
+ expires = jiffies + msecs_to_jiffies(millisec);
+ timeout = millisec;
+
+ while (timeout) {
+ timeout = schedule_timeout(timeout);
+ if (time_after(jiffies, expires))
+ break;
+ }
+}
+
+int aicwf_bus_init(uint bus_hdrlen, struct device *dev);
+void aicwf_bus_deinit(struct device *dev);
+void aicwf_tx_deinit(struct aicwf_tx_priv* tx_priv);
+void aicwf_rx_deinit(struct aicwf_rx_priv* rx_priv);
+struct aicwf_tx_priv* aicwf_tx_init(void *arg);
+struct aicwf_rx_priv* aicwf_rx_init(void *arg);
+void aicwf_frame_queue_init(struct frame_queue *pq, int num_prio, int max_len);
+void aicwf_frame_queue_flush(struct frame_queue *pq);
+bool aicwf_frame_enq(struct device *dev, struct frame_queue *q, struct sk_buff *pkt, int prio);
+bool aicwf_rxframe_enqueue(struct device *dev, struct frame_queue *q, struct sk_buff *pkt);
+bool aicwf_is_framequeue_empty(struct frame_queue *pq);
+void aicwf_frame_tx(void *dev, struct sk_buff *skb);
+void aicwf_dev_skb_free(struct sk_buff *skb);
+struct sk_buff *aicwf_frame_dequeue(struct frame_queue *pq);
+struct sk_buff *aicwf_frame_queue_peek_tail(struct frame_queue *pq, int *prio_out);
+#ifdef CONFIG_PREALLOC_RX_SKB
+void rxbuff_queue_flush(struct aicwf_rx_priv* rx_priv);
+void aicwf_rxframe_queue_init_2(struct rx_frame_queue *pq, int max_len);
+void rxbuff_free(struct rx_buff *rxbuff);
+struct rx_buff *rxbuff_dequeue(struct rx_frame_queue *pq);
+bool aicwf_rxbuff_enqueue(struct device *dev, struct rx_frame_queue *rxq, struct rx_buff *pkt);
+extern struct aicwf_rx_buff_list aic_rx_buff_list;
+#endif
+
+#endif /* _AICWF_TXRXIF_H_ */
diff --git a/drivers/net/wireless/aic8800/aic8800_fdrv/aicwf_usb.c b/drivers/net/wireless/aic8800/aic8800_fdrv/aicwf_usb.c
new file mode 100644
index 000000000000..607ff0ec4734
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic8800_fdrv/aicwf_usb.c
@@ -0,0 +1,2318 @@
+/**
+ * aicwf_usb.c
+ *
+ * USB function declarations
+ *
+ * Copyright (C) AICSemi 2018-2020
+ */
+
+#include <linux/usb.h>
+#include <linux/kthread.h>
+#include "aicwf_txrxif.h"
+#include "aicwf_usb.h"
+#include "rwnx_tx.h"
+#include "rwnx_defs.h"
+#include "usb_host.h"
+#include "rwnx_platform.h"
+
+#ifdef CONFIG_GPIO_WAKEUP
+#ifdef CONFIG_PLATFORM_ROCKCHIP
+#include <linux/rfkill-wlan.h>
+#endif
+static int wakeup_enable;
+static u32 hostwake_irq_num;
+atomic_t irq_count;
+spinlock_t irq_lock;
+#endif
+
+#include <linux/semaphore.h>
+extern struct semaphore aicwf_deinit_sem;
+extern atomic_t aicwf_deinit_atomic;
+#define SEM_TIMOUT 2000
+
+
+#ifdef CONFIG_TXRX_THREAD_PRIO
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0))
+#include "uapi/linux/sched/types.h"
+#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0))
+#include "linux/sched/types.h"
+#else
+#include "linux/sched/rt.h"
+#endif
+
+int bustx_thread_prio = 1;
+module_param(bustx_thread_prio, int, 0);
+int busrx_thread_prio = 1;
+module_param(busrx_thread_prio, int, 0);
+#endif
+
+#ifdef CONFIG_USB_RX_AGGR
+bool aicwf_usb_rx_aggr = true;
+#else
+bool aicwf_usb_rx_aggr = false;
+#endif
+atomic_t rx_urb_cnt;
+
+void aicwf_usb_tx_flowctrl(struct rwnx_hw *rwnx_hw, bool state)
+{
+ struct rwnx_vif *rwnx_vif;
+
+ list_for_each_entry(rwnx_vif, &rwnx_hw->vifs, list) {
+ if (!rwnx_vif || !rwnx_vif->ndev || !rwnx_vif->up)
+ continue;
+ if (state)
+ netif_tx_stop_all_queues(rwnx_vif->ndev);//netif_stop_queue(rwnx_vif->ndev);
+ else
+ netif_tx_wake_all_queues(rwnx_vif->ndev);//netif_wake_queue(rwnx_vif->ndev);
+ }
+}
+
+static struct aicwf_usb_buf *aicwf_usb_tx_dequeue(struct aic_usb_dev *usb_dev,
+ struct list_head *q, int *counter, spinlock_t *qlock)
+{
+ unsigned long flags;
+ struct aicwf_usb_buf *usb_buf;
+
+ spin_lock_irqsave(qlock, flags);
+ if (list_empty(q)) {
+ usb_buf = NULL;
+ } else {
+ usb_buf = list_first_entry(q, struct aicwf_usb_buf, list);
+ list_del_init(&usb_buf->list);
+ if (counter)
+ (*counter)--;
+ }
+ spin_unlock_irqrestore(qlock, flags);
+ return usb_buf;
+}
+
+static void aicwf_usb_tx_queue(struct aic_usb_dev *usb_dev,
+ struct list_head *q, struct aicwf_usb_buf *usb_buf, int *counter,
+ spinlock_t *qlock)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(qlock, flags);
+ list_add_tail(&usb_buf->list, q);
+ (*counter)++;
+ spin_unlock_irqrestore(qlock, flags);
+}
+
+static struct aicwf_usb_buf *aicwf_usb_rx_buf_get(struct aic_usb_dev *usb_dev)
+{
+ unsigned long flags;
+ struct aicwf_usb_buf *usb_buf;
+
+ spin_lock_irqsave(&usb_dev->rx_free_lock, flags);
+ if (list_empty(&usb_dev->rx_free_list)) {
+ usb_buf = NULL;
+ } else {
+ usb_buf = list_first_entry(&usb_dev->rx_free_list, struct aicwf_usb_buf, list);
+ list_del_init(&usb_buf->list);
+ }
+ spin_unlock_irqrestore(&usb_dev->rx_free_lock, flags);
+ return usb_buf;
+}
+
+static void aicwf_usb_rx_buf_put(struct aic_usb_dev *usb_dev, struct aicwf_usb_buf *usb_buf)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&usb_dev->rx_free_lock, flags);
+ list_add_tail(&usb_buf->list, &usb_dev->rx_free_list);
+ spin_unlock_irqrestore(&usb_dev->rx_free_lock, flags);
+}
+
+#ifdef CONFIG_USB_MSG_IN_EP
+static struct aicwf_usb_buf *aicwf_usb_msg_rx_buf_get(struct aic_usb_dev *usb_dev)
+{
+ unsigned long flags;
+ struct aicwf_usb_buf *usb_buf;
+
+ spin_lock_irqsave(&usb_dev->msg_rx_free_lock, flags);
+ if (list_empty(&usb_dev->msg_rx_free_list)) {
+ usb_buf = NULL;
+ } else {
+ usb_buf = list_first_entry(&usb_dev->msg_rx_free_list, struct aicwf_usb_buf, list);
+ list_del_init(&usb_buf->list);
+ }
+ spin_unlock_irqrestore(&usb_dev->msg_rx_free_lock, flags);
+ return usb_buf;
+}
+
+static void aicwf_usb_msg_rx_buf_put(struct aic_usb_dev *usb_dev, struct aicwf_usb_buf *usb_buf)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&usb_dev->msg_rx_free_lock, flags);
+ list_add_tail(&usb_buf->list, &usb_dev->msg_rx_free_list);
+ spin_unlock_irqrestore(&usb_dev->msg_rx_free_lock, flags);
+}
+#endif
+
+void rwnx_stop_sta_all_queues(struct rwnx_sta *sta, struct rwnx_hw *rwnx_hw)
+{
+ u8 tid;
+ struct rwnx_txq *txq;
+ for(tid=0; tid<8; tid++) {
+ txq = rwnx_txq_sta_get(sta, tid, rwnx_hw);
+ netif_stop_subqueue(txq->ndev, txq->ndev_idx);
+ }
+ }
+
+void rwnx_wake_sta_all_queues(struct rwnx_sta *sta, struct rwnx_hw *rwnx_hw)
+{
+ u8 tid;
+ struct rwnx_txq *txq;
+ for(tid=0; tid<8; tid++) {
+ txq = rwnx_txq_sta_get(sta, tid, rwnx_hw);
+ netif_wake_subqueue(txq->ndev, txq->ndev_idx);
+ }
+ }
+
+static void usb_txc_sta_flowctrl(struct aicwf_usb_buf *usb_buf, struct aic_usb_dev *usb_dev)
+{
+#ifdef CONFIG_PER_STA_FC
+ unsigned long flags;
+ struct rwnx_sta *sta;
+ struct txdesc_api *hostdesc;
+ u8 sta_idx;
+ if(usb_buf->cfm)
+ hostdesc = (struct txdesc_api *)((u8 *)usb_buf->skb + 4);
+ else
+ hostdesc = (struct txdesc_api *)((u8 *)usb_buf->skb->data + 4);
+ //printk("txcpl: sta %d\n", hostdesc->host.staid);
+ sta_idx = hostdesc->host.staid;
+ if(sta_idx < NX_REMOTE_STA_MAX && !(hostdesc->host.flags & TXU_CNTRL_MGMT)) {
+ struct rwnx_vif *vif = NULL;
+ sta = &usb_dev->rwnx_hw->sta_table[sta_idx];
+ vif = usb_dev->rwnx_hw->vif_table[sta->vif_idx];
+ spin_lock_irqsave(&usb_dev->tx_flow_lock, flags);
+ atomic_dec(&usb_dev->rwnx_hw->sta_flowctrl[sta_idx].tx_pending_cnt);
+ //printk("sta:%d, pending:%d, flowctrl=%d\n", sta->sta_idx, sta->tx_pending_cnt, sta->flowctrl);
+ if(RWNX_VIF_TYPE(vif) == NL80211_IFTYPE_AP) {
+ if(atomic_read(&usb_dev->rwnx_hw->sta_flowctrl[sta_idx].tx_pending_cnt) < AICWF_USB_FC_PERSTA_LOW_WATER &&
+ usb_dev->rwnx_hw->sta_flowctrl[sta_idx].flowctrl) {
+ //AICWFDBG(LOGDEBUG, "sta 0x%x:0x%x, %d pending %d, wake\n", sta->mac_addr[4], sta->mac_addr[5], sta->sta_idx, atomic_read(&usb_dev->rwnx_hw->sta_flowctrl[sta_idx].tx_pending_cnt));
+ if(!usb_dev->tbusy)
+ rwnx_wake_sta_all_queues(sta, usb_dev->rwnx_hw);
+ usb_dev->rwnx_hw->sta_flowctrl[sta_idx].flowctrl = 0;
+ }
+ }
+ spin_unlock_irqrestore(&usb_dev->tx_flow_lock, flags);
+ }
+#endif
+}
+
+static void aicwf_usb_tx_complete(struct urb *urb)
+{
+ unsigned long flags;
+ struct aicwf_usb_buf *usb_buf = (struct aicwf_usb_buf *) urb->context;
+ struct aic_usb_dev *usb_dev = usb_buf->usbdev;
+ #ifndef CONFIG_USB_TX_AGGR
+ struct sk_buff *skb;
+ #endif
+
+ usb_txc_sta_flowctrl(usb_buf, usb_dev);
+
+#ifdef CONFIG_USB_ALIGN_DATA
+ if(usb_buf->usb_align_data) {
+ kfree(usb_buf->usb_align_data);
+ }
+#endif
+#ifndef CONFIG_USB_TX_AGGR
+ if (usb_buf->cfm == false) {
+ skb = usb_buf->skb;
+ dev_kfree_skb_any(skb);
+ }
+ #if !defined CONFIG_USB_NO_TRANS_DMA_MAP
+ else {
+ u8 *buf;
+ buf = (u8 *)usb_buf->skb;
+ kfree(buf);
+ }
+ #endif
+ usb_buf->skb = NULL;
+#else
+ AICWFDBG(LOGDEBUG,"tx com %d\n", usb_buf->aggr_cnt);
+ usb_buf->aggr_cnt = 0;
+#endif//CONFIG_USB_TX_AGGR
+ aicwf_usb_tx_queue(usb_dev, &usb_dev->tx_free_list, usb_buf,
+ &usb_dev->tx_free_count, &usb_dev->tx_free_lock);
+
+ spin_lock_irqsave(&usb_dev->tx_flow_lock, flags);
+ if (usb_dev->tx_free_count > AICWF_USB_TX_HIGH_WATER) {
+ if (usb_dev->tbusy) {
+ usb_dev->tbusy = false;
+ aicwf_usb_tx_flowctrl(usb_dev->rwnx_hw, false);
+ }
+ }
+ spin_unlock_irqrestore(&usb_dev->tx_flow_lock, flags);
+}
+
+void aicwf_usb_rx_submit_all_urb_(struct aic_usb_dev *usb_dev);
+
+#ifdef CONFIG_PREALLOC_RX_SKB
+static void aicwf_usb_rx_complete(struct urb *urb)
+ {
+ struct aicwf_usb_buf *usb_buf = (struct aicwf_usb_buf *) urb->context;
+ struct aic_usb_dev *usb_dev = usb_buf->usbdev;
+ struct aicwf_rx_priv* rx_priv = usb_dev->rx_priv;
+ struct rx_buff *rx_buff = NULL;
+ unsigned long flags = 0;
+
+ rx_buff = usb_buf->rx_buff;
+ usb_buf->rx_buff = NULL;
+
+ atomic_dec(&rx_urb_cnt);
+ if(atomic_read(&rx_urb_cnt) < 10){
+ AICWFDBG(LOGDEBUG, "%s %d \r\n", __func__, atomic_read(&rx_urb_cnt));
+ //printk("%s %d \r\n", __func__, atomic_read(&rx_urb_cnt));
+ }
+
+ if(!usb_dev->rwnx_hw){
+ aicwf_prealloc_rxbuff_free(rx_buff, &rx_priv->rxbuff_lock);
+ aicwf_usb_rx_buf_put(usb_dev, usb_buf);
+ AICWFDBG(LOGERROR, "usb_dev->rwnx_hw is not ready \r\n");
+ return;
+ }
+
+ if (urb->actual_length > urb->transfer_buffer_length) {
+ aicwf_prealloc_rxbuff_free(rx_buff, &rx_priv->rxbuff_lock);
+ aicwf_usb_rx_buf_put(usb_dev, usb_buf);
+ aicwf_usb_rx_submit_all_urb_(usb_dev);
+ return;
+ }
+
+ if (urb->status != 0 || !urb->actual_length) {
+ aicwf_prealloc_rxbuff_free(rx_buff, &rx_priv->rxbuff_lock);
+ aicwf_usb_rx_buf_put(usb_dev, usb_buf);
+ if(urb->status < 0){
+ AICWFDBG(LOGDEBUG, "%s urb->status:%d \r\n", __func__, urb->status);
+
+ if(g_rwnx_plat->wait_disconnect_cb == false){
+ g_rwnx_plat->wait_disconnect_cb = true;
+ if(atomic_read(&aicwf_deinit_atomic) > 0){
+ atomic_set(&aicwf_deinit_atomic, 0);
+ AICWFDBG(LOGERROR, "%s in_interrupt:%d in_softirq:%d in_atomic:%d\r\n", __func__, (int)in_interrupt(), (int)in_softirq(), (int)in_atomic());
+ down(&aicwf_deinit_sem);
+ AICWFDBG(LOGINFO, "%s need to wait for disconnect callback \r\n", __func__);
+ }else{
+ g_rwnx_plat->wait_disconnect_cb = false;
+ }
+ }
+
+ return;
+ }else{
+ //schedule_work(&usb_dev->rx_urb_work);
+ aicwf_usb_rx_submit_all_urb_(usb_dev);
+ return;
+ }
+ }
+
+ if (usb_dev->state == USB_UP_ST) {
+ spin_lock_irqsave(&rx_priv->rxqlock, flags);
+
+ if(!aicwf_rxbuff_enqueue(usb_dev->dev, &rx_priv->rxq, rx_buff)){
+ spin_unlock_irqrestore(&rx_priv->rxqlock, flags);
+ usb_err("rx_priv->rxq is over flow!!!\n");
+ aicwf_prealloc_rxbuff_free(rx_buff, &rx_priv->rxbuff_lock);
+ aicwf_usb_rx_buf_put(usb_dev, usb_buf);
+ aicwf_usb_rx_submit_all_urb_(usb_dev);
+ return;
+ }
+ spin_unlock_irqrestore(&rx_priv->rxqlock, flags);
+ atomic_inc(&rx_priv->rx_cnt);
+
+ if(atomic_read(&rx_priv->rx_cnt) == 1){
+ complete(&rx_priv->usbdev->bus_if->busrx_trgg);
+ }
+
+ aicwf_usb_rx_buf_put(usb_dev, usb_buf);
+ aicwf_usb_rx_submit_all_urb_(usb_dev);
+ } else {
+ aicwf_prealloc_rxbuff_free(rx_buff, &rx_priv->rxbuff_lock);
+ aicwf_usb_rx_buf_put(usb_dev, usb_buf);
+ }
+}
+
+#else
+static void aicwf_usb_rx_complete(struct urb *urb)
+{
+ struct aicwf_usb_buf *usb_buf = (struct aicwf_usb_buf *) urb->context;
+ struct aic_usb_dev *usb_dev = usb_buf->usbdev;
+ struct aicwf_rx_priv* rx_priv = usb_dev->rx_priv;
+ struct sk_buff *skb = NULL;
+ unsigned long flags = 0;
+
+ skb = usb_buf->skb;
+ usb_buf->skb = NULL;
+
+ atomic_dec(&rx_urb_cnt);
+ if(atomic_read(&rx_urb_cnt) < 10){
+ AICWFDBG(LOGDEBUG, "%s %d \r\n", __func__, atomic_read(&rx_urb_cnt));
+ //printk("%s %d \r\n", __func__, atomic_read(&rx_urb_cnt));
+ }
+
+ if(!usb_dev->rwnx_hw){
+ aicwf_dev_skb_free(skb);
+ aicwf_usb_rx_buf_put(usb_dev, usb_buf);
+ AICWFDBG(LOGERROR, "usb_dev->rwnx_hw is not ready \r\n");
+ return;
+ }
+
+ if (urb->actual_length > urb->transfer_buffer_length) {
+ aicwf_dev_skb_free(skb);
+ aicwf_usb_rx_buf_put(usb_dev, usb_buf);
+ aicwf_usb_rx_submit_all_urb_(usb_dev);
+ return;
+ }
+
+ if (urb->status != 0 || !urb->actual_length) {
+ aicwf_dev_skb_free(skb);
+ aicwf_usb_rx_buf_put(usb_dev, usb_buf);
+ if(urb->status < 0){
+ AICWFDBG(LOGDEBUG, "%s urb->status:%d \r\n", __func__, urb->status);
+
+ if(g_rwnx_plat->wait_disconnect_cb == false){
+ g_rwnx_plat->wait_disconnect_cb = true;
+ if(atomic_read(&aicwf_deinit_atomic) > 0){
+ atomic_set(&aicwf_deinit_atomic, 0);
+ AICWFDBG(LOGERROR, "%s in_interrupt:%d in_softirq:%d in_atomic:%d\r\n", __func__, (int)in_interrupt(), (int)in_softirq(), (int)in_atomic());
+ down(&aicwf_deinit_sem);
+ AICWFDBG(LOGINFO, "%s need to wait for disconnect callback \r\n", __func__);
+ }else{
+ g_rwnx_plat->wait_disconnect_cb = false;
+ }
+ }
+
+ return;
+ }else{
+ //schedule_work(&usb_dev->rx_urb_work);
+ aicwf_usb_rx_submit_all_urb_(usb_dev);
+ return;
+ }
+ }
+
+ if ((urb->actual_length > 1600 * 30) && (aicwf_usb_rx_aggr)) {
+ printk("r%d\n", urb->actual_length);
+ }
+
+ if (usb_dev->state == USB_UP_ST) {
+
+ skb_put(skb, urb->actual_length);
+
+ spin_lock_irqsave(&rx_priv->rxqlock, flags);
+ if (aicwf_usb_rx_aggr) {
+ skb->len = urb->actual_length;
+ }
+ if(!aicwf_rxframe_enqueue(usb_dev->dev, &rx_priv->rxq, skb)){
+ spin_unlock_irqrestore(&rx_priv->rxqlock, flags);
+ usb_err("rx_priv->rxq is over flow!!!\n");
+ aicwf_dev_skb_free(skb);
+ aicwf_usb_rx_buf_put(usb_dev, usb_buf);
+ aicwf_usb_rx_submit_all_urb_(usb_dev);
+ return;
+ }
+ spin_unlock_irqrestore(&rx_priv->rxqlock, flags);
+ atomic_inc(&rx_priv->rx_cnt);
+
+#ifndef CONFIG_RX_TASKLET
+ //if(!rx_priv->rx_thread_working && (atomic_read(&rx_priv->rx_cnt)>0)){
+ if(atomic_read(&rx_priv->rx_cnt) == 1){
+ complete(&rx_priv->usbdev->bus_if->busrx_trgg);
+ }
+#else
+ tasklet_schedule(&rx_priv->usbdev->recv_tasklet);
+#endif
+
+ aicwf_usb_rx_buf_put(usb_dev, usb_buf);
+ aicwf_usb_rx_submit_all_urb_(usb_dev);
+ //schedule_work(&usb_dev->rx_urb_work);
+ } else {
+ aicwf_dev_skb_free(skb);
+ aicwf_usb_rx_buf_put(usb_dev, usb_buf);
+ }
+}
+#endif
+
+#ifdef CONFIG_USB_MSG_IN_EP
+void aicwf_usb_msg_rx_submit_all_urb_(struct aic_usb_dev *usb_dev);
+
+static void aicwf_usb_msg_rx_complete(struct urb *urb)
+{
+ struct aicwf_usb_buf *usb_buf = (struct aicwf_usb_buf *) urb->context;
+ struct aic_usb_dev *usb_dev = usb_buf->usbdev;
+ struct aicwf_rx_priv* rx_priv = usb_dev->rx_priv;
+ struct sk_buff *skb = NULL;
+ unsigned long flags = 0;
+
+ skb = usb_buf->skb;
+ usb_buf->skb = NULL;
+
+ if (urb->actual_length > urb->transfer_buffer_length) {
+ aicwf_dev_skb_free(skb);
+ aicwf_usb_msg_rx_buf_put(usb_dev, usb_buf);
+ aicwf_usb_msg_rx_submit_all_urb_(usb_dev);
+ //schedule_work(&usb_dev->msg_rx_urb_work);
+ return;
+ }
+
+ if (urb->status != 0 || !urb->actual_length) {
+ aicwf_dev_skb_free(skb);
+ aicwf_usb_msg_rx_buf_put(usb_dev, usb_buf);
+
+ if(urb->status < 0){
+ AICWFDBG(LOGDEBUG, "%s urb->status:%d \r\n", __func__, urb->status);
+ return;
+ }else{
+ aicwf_usb_msg_rx_submit_all_urb_(usb_dev);
+ //schedule_work(&usb_dev->msg_rx_urb_work);
+ return;
+ }
+ }
+
+ if (usb_dev->state == USB_UP_ST) {
+ skb_put(skb, urb->actual_length);
+
+ spin_lock_irqsave(&rx_priv->msg_rxqlock, flags);
+ if(!aicwf_rxframe_enqueue(usb_dev->dev, &rx_priv->msg_rxq, skb)){
+ spin_unlock_irqrestore(&rx_priv->msg_rxqlock, flags);
+ usb_err("rx_priv->rxq is over flow!!!\n");
+ aicwf_dev_skb_free(skb);
+ return;
+ }
+ spin_unlock_irqrestore(&rx_priv->msg_rxqlock, flags);
+ atomic_inc(&rx_priv->msg_rx_cnt);
+ complete(&rx_priv->usbdev->bus_if->msg_busrx_trgg);
+ aicwf_usb_msg_rx_buf_put(usb_dev, usb_buf);
+ aicwf_usb_msg_rx_submit_all_urb_(usb_dev);
+ //schedule_work(&usb_dev->msg_rx_urb_work);
+ } else {
+ aicwf_dev_skb_free(skb);
+ aicwf_usb_msg_rx_buf_put(usb_dev, usb_buf);
+ }
+}
+#endif
+
+#ifdef CONFIG_PREALLOC_RX_SKB
+//extern int aic_rxbuff_size;
+static int aicwf_usb_submit_rx_urb(struct aic_usb_dev *usb_dev,
+ struct aicwf_usb_buf *usb_buf)
+{
+ int ret;
+ struct rx_buff *rx_buff;
+
+ if (!usb_buf || !usb_dev)
+ return -1;
+
+ if (usb_dev->state != USB_UP_ST) {
+ usb_err("usb state is not up!\r\n");
+ aicwf_usb_rx_buf_put(usb_dev, usb_buf);
+ return -1;
+ }
+ rx_buff = aicwf_prealloc_rxbuff_alloc(&usb_dev->rx_priv->rxbuff_lock);
+ if (rx_buff == NULL) {
+ AICWFDBG(LOGERROR, "failed to alloc rxbuff\r\n");
+ aicwf_usb_rx_buf_put(usb_dev, usb_buf);
+ return -1;
+ }
+ rx_buff->len = 0;
+ rx_buff->start = rx_buff->data;
+ rx_buff->read = rx_buff->start;
+ rx_buff->end = rx_buff->data + aicwf_rxbuff_size_get();
+
+ usb_buf->rx_buff = rx_buff;
+
+ usb_fill_bulk_urb(usb_buf->urb,
+ usb_dev->udev,
+ usb_dev->bulk_in_pipe,
+ rx_buff->data, aicwf_rxbuff_size_get(), aicwf_usb_rx_complete, usb_buf);
+
+ usb_buf->usbdev = usb_dev;
+
+ usb_anchor_urb(usb_buf->urb, &usb_dev->rx_submitted);
+ ret = usb_submit_urb(usb_buf->urb, GFP_ATOMIC);
+ if (ret) {
+ usb_err("usb submit rx urb fail:%d\n", ret);
+ usb_unanchor_urb(usb_buf->urb);
+ aicwf_prealloc_rxbuff_free(rx_buff, &usb_dev->rx_priv->rxbuff_lock);
+ aicwf_usb_rx_buf_put(usb_dev, usb_buf);
+
+ msleep(100);
+ }else{
+ atomic_inc(&rx_urb_cnt);
+ }
+ return 0;
+}
+
+#else
+static int aicwf_usb_submit_rx_urb(struct aic_usb_dev *usb_dev,
+ struct aicwf_usb_buf *usb_buf)
+{
+ struct sk_buff *skb;
+ int ret;
+
+ if (!usb_buf || !usb_dev)
+ return -1;
+
+ if (usb_dev->state != USB_UP_ST) {
+ usb_err("usb state is not up!\n");
+ aicwf_usb_rx_buf_put(usb_dev, usb_buf);
+ return -1;
+ }
+
+ if(aicwf_usb_rx_aggr){
+ skb = __dev_alloc_skb(AICWF_USB_AGGR_MAX_PKT_SIZE, GFP_ATOMIC/*GFP_KERNEL*/);
+ } else {
+ skb = __dev_alloc_skb(AICWF_USB_MAX_PKT_SIZE, GFP_ATOMIC/*GFP_KERNEL*/);
+ }
+ if (!skb) {
+ aicwf_usb_rx_buf_put(usb_dev, usb_buf);
+ return -1;
+ }
+
+ usb_buf->skb = skb;
+
+ usb_fill_bulk_urb(usb_buf->urb,
+ usb_dev->udev,
+ usb_dev->bulk_in_pipe,
+ skb->data, skb_tailroom(skb), aicwf_usb_rx_complete, usb_buf);
+
+ usb_buf->usbdev = usb_dev;
+
+ usb_anchor_urb(usb_buf->urb, &usb_dev->rx_submitted);
+ ret = usb_submit_urb(usb_buf->urb, GFP_ATOMIC);
+ if (ret) {
+ usb_err("usb submit rx urb fail:%d\n", ret);
+ usb_unanchor_urb(usb_buf->urb);
+ aicwf_dev_skb_free(usb_buf->skb);
+ usb_buf->skb = NULL;
+ aicwf_usb_rx_buf_put(usb_dev, usb_buf);
+
+ msleep(100);
+ }else{
+ atomic_inc(&rx_urb_cnt);
+ }
+ return 0;
+}
+#endif
+
+static void aicwf_usb_rx_submit_all_urb(struct aic_usb_dev *usb_dev)
+{
+ struct aicwf_usb_buf *usb_buf;
+// int i = 0;
+
+ if (usb_dev->state != USB_UP_ST) {
+ AICWFDBG(LOGERROR, "bus is not up=%d\n", usb_dev->state);
+ return;
+ }
+
+ while((usb_buf = aicwf_usb_rx_buf_get(usb_dev)) != NULL) {
+ if (aicwf_usb_submit_rx_urb(usb_dev, usb_buf)) {
+ AICWFDBG(LOGERROR, "usb rx refill fail\n");
+ if (usb_dev->state != USB_UP_ST)
+ return;
+ }
+ }
+}
+
+#ifdef CONFIG_USB_MSG_IN_EP
+static int aicwf_usb_submit_msg_rx_urb(struct aic_usb_dev *usb_dev,
+ struct aicwf_usb_buf *usb_buf)
+{
+ struct sk_buff *skb;
+ int ret;
+
+ if (!usb_buf || !usb_dev)
+ return -1;
+
+ if (usb_dev->state != USB_UP_ST) {
+ AICWFDBG(LOGERROR, "usb state is not up!\n");
+ aicwf_usb_msg_rx_buf_put(usb_dev, usb_buf);
+ return -1;
+ }
+
+ skb = __dev_alloc_skb(AICWF_USB_MAX_PKT_SIZE, GFP_ATOMIC);
+ if (!skb) {
+ aicwf_usb_msg_rx_buf_put(usb_dev, usb_buf);
+ return -1;
+ }
+
+ usb_buf->skb = skb;
+
+ usb_fill_bulk_urb(usb_buf->urb,
+ usb_dev->udev,
+ usb_dev->msg_in_pipe,
+ skb->data, skb_tailroom(skb), aicwf_usb_msg_rx_complete, usb_buf);
+
+ usb_buf->usbdev = usb_dev;
+
+ usb_anchor_urb(usb_buf->urb, &usb_dev->msg_rx_submitted);
+ ret = usb_submit_urb(usb_buf->urb, GFP_ATOMIC);
+ if (ret) {
+ AICWFDBG(LOGERROR, "usb submit msg rx urb fail:%d\n", ret);
+ usb_unanchor_urb(usb_buf->urb);
+ aicwf_dev_skb_free(usb_buf->skb);
+ usb_buf->skb = NULL;
+ aicwf_usb_msg_rx_buf_put(usb_dev, usb_buf);
+
+ msleep(100);
+ }
+ return 0;
+}
+
+
+static void aicwf_usb_msg_rx_submit_all_urb(struct aic_usb_dev *usb_dev)
+{
+ struct aicwf_usb_buf *usb_buf;
+
+ if (usb_dev->state != USB_UP_ST) {
+ AICWFDBG(LOGERROR, "bus is not up=%d\n", usb_dev->state);
+ return;
+ }
+
+ while((usb_buf = aicwf_usb_msg_rx_buf_get(usb_dev)) != NULL) {
+ if (aicwf_usb_submit_msg_rx_urb(usb_dev, usb_buf)) {
+ AICWFDBG(LOGERROR, "usb msg rx refill fail\n");
+ if (usb_dev->state != USB_UP_ST)
+ return;
+ }
+ }
+}
+#endif
+
+#ifdef CONFIG_USB_MSG_IN_EP
+void aicwf_usb_msg_rx_submit_all_urb_(struct aic_usb_dev *usb_dev){
+ aicwf_usb_msg_rx_submit_all_urb(usb_dev);
+}
+#endif
+
+void aicwf_usb_rx_submit_all_urb_(struct aic_usb_dev *usb_dev){
+ aicwf_usb_rx_submit_all_urb(usb_dev);
+}
+
+
+static void aicwf_usb_rx_prepare(struct aic_usb_dev *usb_dev)
+{
+ aicwf_usb_rx_submit_all_urb(usb_dev);
+}
+
+#ifdef CONFIG_USB_MSG_IN_EP
+static void aicwf_usb_msg_rx_prepare(struct aic_usb_dev *usb_dev)
+{
+ aicwf_usb_msg_rx_submit_all_urb(usb_dev);
+}
+#endif
+
+
+static void aicwf_usb_tx_prepare(struct aic_usb_dev *usb_dev)
+{
+ struct aicwf_usb_buf *usb_buf;
+
+ while(!list_empty(&usb_dev->tx_post_list)){
+ usb_buf = aicwf_usb_tx_dequeue(usb_dev, &usb_dev->tx_post_list,
+ &usb_dev->tx_post_count, &usb_dev->tx_post_lock);
+ #ifndef CONFIG_USB_TX_AGGR
+ if(usb_buf->skb) {
+ dev_kfree_skb(usb_buf->skb);
+ usb_buf->skb = NULL;
+ }
+ #endif
+ aicwf_usb_tx_queue(usb_dev, &usb_dev->tx_free_list, usb_buf,
+ &usb_dev->tx_free_count, &usb_dev->tx_free_lock);
+ }
+}
+
+#ifdef CONFIG_USB_TX_AGGR
+int aicwf_usb_send_pkt(struct aic_usb_dev *usb_dev, u8 *buf, uint buf_len)
+{
+ int ret = 0;
+ struct aicwf_usb_buf *usb_buf;
+ unsigned long flags;
+ bool need_cfm = false;
+
+ if (usb_dev->state != USB_UP_ST) {
+ AICWFDBG(LOGERROR, "usb state is not up!\n");
+ return -EIO;
+ }
+
+ usb_buf = aicwf_usb_tx_dequeue(usb_dev, &usb_dev->tx_free_list,
+ &usb_dev->tx_free_count, &usb_dev->tx_free_lock);
+ if (!usb_buf) {
+ AICWFDBG(LOGERROR, "free:%d, post:%d\n", usb_dev->tx_free_count, usb_dev->tx_post_count);
+ ret = -ENOMEM;
+ goto flow_ctrl;
+ }
+
+ usb_buf->skb = (struct sk_buff *)buf;
+ usb_buf->usbdev = usb_dev;
+ if (need_cfm)
+ usb_buf->cfm = true;
+ else
+ usb_buf->cfm = false;
+ AICWFDBG(LOGERROR, "%s len %d\n", __func__, buf_len);
+ print_hex_dump(KERN_ERR, "buf ", DUMP_PREFIX_NONE, 16, 1, &buf[0], 32, false);
+ usb_fill_bulk_urb(usb_buf->urb, usb_dev->udev, usb_dev->bulk_out_pipe,
+ buf, buf_len, aicwf_usb_tx_complete, usb_buf);
+ usb_buf->urb->transfer_flags |= URB_ZERO_PACKET;
+
+ aicwf_usb_tx_queue(usb_dev, &usb_dev->tx_post_list, usb_buf,
+ &usb_dev->tx_post_count, &usb_dev->tx_post_lock);
+ ret = 0;
+
+ flow_ctrl:
+ spin_lock_irqsave(&usb_dev->tx_flow_lock, flags);
+ if (usb_dev->tx_free_count < AICWF_USB_TX_LOW_WATER) {
+ usb_dev->tbusy = true;
+ aicwf_usb_tx_flowctrl(usb_dev->rwnx_hw, true);
+ }
+ spin_unlock_irqrestore(&usb_dev->tx_flow_lock, flags);
+
+ return ret;
+}
+
+int aicwf_usb_aggr(struct aicwf_tx_priv *tx_priv, struct sk_buff *pkt)
+{
+ struct rwnx_txhdr *txhdr = (struct rwnx_txhdr *)pkt->data;
+ u8 usb_header[8];
+ u8 adjust_str[4] = {0, 0, 0, 0};
+ u32 curr_len = 0;
+ int allign_len = 0;
+ u32 data_len = (pkt->len - sizeof(struct rwnx_txhdr) + sizeof(struct txdesc_api)) + 4;
+
+ usb_header[0] =(data_len & 0xff);
+ usb_header[1] =((data_len >> 8)&0x0f);
+ usb_header[2] =(data_len & 0xff);
+ usb_header[3] =((data_len >> 8)&0x0f);
+
+ usb_header[4] =(data_len & 0xff);
+ usb_header[5] =((data_len >> 8)&0x0f);
+ usb_header[6] = 0x01; //data
+ usb_header[7] = 0; //reserved
+
+ memcpy(tx_priv->tail, (u8 *)&usb_header, sizeof(usb_header));
+ tx_priv->tail += sizeof(usb_header);
+ //payload
+ memcpy(tx_priv->tail, (u8 *)(long)&txhdr->sw_hdr->desc, sizeof(struct txdesc_api));
+ tx_priv->tail += sizeof(struct txdesc_api); //hostdesc
+ memcpy(tx_priv->tail, (u8 *)((u8 *)txhdr + txhdr->sw_hdr->headroom), pkt->len-txhdr->sw_hdr->headroom);
+ tx_priv->tail += (pkt->len - txhdr->sw_hdr->headroom);
+
+ //word alignment
+ curr_len = tx_priv->tail - tx_priv->head;
+ if (curr_len & (TX_ALIGNMENT - 1)) {
+ allign_len = roundup(curr_len, TX_ALIGNMENT)-curr_len;
+ memcpy(tx_priv->tail, adjust_str, allign_len);
+ tx_priv->tail += allign_len;
+ }
+
+ tx_priv->aggr_buf->dev = pkt->dev;
+
+ if(!txhdr->sw_hdr->need_cfm) {
+ kmem_cache_free(txhdr->sw_hdr->rwnx_vif->rwnx_hw->sw_txhdr_cache, txhdr->sw_hdr);
+ skb_pull(pkt, txhdr->sw_hdr->headroom);
+ consume_skb(pkt);
+ }
+
+ atomic_inc(&tx_priv->aggr_count);
+ return 0;
+}
+
+int aicwf_usb_send(struct aicwf_tx_priv *tx_priv)
+{
+ struct sk_buff *pkt;
+ struct sk_buff *tx_buf;
+ struct aic_usb_dev *usbdev = tx_priv->usbdev;
+ struct aicwf_usb_buf *usb_buf;
+ u8* buf;
+ int ret = 0;
+ int curr_len = 0;
+ unsigned long flags;
+
+ if (aicwf_is_framequeue_empty(&tx_priv->txq)) {
+ ret = -1;
+ AICWFDBG(LOGERROR, "no buf to send\n");
+ return ret;
+ }
+
+ if (usbdev->state != USB_UP_ST) {
+ AICWFDBG(LOGERROR, "usb state is not up!\n");
+ ret = -ENODEV;
+ return ret;
+ }
+ usb_buf = aicwf_usb_tx_dequeue(usbdev, &usbdev->tx_free_list,
+ &usbdev->tx_free_count, &usbdev->tx_free_lock);
+ if (!usb_buf) {
+ AICWFDBG(LOGERROR, "free:%d, post:%d\n", usbdev->tx_free_count, usbdev->tx_post_count);
+ ret = -ENOMEM;
+ return ret;
+ }
+
+ usb_buf->aggr_cnt = 0;
+ spin_lock_bh(&usbdev->tx_priv->txdlock);
+ tx_priv->head = usb_buf->skb->data;
+ tx_priv->tail = usb_buf->skb->data;
+
+ while (!aicwf_is_framequeue_empty(&usbdev->tx_priv->txq)) {
+ if (usbdev->state != USB_UP_ST) {
+ AICWFDBG(LOGERROR, "usb state is not up, break!\n");
+ ret = -ENODEV;
+ break;
+ }
+
+ if (usb_buf->aggr_cnt == 10) {
+ break;
+ }
+ spin_lock_bh(&usbdev->tx_priv->txqlock);
+ pkt = aicwf_frame_dequeue(&usbdev->tx_priv->txq);
+ if (pkt == NULL) {
+ AICWFDBG(LOGERROR, "txq no pkt\n");
+ spin_unlock_bh(&usbdev->tx_priv->txqlock);
+ ret = -1;
+ return ret;
+ }
+ atomic_dec(&usbdev->tx_priv->tx_pktcnt);
+ spin_unlock_bh(&usbdev->tx_priv->txqlock);
+ if(tx_priv==NULL || tx_priv->tail==NULL || pkt==NULL) {
+ AICWFDBG(LOGERROR, "null error\n");
+ }
+ aicwf_usb_aggr(tx_priv, pkt);
+ usb_buf->aggr_cnt++;
+ }
+
+ tx_buf = usb_buf->skb;
+ buf = tx_buf->data;
+
+ curr_len = tx_priv->tail - tx_priv->head;
+
+ AICWFDBG(LOGTRACE, "%s len %d, cnt %d\n", __func__,curr_len, usb_buf->aggr_cnt);
+ tx_buf->len = tx_priv->tail - tx_priv->head;
+ spin_unlock_bh(&usbdev->tx_priv->txdlock);
+ usb_fill_bulk_urb(usb_buf->urb, usbdev->udev, usbdev->bulk_out_pipe,
+ buf, curr_len, aicwf_usb_tx_complete, usb_buf);
+ usb_buf->urb->transfer_flags |= URB_ZERO_PACKET;
+
+ aicwf_usb_tx_queue(usbdev, &usbdev->tx_post_list, usb_buf,
+ &usbdev->tx_post_count, &usbdev->tx_post_lock);
+
+ flow_ctrl:
+ spin_lock_irqsave(&usbdev->tx_flow_lock, flags);
+ if (usbdev->tx_free_count < AICWF_USB_TX_LOW_WATER) {
+ usbdev->tbusy = true;
+ aicwf_usb_tx_flowctrl(usbdev->rwnx_hw, true);
+ }
+ spin_unlock_irqrestore(&usbdev->tx_flow_lock, flags);
+
+ return ret;
+}
+
+#endif
+
+static void aicwf_usb_tx_process(struct aic_usb_dev *usb_dev)
+{
+ struct aicwf_usb_buf *usb_buf;
+ int ret = 0;
+ u8* data = NULL;
+
+#ifdef CONFIG_USB_TX_AGGR
+ if (!aicwf_is_framequeue_empty(&usb_dev->tx_priv->txq)) {
+ if (aicwf_usb_send(usb_dev->tx_priv)) {
+ AICWFDBG(LOGERROR, "%s no buf send\n", __func__);
+ }
+ }
+#endif
+
+ while(!list_empty(&usb_dev->tx_post_list)) {
+
+ if (usb_dev->state != USB_UP_ST) {
+ usb_err("usb state is not up!\n");
+ return;
+ }
+
+ usb_buf = aicwf_usb_tx_dequeue(usb_dev, &usb_dev->tx_post_list,
+ &usb_dev->tx_post_count, &usb_dev->tx_post_lock);
+ if(!usb_buf) {
+ usb_err("can not get usb_buf from tx_post_list!\n");
+ return;
+ }
+ data = usb_buf->skb->data;
+
+ ret = usb_submit_urb(usb_buf->urb, GFP_KERNEL);
+ if (ret) {
+ AICWFDBG(LOGERROR, "aicwf_usb_bus_tx usb_submit_urb FAILED err:%d\n", ret);
+ #ifdef CONFIG_USB_TX_AGGR
+ aicwf_usb_tx_queue(usb_dev, &usb_dev->tx_post_list, usb_buf,
+ &usb_dev->tx_post_count, &usb_dev->tx_post_lock);
+ break;
+ #else
+ goto fail;
+ #endif
+ }
+
+ continue;
+#ifndef CONFIG_USB_TX_AGGR
+fail:
+ usb_txc_sta_flowctrl(usb_buf, usb_dev);
+ dev_kfree_skb(usb_buf->skb);
+ usb_buf->skb = NULL;
+ aicwf_usb_tx_queue(usb_dev, &usb_dev->tx_free_list, usb_buf,
+ &usb_dev->tx_free_count, &usb_dev->tx_free_lock);
+#endif
+ }
+}
+
+#ifdef CONFIG_TX_TASKLET
+void aicwf_tasklet_tx_process(struct aic_usb_dev *usb_dev){
+ aicwf_usb_tx_process(usb_dev);
+}
+#endif
+
+static inline void aic_thread_wait_stop(void)
+{
+#if 1// PLATFORM_LINUX
+ #if 0
+ while (!kthread_should_stop())
+ rtw_msleep_os(10);
+ #else
+ set_current_state(TASK_INTERRUPTIBLE);
+ while (!kthread_should_stop()) {
+ schedule();
+ set_current_state(TASK_INTERRUPTIBLE);
+ }
+ __set_current_state(TASK_RUNNING);
+ #endif
+#endif
+}
+
+
+int usb_bustx_thread(void *data)
+{
+ struct aicwf_bus *bus = (struct aicwf_bus *)data;
+ struct aic_usb_dev *usbdev = bus->bus_priv.usb;
+
+#ifdef CONFIG_TXRX_THREAD_PRIO
+ if (bustx_thread_prio > 0) {
+ struct sched_param param;
+ param.sched_priority = (bustx_thread_prio < MAX_RT_PRIO)?bustx_thread_prio:(MAX_RT_PRIO-1);
+ sched_setscheduler(current, SCHED_FIFO, &param);
+ }
+#endif
+ AICWFDBG(LOGINFO, "%s the policy of current thread is:%d\n", __func__, current->policy);
+ AICWFDBG(LOGINFO, "%s the rt_priority of current thread is:%d\n", __func__, current->rt_priority);
+ AICWFDBG(LOGINFO, "%s the current pid is:%d\n", __func__, current->pid);
+
+
+ while (1) {
+ #if 0
+ if(kthread_should_stop()) {
+ usb_err("usb bustx thread stop 2\n");
+ break;
+ }
+ #endif
+ if (!wait_for_completion_interruptible(&bus->bustx_trgg)) {
+ if(usbdev->bus_if->state == BUS_DOWN_ST){
+ AICWFDBG(LOGINFO, "usb bustx thread will to stop\n");
+ break;
+ }
+ #ifdef CONFIG_USB_TX_AGGR
+ if ((usbdev->tx_post_count > 0) || !aicwf_is_framequeue_empty(&usbdev->tx_priv->txq))
+ #else
+ if (usbdev->tx_post_count > 0)
+ #endif
+ aicwf_usb_tx_process(usbdev);
+ }
+ }
+
+ aic_thread_wait_stop();
+ AICWFDBG(LOGINFO, "usb bustx thread stop\n");
+
+ return 0;
+}
+
+int usb_busrx_thread(void *data)
+{
+ struct aicwf_rx_priv *rx_priv = (struct aicwf_rx_priv *)data;
+ struct aicwf_bus *bus_if = rx_priv->usbdev->bus_if;
+
+#ifdef CONFIG_TXRX_THREAD_PRIO
+ if (busrx_thread_prio > 0) {
+ struct sched_param param;
+ param.sched_priority = (busrx_thread_prio < MAX_RT_PRIO)?busrx_thread_prio:(MAX_RT_PRIO-1);
+ sched_setscheduler(current, SCHED_FIFO, &param);
+ }
+#endif
+ AICWFDBG(LOGINFO, "%s the policy of current thread is:%d\n", __func__, current->policy);
+ AICWFDBG(LOGINFO, "%s the rt_priority of current thread is:%d\n", __func__, current->rt_priority);
+ AICWFDBG(LOGINFO, "%s the current pid is:%d\n", __func__, current->pid);
+
+ while (1) {
+#if 0
+ if(kthread_should_stop()) {
+ usb_err("usb busrx thread stop 2\n");
+ break;
+ }
+#endif
+ //rx_priv->rx_thread_working = 0;//AIDEN
+ if (!wait_for_completion_interruptible(&bus_if->busrx_trgg)) {
+ if(bus_if->state == BUS_DOWN_ST){
+ AICWFDBG(LOGINFO, "usb busrx thread will to stop\n");
+ break;
+ }
+ //rx_priv->rx_thread_working = 1;//AIDEN
+ aicwf_process_rxframes(rx_priv);
+ }
+ }
+
+ aic_thread_wait_stop();
+ AICWFDBG(LOGINFO, "usb busrx thread stop\n");
+
+ return 0;
+}
+
+#ifdef CONFIG_USB_MSG_IN_EP
+int usb_msg_busrx_thread(void *data)
+{
+ struct aicwf_rx_priv *rx_priv = (struct aicwf_rx_priv *)data;
+ struct aicwf_bus *bus_if = rx_priv->usbdev->bus_if;
+
+#ifdef CONFIG_TXRX_THREAD_PRIO
+ if (busrx_thread_prio > 0) {
+ struct sched_param param;
+ param.sched_priority = (busrx_thread_prio < MAX_RT_PRIO)?busrx_thread_prio:(MAX_RT_PRIO-1);
+ sched_setscheduler(current, SCHED_FIFO, &param);
+ }
+#endif
+ AICWFDBG(LOGINFO, "%s the policy of current thread is:%d\n", __func__, current->policy);
+ AICWFDBG(LOGINFO, "%s the rt_priority of current thread is:%d\n", __func__, current->rt_priority);
+ AICWFDBG(LOGINFO, "%s the current pid is:%d\n", __func__, current->pid);
+
+
+
+ while (1) {
+ if(kthread_should_stop()) {
+ usb_err("usb msg busrx thread stop\n");
+ break;
+ }
+ if (!wait_for_completion_interruptible(&bus_if->msg_busrx_trgg)) {
+ if(bus_if->state == BUS_DOWN_ST)
+ break;
+ aicwf_process_msg_rxframes(rx_priv);
+ }
+ }
+
+ return 0;
+}
+#endif
+
+
+static void aicwf_usb_send_msg_complete(struct urb *urb)
+{
+ struct aic_usb_dev *usb_dev = (struct aic_usb_dev *) urb->context;
+
+ usb_dev->msg_finished = true;
+ if (waitqueue_active(&usb_dev->msg_wait))
+ wake_up(&usb_dev->msg_wait);
+}
+
+static int aicwf_usb_bus_txmsg(struct device *dev, u8 *buf, u32 len)
+{
+ int ret = 0;
+ struct aicwf_bus *bus_if = dev_get_drvdata(dev);
+ struct aic_usb_dev *usb_dev = bus_if->bus_priv.usb;
+
+ if (usb_dev->state != USB_UP_ST)
+ return -EIO;
+
+ if (buf == NULL || len == 0 || usb_dev->msg_out_urb == NULL)
+ return -EINVAL;
+
+#if 0
+ if (test_and_set_bit(0, &usb_dev->msg_busy)) {
+ usb_err("In a control frame option, can't tx!\n");
+ return -EIO;
+ }
+#endif
+
+ usb_dev->msg_finished = false;
+
+#ifdef CONFIG_USB_MSG_OUT_EP
+ if (usb_dev->msg_out_pipe) {
+ usb_fill_bulk_urb(usb_dev->msg_out_urb,
+ usb_dev->udev,
+ usb_dev->msg_out_pipe,
+ buf, len, (usb_complete_t) aicwf_usb_send_msg_complete, usb_dev);
+ } else {
+ usb_fill_bulk_urb(usb_dev->msg_out_urb,
+ usb_dev->udev,
+ usb_dev->bulk_out_pipe,
+ buf, len, (usb_complete_t) aicwf_usb_send_msg_complete, usb_dev);
+ }
+#else
+ usb_fill_bulk_urb(usb_dev->msg_out_urb,
+ usb_dev->udev,
+ usb_dev->bulk_out_pipe,
+ buf, len, (usb_complete_t) aicwf_usb_send_msg_complete, usb_dev);
+#endif
+ #if defined CONFIG_USB_NO_TRANS_DMA_MAP
+ usb_dev->msg_out_urb->transfer_dma = usb_dev->cmd_dma_trans_addr;
+ usb_dev->msg_out_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+ #endif
+#ifdef CONFIG_USE_USB_ZERO_PACKET
+ usb_dev->msg_out_urb->transfer_flags |= URB_ZERO_PACKET;
+#endif
+ ret = usb_submit_urb(usb_dev->msg_out_urb, GFP_ATOMIC);
+ if (ret) {
+ usb_err("usb_submit_urb failed %d\n", ret);
+ goto exit;
+ }
+
+ ret = wait_event_timeout(usb_dev->msg_wait,
+ usb_dev->msg_finished, msecs_to_jiffies(CMD_TX_TIMEOUT));
+ if (!ret) {
+ if (usb_dev->msg_out_urb)
+ usb_kill_urb(usb_dev->msg_out_urb);
+ usb_err("Txmsg wait timed out\n");
+ ret = -EIO;
+ goto exit;
+ }
+
+ if (usb_dev->msg_finished == false) {
+ usb_err("Txmsg timed out\n");
+ ret = -ETIMEDOUT;
+ goto exit;
+ }
+exit:
+#if 0
+ clear_bit(0, &usb_dev->msg_busy);
+#endif
+ return ret;
+}
+
+
+static void aicwf_usb_free_urb(struct list_head *q, spinlock_t *qlock)
+{
+ struct aicwf_usb_buf *usb_buf, *tmp;
+ unsigned long flags;
+
+ spin_lock_irqsave(qlock, flags);
+ list_for_each_entry_safe(usb_buf, tmp, q, list) {
+ spin_unlock_irqrestore(qlock, flags);
+ if (!usb_buf->urb) {
+ usb_err("bad usb_buf\n");
+ spin_lock_irqsave(qlock, flags);
+ break;
+ }
+ #ifdef CONFIG_USB_TX_AGGR
+ if (usb_buf->skb) {
+ dev_kfree_skb(usb_buf->skb);
+ }
+ #endif
+ usb_free_urb(usb_buf->urb);
+ #if defined CONFIG_USB_NO_TRANS_DMA_MAP
+ // free dma buf if needed
+ if (usb_buf->data_buf) {
+ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35))
+ usb_free_coherent(usb_buf->usbdev->udev, DATA_BUF_MAX, usb_buf->data_buf, usb_buf->data_dma_trans_addr);
+ #else
+ usb_buffer_free(usb_buf->usbdev->udev, DATA_BUF_MAX, usb_buf->data_buf, usb_buf->data_dma_trans_addr);
+ #endif
+ usb_buf->data_buf = NULL;
+ usb_buf->data_dma_trans_addr = 0x0;
+ }
+ #endif
+ list_del_init(&usb_buf->list);
+ spin_lock_irqsave(qlock, flags);
+ }
+ spin_unlock_irqrestore(qlock, flags);
+}
+
+static int aicwf_usb_alloc_rx_urb(struct aic_usb_dev *usb_dev)
+{
+ int i;
+
+ AICWFDBG(LOGINFO, "%s AICWF_USB_RX_URBS:%d \r\n", __func__, AICWF_USB_RX_URBS);
+ for (i = 0; i < AICWF_USB_RX_URBS; i++) {
+ struct aicwf_usb_buf *usb_buf = &usb_dev->usb_rx_buf[i];
+
+ usb_buf->usbdev = usb_dev;
+ usb_buf->urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!usb_buf->urb) {
+ usb_err("could not allocate rx data urb\n");
+ goto err;
+ }
+ #if defined CONFIG_USB_NO_TRANS_DMA_MAP
+ // dma buf unused
+ usb_buf->data_buf = NULL;
+ usb_buf->data_dma_trans_addr = 0x0;
+ #endif
+ list_add_tail(&usb_buf->list, &usb_dev->rx_free_list);
+ }
+ return 0;
+
+err:
+ aicwf_usb_free_urb(&usb_dev->rx_free_list, &usb_dev->rx_free_lock);
+ return -ENOMEM;
+}
+
+static int aicwf_usb_alloc_tx_urb(struct aic_usb_dev *usb_dev)
+{
+ int i;
+
+ AICWFDBG(LOGINFO, "%s AICWF_USB_TX_URBS:%d \r\n", __func__, AICWF_USB_TX_URBS);
+ for (i = 0; i < AICWF_USB_TX_URBS; i++) {
+ struct aicwf_usb_buf *usb_buf = &usb_dev->usb_tx_buf[i];
+
+ usb_buf->usbdev = usb_dev;
+ usb_buf->urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!usb_buf->urb) {
+ usb_err("could not allocate tx data urb\n");
+ goto err;
+ }
+ #ifdef CONFIG_USB_TX_AGGR
+ usb_buf->skb = dev_alloc_skb(MAX_USB_AGGR_TXPKT_LEN);
+ #endif
+ #if defined CONFIG_USB_NO_TRANS_DMA_MAP
+ // alloc dma buf
+ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35))
+ usb_buf->data_buf = usb_alloc_coherent(usb_dev->udev, DATA_BUF_MAX, (in_interrupt() ? GFP_ATOMIC : GFP_KERNEL), &usb_buf->data_dma_trans_addr);
+ #else
+ usb_buf->data_buf = usb_buffer_alloc(usb_dev->udev, DATA_BUF_MAX, (in_interrupt() ? GFP_ATOMIC : GFP_KERNEL), &usb_buf->data_dma_trans_addr);
+ #endif
+ if (usb_buf->data_buf == NULL) {
+ usb_err("could not allocate tx data dma buf\n");
+ goto err;
+ }
+ #endif
+ list_add_tail(&usb_buf->list, &usb_dev->tx_free_list);
+ (usb_dev->tx_free_count)++;
+ }
+ return 0;
+
+err:
+ aicwf_usb_free_urb(&usb_dev->tx_free_list, &usb_dev->tx_free_lock);
+ return -ENOMEM;
+}
+
+#ifdef CONFIG_USB_MSG_IN_EP
+static int aicwf_usb_alloc_msg_rx_urb(struct aic_usb_dev *usb_dev)
+{
+ int i;
+
+ AICWFDBG(LOGINFO, "%s AICWF_USB_MSG_RX_URBS:%d \r\n", __func__, AICWF_USB_MSG_RX_URBS);
+
+ for (i = 0; i < AICWF_USB_MSG_RX_URBS; i++) {
+ struct aicwf_usb_buf *usb_buf = &usb_dev->usb_msg_rx_buf[i];
+
+ usb_buf->usbdev = usb_dev;
+ usb_buf->urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!usb_buf->urb) {
+ usb_err("could not allocate rx data urb\n");
+ goto err;
+ }
+ list_add_tail(&usb_buf->list, &usb_dev->msg_rx_free_list);
+ }
+ return 0;
+
+err:
+ aicwf_usb_free_urb(&usb_dev->msg_rx_free_list, &usb_dev->msg_rx_free_lock);
+ return -ENOMEM;
+}
+#endif
+
+static void aicwf_usb_state_change(struct aic_usb_dev *usb_dev, int state)
+{
+ int old_state;
+
+ if (usb_dev->state == state)
+ return;
+
+ old_state = usb_dev->state;
+ usb_dev->state = state;
+
+ if (state == USB_DOWN_ST) {
+ usb_dev->bus_if->state = BUS_DOWN_ST;
+ }
+ if (state == USB_UP_ST) {
+ usb_dev->bus_if->state = BUS_UP_ST;
+ }
+}
+
+int align_param = 8;
+module_param(align_param, int, 0660);
+
+static void usb_tx_flow_ctrl(struct rwnx_txhdr *txhdr, struct aic_usb_dev *usb_dev, struct rwnx_hw *rwnx_hw)
+{
+#ifdef CONFIG_PER_STA_FC
+ struct rwnx_sta *sta;
+ u8 sta_idx;
+ unsigned long flags;
+
+ //printk("txdata: sta %d\n", txhdr->sw_hdr->desc.host.staid);
+ sta_idx = txhdr->sw_hdr->desc.host.staid;
+ if(sta_idx < NX_REMOTE_STA_MAX && !(txhdr->sw_hdr->desc.host.flags & TXU_CNTRL_MGMT)) {
+ struct rwnx_vif *vif = NULL;
+ sta = &rwnx_hw->sta_table[sta_idx];
+ vif = rwnx_hw->vif_table[sta->vif_idx];
+ spin_lock_irqsave(&usb_dev->tx_flow_lock, flags);
+ atomic_inc(&rwnx_hw->sta_flowctrl[sta_idx].tx_pending_cnt);
+ //printk("sta %d pending %d >= 64, flowctrl=%d\n", sta->sta_idx, sta->tx_pending_cnt, sta->flowctrl);
+ if(RWNX_VIF_TYPE(vif) == NL80211_IFTYPE_AP) {
+ if((atomic_read(&rwnx_hw->sta_flowctrl[sta_idx].tx_pending_cnt) >= AICWF_USB_FC_PERSTA_HIGH_WATER && rwnx_hw->sta_flowctrl[sta_idx].flowctrl == 0) ||
+ rwnx_hw->sta_flowctrl[sta_idx].flowctrl) {
+ //AICWFDBG(LOGDEBUG, "sta 0x%x:0x%x, %d pending %d, stop\n", sta->mac_addr[4], sta->mac_addr[5], sta->sta_idx, atomic_read(&rwnx_hw->sta_flowctrl[sta_idx].tx_pending_cnt));
+ if(!usb_dev->tbusy)
+ rwnx_stop_sta_all_queues(sta, usb_dev->rwnx_hw);
+ rwnx_hw->sta_flowctrl[sta_idx].flowctrl = 1;
+ }
+ }
+ spin_unlock_irqrestore(&usb_dev->tx_flow_lock, flags);
+ }
+#endif
+}
+
+#ifdef CONFIG_USB_TX_AGGR
+static int aicwf_usb_bus_txdata(struct device *dev, struct sk_buff *pkt)
+{
+ uint prio;
+ int ret = -EBADE;
+ struct aicwf_bus *bus_if = dev_get_drvdata(dev);
+ struct aic_usb_dev *usbdev = bus_if->bus_priv.usb;
+
+ //printk("%s\n", __func__);
+ prio = (pkt->priority & 0x7);
+ spin_lock_bh(&usbdev->tx_priv->txqlock);
+ if (!aicwf_frame_enq(usbdev->dev, &usbdev->tx_priv->txq, pkt, prio)) {
+ aicwf_dev_skb_free(pkt);
+ spin_unlock_bh(&usbdev->tx_priv->txqlock);
+ return -ENOSR;
+ } else {
+ ret = 0;
+ }
+
+ if (bus_if->state != BUS_UP_ST) {
+ usb_err("bus_if stopped\n");
+ spin_unlock_bh(&usbdev->tx_priv->txqlock);
+ return -1;
+ }
+
+ atomic_inc(&usbdev->tx_priv->tx_pktcnt);
+ spin_unlock_bh(&usbdev->tx_priv->txqlock);
+ complete(&bus_if->bustx_trgg);
+
+ return ret;
+}
+
+#else
+static int aicwf_usb_bus_txdata(struct device *dev, struct sk_buff *skb)
+{
+ u8 *buf;
+ u16 buf_len = 0;
+ u16 adjust_len = 0;
+ struct aicwf_usb_buf *usb_buf;
+ int ret = 0;
+ unsigned long flags;
+ struct aicwf_bus *bus_if = dev_get_drvdata(dev);
+ struct aic_usb_dev *usb_dev = bus_if->bus_priv.usb;
+ struct rwnx_txhdr *txhdr = (struct rwnx_txhdr *)skb->data;
+ struct rwnx_hw *rwnx_hw = usb_dev->rwnx_hw;
+ u8 usb_header[4];
+ u8 adj_buf[4] = {0};
+ u16 index = 0;
+ bool need_cfm = false;
+#ifdef CONFIG_USB_ALIGN_DATA//AIDEN
+ int align;
+#endif
+
+ if (usb_dev->state != USB_UP_ST) {
+ usb_err("usb state is not up!\n");
+ kmem_cache_free(rwnx_hw->sw_txhdr_cache, txhdr->sw_hdr);
+ dev_kfree_skb_any(skb);
+ return -EIO;
+ }
+
+ usb_buf = aicwf_usb_tx_dequeue(usb_dev, &usb_dev->tx_free_list,
+ &usb_dev->tx_free_count, &usb_dev->tx_free_lock);
+ if (!usb_buf) {
+ usb_err("free:%d, post:%d\n", usb_dev->tx_free_count, usb_dev->tx_post_count);
+ kmem_cache_free(rwnx_hw->sw_txhdr_cache, txhdr->sw_hdr);
+ dev_kfree_skb_any(skb);
+ ret = -ENOMEM;
+ goto flow_ctrl;
+ }
+
+ usb_tx_flow_ctrl(txhdr, usb_dev, rwnx_hw);
+
+ if (txhdr->sw_hdr->need_cfm) {
+ need_cfm = true;
+ #if defined CONFIG_USB_NO_TRANS_DMA_MAP
+ buf = usb_buf->data_buf;
+ #else
+ buf = kmalloc(skb->len + 1, GFP_ATOMIC/*GFP_KERNEL*/);
+ #endif
+ index += sizeof(usb_header);
+ memcpy(&buf[index], (u8 *)(long)&txhdr->sw_hdr->desc, sizeof(struct txdesc_api));
+ index += sizeof(struct txdesc_api);
+ memcpy(&buf[index], &skb->data[txhdr->sw_hdr->headroom], skb->len - txhdr->sw_hdr->headroom);
+ index += skb->len - txhdr->sw_hdr->headroom;
+ buf_len = index;
+ if (buf_len & (TX_ALIGNMENT - 1)) {
+ adjust_len = roundup(buf_len, TX_ALIGNMENT)-buf_len;
+ memcpy(&buf[buf_len], adj_buf, adjust_len);
+ buf_len += adjust_len;
+ }
+ usb_header[0] =((buf_len) & 0xff);
+ usb_header[1] =(((buf_len) >> 8)&0x0f);
+ usb_header[2] = 0x01; //data
+ usb_header[3] = 0; //reserved
+ memcpy(&buf[0], usb_header, sizeof(usb_header));
+ usb_buf->skb = (struct sk_buff *)buf;
+ } else {
+ skb_pull(skb, txhdr->sw_hdr->headroom);
+ skb_push(skb, sizeof(struct txdesc_api));
+ memcpy(&skb->data[0], (u8 *)(long)&txhdr->sw_hdr->desc, sizeof(struct txdesc_api));
+ kmem_cache_free(rwnx_hw->sw_txhdr_cache, txhdr->sw_hdr);
+
+ skb_push(skb, sizeof(usb_header));
+ usb_header[0] =((skb->len) & 0xff);
+ usb_header[1] =(((skb->len) >> 8)&0x0f);
+ usb_header[2] = 0x01; //data
+ usb_header[3] = 0; //reserved
+ memcpy(&skb->data[0], usb_header, sizeof(usb_header));
+
+ #if defined CONFIG_USB_NO_TRANS_DMA_MAP
+ buf = usb_buf->data_buf;
+ memcpy(&buf[0], skb->data, skb->len);
+ #else
+ buf = skb->data;
+ #endif
+ buf_len = skb->len;
+
+ usb_buf->skb = skb;
+ }
+ usb_buf->usbdev = usb_dev;
+ if (need_cfm)
+ usb_buf->cfm = true;
+ else
+ usb_buf->cfm = false;
+
+
+#ifndef CONFIG_USE_USB_ZERO_PACKET
+ if((buf_len % 512) == 0){
+ printk("%s send zero package buf_len: %d\r\n", __func__, buf_len);
+ if(txhdr->sw_hdr->need_cfm){
+ buf[buf_len] = 0x00;
+ buf_len = buf_len + 1;
+ }else{
+ skb_put(skb, 1);
+ skb->data[buf_len] = 0x00;
+ buf = skb->data;
+ buf_len = skb->len;
+ }
+ }
+#endif
+
+#ifdef CONFIG_USB_ALIGN_DATA
+ #if defined CONFIG_USB_NO_TRANS_DMA_MAP
+ #error "CONFIG_USB_NO_TRANS_DMA_MAP not supported"
+ #endif
+ usb_buf->usb_align_data = (u8*)kmalloc(sizeof(u8) * buf_len + align_param, GFP_ATOMIC);
+
+ align = ((unsigned long)(usb_buf->usb_align_data)) & (align_param - 1);
+ memcpy(usb_buf->usb_align_data + (align_param - align), buf, buf_len);
+
+ usb_fill_bulk_urb(usb_buf->urb, usb_dev->udev, usb_dev->bulk_out_pipe,
+ usb_buf->usb_align_data + (align_param - align), buf_len, aicwf_usb_tx_complete, usb_buf);
+#else
+ usb_fill_bulk_urb(usb_buf->urb, usb_dev->udev, usb_dev->bulk_out_pipe,
+ buf, buf_len, aicwf_usb_tx_complete, usb_buf);
+#endif
+
+ #if defined CONFIG_USB_NO_TRANS_DMA_MAP
+ usb_buf->urb->transfer_dma = usb_buf->data_dma_trans_addr;
+ usb_buf->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+ #endif
+#ifdef CONFIG_USE_USB_ZERO_PACKET
+ usb_buf->urb->transfer_flags |= URB_ZERO_PACKET;
+#endif
+
+ aicwf_usb_tx_queue(usb_dev, &usb_dev->tx_post_list, usb_buf,
+ &usb_dev->tx_post_count, &usb_dev->tx_post_lock);
+
+#ifdef CONFIG_TX_TASKLET
+ tasklet_schedule(&usb_dev->xmit_tasklet);
+#else
+ complete(&bus_if->bustx_trgg);
+#endif
+
+ ret = 0;
+
+ flow_ctrl:
+ spin_lock_irqsave(&usb_dev->tx_flow_lock, flags);
+ if (usb_dev->tx_free_count < AICWF_USB_TX_LOW_WATER) {
+ AICWFDBG(LOGDEBUG, "usb_dev->tx_free_count < AICWF_USB_TX_LOW_WATER:%d\r\n",
+ usb_dev->tx_free_count);
+ usb_dev->tbusy = true;
+ aicwf_usb_tx_flowctrl(usb_dev->rwnx_hw, true);
+ }
+ spin_unlock_irqrestore(&usb_dev->tx_flow_lock, flags);
+
+ return ret;
+}
+#endif
+static int aicwf_usb_bus_start(struct device *dev)
+{
+ struct aicwf_bus *bus_if = dev_get_drvdata(dev);
+ struct aic_usb_dev *usb_dev = bus_if->bus_priv.usb;
+
+ if (usb_dev->state == USB_UP_ST)
+ return 0;
+
+ aicwf_usb_state_change(usb_dev, USB_UP_ST);
+ aicwf_usb_rx_prepare(usb_dev);
+ aicwf_usb_tx_prepare(usb_dev);
+#ifdef CONFIG_USB_MSG_IN_EP
+ if(usb_dev->chipid != PRODUCT_ID_AIC8801 &&
+ usb_dev->chipid != PRODUCT_ID_AIC8800D81){
+ aicwf_usb_msg_rx_prepare(usb_dev);
+ }
+#endif
+
+ return 0;
+}
+
+static void aicwf_usb_cancel_all_urbs_(struct aic_usb_dev *usb_dev)
+{
+ struct aicwf_usb_buf *usb_buf, *tmp;
+ unsigned long flags;
+
+ if (usb_dev->msg_out_urb)
+ usb_kill_urb(usb_dev->msg_out_urb);
+
+ spin_lock_irqsave(&usb_dev->tx_post_lock, flags);
+ list_for_each_entry_safe(usb_buf, tmp, &usb_dev->tx_post_list, list) {
+ spin_unlock_irqrestore(&usb_dev->tx_post_lock, flags);
+ if (!usb_buf->urb) {
+ usb_err("bad usb_buf\n");
+ spin_lock_irqsave(&usb_dev->tx_post_lock, flags);
+ break;
+ }
+ usb_kill_urb(usb_buf->urb);
+ #if defined CONFIG_USB_NO_TRANS_DMA_MAP
+ // free dma buf if needed
+ if (usb_buf->data_buf) {
+ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35))
+ usb_free_coherent(usb_buf->usbdev->udev, DATA_BUF_MAX, usb_buf->data_buf, usb_buf->data_dma_trans_addr);
+ #else
+ usb_buffer_free(usb_buf->usbdev->udev, DATA_BUF_MAX, usb_buf->data_buf, usb_buf->data_dma_trans_addr);
+ #endif
+ usb_buf->data_buf = NULL;
+ usb_buf->data_dma_trans_addr = 0x0;
+ } else {
+ usb_err("bad usb dma buf\n");
+ spin_lock_irqsave(&usb_dev->tx_post_lock, flags);
+ break;
+ }
+ #endif
+ spin_lock_irqsave(&usb_dev->tx_post_lock, flags);
+ }
+ spin_unlock_irqrestore(&usb_dev->tx_post_lock, flags);
+
+ usb_kill_anchored_urbs(&usb_dev->rx_submitted);
+#ifdef CONFIG_USB_MSG_IN_EP
+ if(usb_dev->chipid != PRODUCT_ID_AIC8801 &&
+ usb_dev->chipid != PRODUCT_ID_AIC8800D81){
+ usb_kill_anchored_urbs(&usb_dev->msg_rx_submitted);
+ }
+#endif
+}
+
+void aicwf_usb_cancel_all_urbs(struct aic_usb_dev *usb_dev){
+ aicwf_usb_cancel_all_urbs_(usb_dev);
+}
+
+
+static void aicwf_usb_bus_stop(struct device *dev)
+{
+ struct aicwf_bus *bus_if = dev_get_drvdata(dev);
+ struct aic_usb_dev *usb_dev = bus_if->bus_priv.usb;
+
+ AICWFDBG(LOGINFO, "%s\r\n", __func__);
+ if (usb_dev == NULL)
+ return;
+
+ if (usb_dev->state == USB_DOWN_ST)
+ return;
+
+ if(g_rwnx_plat->wait_disconnect_cb == true){
+ atomic_set(&aicwf_deinit_atomic, 1);
+ up(&aicwf_deinit_sem);
+ }
+ aicwf_usb_state_change(usb_dev, USB_DOWN_ST);
+ //aicwf_usb_cancel_all_urbs(usb_dev);//AIDEN
+}
+
+static void aicwf_usb_deinit(struct aic_usb_dev *usbdev)
+{
+ cancel_work_sync(&usbdev->rx_urb_work);
+ aicwf_usb_free_urb(&usbdev->rx_free_list, &usbdev->rx_free_lock);
+ aicwf_usb_free_urb(&usbdev->tx_free_list, &usbdev->tx_free_lock);
+#ifdef CONFIG_USB_MSG_IN_EP
+ if(usbdev->chipid != PRODUCT_ID_AIC8801 &&
+ usbdev->chipid != PRODUCT_ID_AIC8800D81){
+ cancel_work_sync(&usbdev->msg_rx_urb_work);
+ aicwf_usb_free_urb(&usbdev->msg_rx_free_list, &usbdev->msg_rx_free_lock);
+ }
+#endif
+
+ usb_free_urb(usbdev->msg_out_urb);
+}
+
+static void aicwf_usb_rx_urb_work(struct work_struct *work)
+{
+ struct aic_usb_dev *usb_dev = container_of(work, struct aic_usb_dev, rx_urb_work);
+
+ aicwf_usb_rx_submit_all_urb(usb_dev);
+}
+
+#ifdef CONFIG_USB_MSG_IN_EP
+static void aicwf_usb_msg_rx_urb_work(struct work_struct *work)
+{
+ struct aic_usb_dev *usb_dev = container_of(work, struct aic_usb_dev, msg_rx_urb_work);
+
+ aicwf_usb_msg_rx_submit_all_urb(usb_dev);
+}
+#endif
+
+static int aicwf_usb_init(struct aic_usb_dev *usb_dev)
+{
+ int ret = 0;
+
+ usb_dev->tbusy = false;
+ usb_dev->state = USB_DOWN_ST;
+
+ init_waitqueue_head(&usb_dev->msg_wait);
+ init_usb_anchor(&usb_dev->rx_submitted);
+#ifdef CONFIG_USB_MSG_IN_EP
+ if(usb_dev->chipid != PRODUCT_ID_AIC8801 &&
+ usb_dev->chipid != PRODUCT_ID_AIC8800D81){
+ init_usb_anchor(&usb_dev->msg_rx_submitted);
+ }
+#endif
+
+ spin_lock_init(&usb_dev->tx_free_lock);
+ spin_lock_init(&usb_dev->tx_post_lock);
+ spin_lock_init(&usb_dev->rx_free_lock);
+ spin_lock_init(&usb_dev->tx_flow_lock);
+#ifdef CONFIG_USB_MSG_IN_EP
+ if(usb_dev->chipid != PRODUCT_ID_AIC8801 &&
+ usb_dev->chipid != PRODUCT_ID_AIC8800D81){
+ spin_lock_init(&usb_dev->msg_rx_free_lock);
+ }
+#endif
+
+ INIT_LIST_HEAD(&usb_dev->rx_free_list);
+ INIT_LIST_HEAD(&usb_dev->tx_free_list);
+ INIT_LIST_HEAD(&usb_dev->tx_post_list);
+#ifdef CONFIG_USB_MSG_IN_EP
+ if(usb_dev->chipid != PRODUCT_ID_AIC8801 &&
+ usb_dev->chipid != PRODUCT_ID_AIC8800D81){
+ INIT_LIST_HEAD(&usb_dev->msg_rx_free_list);
+ }
+#endif
+
+ atomic_set(&rx_urb_cnt, 0);
+
+ usb_dev->tx_free_count = 0;
+ usb_dev->tx_post_count = 0;
+
+ ret = aicwf_usb_alloc_rx_urb(usb_dev);
+ if (ret) {
+ goto error;
+ }
+ ret = aicwf_usb_alloc_tx_urb(usb_dev);
+ if (ret) {
+ goto error;
+ }
+#ifdef CONFIG_USB_MSG_IN_EP
+ if(usb_dev->chipid != PRODUCT_ID_AIC8801 &&
+ usb_dev->chipid != PRODUCT_ID_AIC8800D81){
+ ret = aicwf_usb_alloc_msg_rx_urb(usb_dev);
+ if (ret) {
+ goto error;
+ }
+ }
+#endif
+
+
+ usb_dev->msg_out_urb = usb_alloc_urb(0, GFP_ATOMIC);
+ if (!usb_dev->msg_out_urb) {
+ usb_err("usb_alloc_urb (msg out) failed\n");
+ ret = ENOMEM;
+ goto error;
+ }
+
+ INIT_WORK(&usb_dev->rx_urb_work, aicwf_usb_rx_urb_work);
+#ifdef CONFIG_USB_MSG_IN_EP
+ if(usb_dev->chipid != PRODUCT_ID_AIC8801 &&
+ usb_dev->chipid != PRODUCT_ID_AIC8800D81){
+ INIT_WORK(&usb_dev->msg_rx_urb_work, aicwf_usb_msg_rx_urb_work);
+ }
+#endif
+
+ return ret;
+ error:
+ usb_err("failed!\n");
+ aicwf_usb_deinit(usb_dev);
+ return ret;
+}
+
+
+static int aicwf_parse_usb(struct aic_usb_dev *usb_dev, struct usb_interface *interface)
+{
+ struct usb_interface_descriptor *interface_desc;
+ struct usb_host_interface *host_interface;
+ struct usb_endpoint_descriptor *endpoint;
+ struct usb_device *usb = usb_dev->udev;
+ int i, endpoints;
+ u8 endpoint_num;
+ int ret = 0;
+
+ usb_dev->bulk_in_pipe = 0;
+ usb_dev->bulk_out_pipe = 0;
+#ifdef CONFIG_USB_MSG_OUT_EP
+ usb_dev->msg_out_pipe = 0;
+#endif
+#ifdef CONFIG_USB_MSG_IN_EP
+ usb_dev->msg_in_pipe = 0;
+#endif
+
+ host_interface = &interface->altsetting[0];
+ interface_desc = &host_interface->desc;
+ endpoints = interface_desc->bNumEndpoints;
+ AICWFDBG(LOGINFO, "%s endpoints = %d\n", __func__, endpoints);
+
+ /* Check device configuration */
+ if (usb->descriptor.bNumConfigurations != 1) {
+ usb_err("Number of configurations: %d not supported\n",
+ usb->descriptor.bNumConfigurations);
+ ret = -ENODEV;
+ goto exit;
+ }
+
+ /* Check deviceclass */
+#ifndef CONFIG_USB_BT
+ if (usb->descriptor.bDeviceClass != 0x00) {
+ usb_err("DeviceClass %d not supported\n",
+ usb->descriptor.bDeviceClass);
+ ret = -ENODEV;
+ goto exit;
+ }
+#endif
+
+ /* Check interface number */
+#ifdef CONFIG_USB_BT
+ if (usb->actconfig->desc.bNumInterfaces != 3) {
+#else
+ if (usb->actconfig->desc.bNumInterfaces != 1) {
+#endif
+ AICWFDBG(LOGERROR, "Number of interfaces: %d not supported\n",
+ usb->actconfig->desc.bNumInterfaces);
+ if(usb_dev->chipid == PRODUCT_ID_AIC8800DC){
+ AICWFDBG(LOGERROR, "AIC8800DC change to AIC8800DW\n");
+ usb_dev->chipid = PRODUCT_ID_AIC8800DW;
+ }else{
+ ret = -ENODEV;
+ goto exit;
+ }
+ }
+
+ if ((interface_desc->bInterfaceClass != USB_CLASS_VENDOR_SPEC) ||
+ (interface_desc->bInterfaceSubClass != 0xff) ||
+ (interface_desc->bInterfaceProtocol != 0xff)) {
+ usb_err("non WLAN interface %d: 0x%x:0x%x:0x%x\n",
+ interface_desc->bInterfaceNumber, interface_desc->bInterfaceClass,
+ interface_desc->bInterfaceSubClass, interface_desc->bInterfaceProtocol);
+ ret = -ENODEV;
+ goto exit;
+ }
+
+ for (i = 0; i < endpoints; i++) {
+ endpoint = &host_interface->endpoint[i].desc;
+ endpoint_num = usb_endpoint_num(endpoint);
+
+ if (usb_endpoint_dir_in(endpoint) &&
+ usb_endpoint_xfer_bulk(endpoint)) {
+ if (!usb_dev->bulk_in_pipe) {
+ usb_dev->bulk_in_pipe = usb_rcvbulkpipe(usb, endpoint_num);
+ }
+#ifdef CONFIG_USB_MSG_IN_EP
+ else if (!usb_dev->msg_in_pipe) {
+ if(usb_dev->chipid != PRODUCT_ID_AIC8801 &&
+ usb_dev->chipid != PRODUCT_ID_AIC8800D81){
+ usb_dev->msg_in_pipe = usb_rcvbulkpipe(usb, endpoint_num);
+ }
+ }
+#endif
+ }
+
+ if (usb_endpoint_dir_out(endpoint) &&
+ usb_endpoint_xfer_bulk(endpoint)) {
+ if (!usb_dev->bulk_out_pipe)
+ {
+ usb_dev->bulk_out_pipe = usb_sndbulkpipe(usb, endpoint_num);
+ }
+#ifdef CONFIG_USB_MSG_OUT_EP
+ else if (!usb_dev->msg_out_pipe) {
+ usb_dev->msg_out_pipe = usb_sndbulkpipe(usb, endpoint_num);
+ }
+#endif
+
+ }
+ }
+
+ if (usb_dev->bulk_in_pipe == 0) {
+ usb_err("No RX (in) Bulk EP found\n");
+ ret = -ENODEV;
+ goto exit;
+ }
+ if (usb_dev->bulk_out_pipe == 0) {
+ usb_err("No TX (out) Bulk EP found\n");
+ ret = -ENODEV;
+ goto exit;
+ }
+#ifdef CONFIG_USB_MSG_OUT_EP
+ if (usb_dev->msg_out_pipe == 0) {
+ usb_err("No TX Msg (out) Bulk EP found\n");
+ }
+#endif
+#ifdef CONFIG_USB_MSG_IN_EP
+ if(usb_dev->chipid != PRODUCT_ID_AIC8801 &&
+ usb_dev->chipid != PRODUCT_ID_AIC8800D81){
+ if (usb_dev->msg_in_pipe == 0) {
+ usb_err("No RX Msg (in) Bulk EP found\n");
+ }
+ }
+#endif
+
+ if (usb->speed == USB_SPEED_HIGH){
+ AICWFDBG(LOGINFO, "Aic high speed USB device detected\n");
+ }else{
+ AICWFDBG(LOGINFO, "Aic full speed USB device detected\n");
+ }
+
+ exit:
+ return ret;
+}
+
+
+
+static struct aicwf_bus_ops aicwf_usb_bus_ops = {
+ .start = aicwf_usb_bus_start,
+ .stop = aicwf_usb_bus_stop,
+ .txdata = aicwf_usb_bus_txdata,
+ .txmsg = aicwf_usb_bus_txmsg,
+};
+
+
+#ifdef CONFIG_GPIO_WAKEUP
+
+static irqreturn_t rwnx_irq_handler(int irq, void *para)
+{
+ unsigned long irqflags;
+ spin_lock_irqsave(&irq_lock, irqflags);
+ disable_irq_nosync(hostwake_irq_num);
+ //do something
+ printk("%s gpio irq trigger\r\n", __func__);
+ spin_unlock_irqrestore(&irq_lock, irqflags);
+ atomic_dec(&irq_count);
+ return IRQ_HANDLED;
+}
+
+
+static int rwnx_register_hostwake_irq(struct device *dev)
+{
+ int ret = 0;
+ uint irq_flags = 0;
+
+ spin_lock_init(&irq_lock);
+
+//Setting hostwake gpio for platform
+//For Rockchip
+#ifdef CONFIG_PLATFORM_ROCKCHIP
+ hostwake_irq_num = rockchip_wifi_get_oob_irq();
+ printk("%s hostwake_irq_num:%d \r\n", __func__, hostwake_irq_num);
+ irq_flags = (IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL | IORESOURCE_IRQ_SHAREABLE) & IRQF_TRIGGER_MASK;
+ printk("%s irq_flags:%d \r\n", __func__, irq_flags);
+ wakeup_enable = 1;
+#endif //CONFIG_PLATFORM_ROCKCHIP
+
+//For Allwinner
+#ifdef CONFIG_PLATFORM_ALLWINNER
+ int irq_flags;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)
+ hostwake_irq_num = sunxi_wlan_get_oob_irq(&irq_flags, &wakeup_enable);
+#else
+ hostwake_irq_num = sunxi_wlan_get_oob_irq();
+ irq_flags = sunxi_wlan_get_oob_irq_flags();
+ wakeup_enable = 1;
+#endif
+#endif //CONFIG_PLATFORM_ALLWINNER
+
+
+ ret = request_irq(hostwake_irq_num,
+ rwnx_irq_handler, IRQF_TRIGGER_RISING | IRQF_NO_SUSPEND,
+ "rwnx_irq_handler", NULL);
+
+ enable_irq_wake(hostwake_irq_num);
+
+ return ret;
+}
+
+static int rwnx_unregister_hostwake_irq(struct device *dev)
+{
+ wakeup_enable = 0;
+
+ printk("%s hostwake_irq_num:%d \r\n", __func__, hostwake_irq_num);
+ disable_irq_wake(hostwake_irq_num);
+ free_irq(hostwake_irq_num, NULL);
+
+ return 0;
+}
+
+#endif //CONFIG_GPIO_WAKEUP
+
+static int aicwf_usb_chipmatch(struct aic_usb_dev *usb_dev, u16_l vid, u16_l pid){
+
+ if(pid == USB_PRODUCT_ID_AIC8801){
+ usb_dev->chipid = PRODUCT_ID_AIC8801;
+ AICWFDBG(LOGINFO, "%s USE AIC8801\r\n", __func__);
+ return 0;
+ }else if(pid == USB_PRODUCT_ID_AIC8800DC){
+ usb_dev->chipid = PRODUCT_ID_AIC8800DC;
+ AICWFDBG(LOGINFO, "%s USE AIC8800DC\r\n", __func__);
+ return 0;
+ }else if(pid == USB_PRODUCT_ID_AIC8800DW){
+ usb_dev->chipid = PRODUCT_ID_AIC8800DW;
+ AICWFDBG(LOGINFO, "%s USE AIC8800DW\r\n", __func__);
+ return 0;
+ }else if(pid == USB_PRODUCT_ID_AIC8800D81){
+ usb_dev->chipid = PRODUCT_ID_AIC8800D81;
+ aicwf_usb_rx_aggr = true;
+ AICWFDBG(LOGINFO, "%s USE AIC8800D81\r\n", __func__);
+ return 0;
+ }else{
+ return -1;
+ }
+}
+
+
+static int aicwf_usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+ int ret = 0;
+ struct usb_device *usb = interface_to_usbdev(intf);
+ struct aicwf_bus *bus_if = NULL;
+ struct device *dev = NULL;
+ struct aicwf_rx_priv* rx_priv = NULL;
+ struct aic_usb_dev *usb_dev = NULL;
+ #ifdef CONFIG_USB_TX_AGGR
+ struct aicwf_tx_priv *tx_priv = NULL;
+ #endif
+
+ usb_dev = kzalloc(sizeof(struct aic_usb_dev), GFP_ATOMIC);
+ if (!usb_dev) {
+ return -ENOMEM;
+ }
+
+ usb_dev->udev = usb;
+ usb_dev->dev = &usb->dev;
+ usb_set_intfdata(intf, usb_dev);
+
+ ret = aicwf_usb_chipmatch(usb_dev, id->idVendor, id->idProduct);
+
+ if (ret < 0) {
+ AICWFDBG(LOGERROR, "%s pid:0x%04X vid:0x%04X unsupport\n",
+ __func__, id->idVendor, id->idProduct);
+ goto out_free_bus;
+ }
+
+ ret = aicwf_parse_usb(usb_dev, intf);
+ if (ret) {
+ AICWFDBG(LOGERROR, "aicwf_parse_usb err %d\n", ret);
+ goto out_free;
+ }
+
+ ret = aicwf_usb_init(usb_dev);
+ if (ret) {
+ AICWFDBG(LOGERROR, "aicwf_usb_init err %d\n", ret);
+ goto out_free;
+ }
+
+ bus_if = kzalloc(sizeof(struct aicwf_bus), GFP_ATOMIC);
+ if (!bus_if) {
+ ret = -ENOMEM;
+ goto out_free_usb;
+ }
+
+ dev = usb_dev->dev;
+ bus_if->dev = dev;
+ usb_dev->bus_if = bus_if;
+ bus_if->bus_priv.usb = usb_dev;
+ dev_set_drvdata(dev, bus_if);
+
+ bus_if->ops = &aicwf_usb_bus_ops;
+
+ rx_priv = aicwf_rx_init(usb_dev);
+ if(!rx_priv) {
+ AICWFDBG(LOGERROR, "rx init failed\n");
+ ret = -1;
+ goto out_free_bus;
+ }
+ usb_dev->rx_priv = rx_priv;
+
+#ifdef CONFIG_USB_TX_AGGR
+ tx_priv = aicwf_tx_init(usb_dev);
+ if(!tx_priv) {
+ usb_err("tx init fail\n");
+ goto out_free_bus;
+ }
+ usb_dev->tx_priv = tx_priv;
+ aicwf_frame_queue_init(&tx_priv->txq, 8, TXQLEN);
+ spin_lock_init(&tx_priv->txqlock);
+ spin_lock_init(&tx_priv->txdlock);
+#endif
+
+ ret = aicwf_bus_init(0, dev);
+ if (ret < 0) {
+ AICWFDBG(LOGERROR, "aicwf_bus_init err %d\n", ret);
+ goto out_free_bus;
+ }
+
+ ret = aicwf_bus_start(bus_if);
+ if (ret < 0) {
+ AICWFDBG(LOGERROR, "aicwf_bus_start err %d\n", ret);
+ goto out_free_bus;
+ }
+
+ ret = aicwf_rwnx_usb_platform_init(usb_dev);
+ if (ret < 0) {
+ AICWFDBG(LOGERROR, "aicwf_rwnx_usb_platform_init err %d\n", ret);
+ goto out_free_bus;
+ }
+ aicwf_hostif_ready();
+
+#ifdef CONFIG_GPIO_WAKEUP
+ rwnx_register_hostwake_irq(usb_dev->dev);
+#endif
+
+ return 0;
+
+out_free_bus:
+ aicwf_bus_deinit(dev);
+ kfree(bus_if);
+out_free_usb:
+ aicwf_usb_deinit(usb_dev);
+out_free:
+ usb_err("failed with errno %d\n", ret);
+ kfree(usb_dev);
+ usb_set_intfdata(intf, NULL);
+ return ret;
+}
+
+static void aicwf_usb_disconnect(struct usb_interface *intf)
+{
+ struct aic_usb_dev *usb_dev =
+ (struct aic_usb_dev *) usb_get_intfdata(intf);
+ AICWFDBG(LOGINFO, "%s Enter\r\n", __func__);
+
+ if(g_rwnx_plat->wait_disconnect_cb == false){
+ atomic_set(&aicwf_deinit_atomic, 0);
+ down(&aicwf_deinit_sem);
+ }
+
+ if (!usb_dev){
+ AICWFDBG(LOGERROR, "%s usb_dev is null \r\n", __func__);
+ return;
+ }
+
+#if 0
+ if(timer_pending(&usb_dev->rwnx_hw->p2p_alive_timer) && usb_dev->rwnx_hw->is_p2p_alive == 1){
+ printk("%s del timer rwnx_hw->p2p_alive_timer \r\n", __func__);
+ rwnx_del_timer(&usb_dev->rwnx_hw->p2p_alive_timer);
+ }
+#endif
+ aicwf_bus_deinit(usb_dev->dev);
+ aicwf_usb_deinit(usb_dev);
+ rwnx_cmd_mgr_deinit(&usb_dev->cmd_mgr);
+
+#ifdef CONFIG_GPIO_WAKEUP
+ rwnx_unregister_hostwake_irq(usb_dev->dev);
+#endif
+
+ if (usb_dev->rx_priv)
+ aicwf_rx_deinit(usb_dev->rx_priv);
+
+ kfree(usb_dev->bus_if);
+ kfree(usb_dev);
+ AICWFDBG(LOGINFO, "%s exit\r\n", __func__);
+ up(&aicwf_deinit_sem);
+ atomic_set(&aicwf_deinit_atomic, 1);
+}
+
+static int aicwf_usb_suspend(struct usb_interface *intf, pm_message_t state)
+{
+ struct aic_usb_dev *usb_dev =
+ (struct aic_usb_dev *) usb_get_intfdata(intf);
+#ifdef CONFIG_GPIO_WAKEUP
+ struct rwnx_vif *rwnx_vif, *tmp;
+ //unsigned long irqflags;
+#endif
+
+ printk("%s enter\r\n", __func__);
+
+#ifdef CONFIG_GPIO_WAKEUP
+// spin_lock_irqsave(&irq_lock, irqflags);
+// rwnx_enable_hostwake_irq();
+// spin_unlock_irqrestore(&irq_lock, irqflags);
+ atomic_inc(&irq_count);
+
+ list_for_each_entry_safe(rwnx_vif, tmp, &usb_dev->rwnx_hw->vifs, list) {
+ if (rwnx_vif->ndev)
+ netif_device_detach(rwnx_vif->ndev);
+ }
+#endif
+
+ aicwf_usb_state_change(usb_dev, USB_SLEEP_ST);
+ aicwf_bus_stop(usb_dev->bus_if);
+
+
+ return 0;
+}
+
+static int aicwf_usb_resume(struct usb_interface *intf)
+{
+ struct aic_usb_dev *usb_dev =
+ (struct aic_usb_dev *) usb_get_intfdata(intf);
+#ifdef CONFIG_GPIO_WAKEUP
+ struct rwnx_vif *rwnx_vif, *tmp;
+// unsigned long irqflags;
+#endif
+ printk("%s enter\r\n", __func__);
+
+#ifdef CONFIG_GPIO_WAKEUP
+// spin_lock_irqsave(&irq_lock, irqflags);
+// rwnx_disable_hostwake_irq();
+// spin_unlock_irqrestore(&irq_lock, irqflags);
+ atomic_dec(&irq_count);
+
+ list_for_each_entry_safe(rwnx_vif, tmp, &usb_dev->rwnx_hw->vifs, list) {
+ if (rwnx_vif->ndev)
+ netif_device_attach(rwnx_vif->ndev);
+ }
+#endif
+
+ if (usb_dev->state == USB_UP_ST)
+ return 0;
+
+ aicwf_bus_start(usb_dev->bus_if);
+ return 0;
+}
+
+static int aicwf_usb_reset_resume(struct usb_interface *intf)
+{
+ return aicwf_usb_resume(intf);
+}
+
+static struct usb_device_id aicwf_usb_id_table[] = {
+#ifndef CONFIG_USB_BT
+ {USB_DEVICE(USB_VENDOR_ID_AIC, USB_PRODUCT_ID_AIC8800)},
+#else
+ {USB_DEVICE_AND_INTERFACE_INFO(USB_VENDOR_ID_AIC, USB_PRODUCT_ID_AIC8801, 0xff, 0xff, 0xff)},
+ {USB_DEVICE_AND_INTERFACE_INFO(USB_VENDOR_ID_AIC, USB_PRODUCT_ID_AIC8800D81, 0xff, 0xff, 0xff)},
+ {USB_DEVICE_AND_INTERFACE_INFO(USB_VENDOR_ID_AIC, USB_PRODUCT_ID_AIC8800DC, 0xff, 0xff, 0xff)},
+ {USB_DEVICE(USB_VENDOR_ID_AIC, USB_PRODUCT_ID_AIC8800DW)},
+#endif
+ {}
+};
+
+MODULE_DEVICE_TABLE(usb, aicwf_usb_id_table);
+
+static struct usb_driver aicwf_usbdrvr = {
+ .name = KBUILD_MODNAME,
+ .probe = aicwf_usb_probe,
+ .disconnect = aicwf_usb_disconnect,
+ .id_table = aicwf_usb_id_table,
+ .suspend = aicwf_usb_suspend,
+ .resume = aicwf_usb_resume,
+ .reset_resume = aicwf_usb_reset_resume,
+#ifdef ANDROID_PLATFORM
+ .supports_autosuspend = 1,
+#else
+ .supports_autosuspend = 0,
+#endif
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0)
+ .disable_hub_initiated_lpm = 1,
+#endif
+};
+
+void aicwf_usb_register(void)
+{
+ if (usb_register(&aicwf_usbdrvr) < 0) {
+ usb_err("usb_register failed\n");
+ }
+}
+
+void aicwf_usb_exit(void)
+{
+ int retry = 5;
+ AICWFDBG(LOGINFO, "%s Enter\r\n", __func__);
+
+ AICWFDBG(LOGDEBUG, "%s in_interrupt:%d in_softirq:%d in_atomic:%d\r\n", __func__, (int)in_interrupt(), (int)in_softirq(), (int)in_atomic());
+
+ do{
+ AICWFDBG(LOGINFO, "aicwf_deinit_atomic is busy. waiting for 500ms retry:%d \r\n",
+ retry);
+ mdelay(500);
+ retry--;
+ if(retry == 0){
+ break;
+ }
+ }while(atomic_read(&aicwf_deinit_atomic) == 0);
+
+ atomic_set(&aicwf_deinit_atomic, 0);
+ if(down_timeout(&aicwf_deinit_sem, msecs_to_jiffies(SEM_TIMOUT)) != 0){
+ AICWFDBG(LOGERROR, "%s semaphore waiting timeout\r\n", __func__);
+ }
+
+ if(g_rwnx_plat){
+ g_rwnx_plat->wait_disconnect_cb = false;
+ }
+
+
+
+ if(!g_rwnx_plat || !g_rwnx_plat->enabled){
+ AICWFDBG(LOGINFO, "g_rwnx_plat is not ready. waiting for 500ms\r\n");
+ mdelay(500);
+ }
+
+#if 1
+ if(g_rwnx_plat && g_rwnx_plat->enabled){
+ rwnx_platform_deinit(g_rwnx_plat->usbdev->rwnx_hw);
+ }
+#endif
+
+ up(&aicwf_deinit_sem);
+ atomic_set(&aicwf_deinit_atomic, 1);
+
+ AICWFDBG(LOGINFO, "%s usb_deregister \r\n", __func__);
+
+ usb_deregister(&aicwf_usbdrvr);
+ //mdelay(500);
+ if(g_rwnx_plat){
+ kfree(g_rwnx_plat);
+ }
+
+ AICWFDBG(LOGINFO, "%s exit\r\n", __func__);
+
+}
diff --git a/drivers/net/wireless/aic8800/aic8800_fdrv/aicwf_usb.h b/drivers/net/wireless/aic8800/aic8800_fdrv/aicwf_usb.h
new file mode 100644
index 000000000000..dd4143cdc103
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic8800_fdrv/aicwf_usb.h
@@ -0,0 +1,173 @@
+/**
+ * aicwf_usb.h
+ *
+ * USB function declarations
+ *
+ * Copyright (C) AICSemi 2018-2020
+ */
+
+#ifndef _AICWF_USB_H_
+#define _AICWF_USB_H_
+
+#include <linux/usb.h>
+#include "rwnx_cmds.h"
+
+#ifdef AICWF_USB_SUPPORT
+
+/* USB Device ID */
+#define USB_VENDOR_ID_AIC 0xA69C
+
+#ifndef CONFIG_USB_BT
+#define USB_PRODUCT_ID_AIC8800 0x8800
+#else
+#define USB_PRODUCT_ID_AIC8801 0x8801
+#define USB_PRODUCT_ID_AIC8800DC 0x88dc
+#define USB_PRODUCT_ID_AIC8800DW 0x88dd
+#define USB_PRODUCT_ID_AIC8800D81 0x8d81
+
+#endif
+
+enum AICWF_IC{
+ PRODUCT_ID_AIC8801 = 0,
+ PRODUCT_ID_AIC8800DC,
+ PRODUCT_ID_AIC8800DW,
+ PRODUCT_ID_AIC8800D81
+};
+
+
+#define AICWF_USB_RX_URBS (200)//(200)
+#ifdef CONFIG_USB_MSG_IN_EP
+#define AICWF_USB_MSG_RX_URBS (100)
+#endif
+#ifdef CONFIG_USB_TX_AGGR
+#define TXQLEN (2048*4)
+#define AICWF_USB_TX_URBS (50)
+#else
+#define AICWF_USB_TX_URBS 200//(100)
+#endif
+#define AICWF_USB_TX_LOW_WATER (AICWF_USB_TX_URBS/4)//25%
+#define AICWF_USB_TX_HIGH_WATER (AICWF_USB_TX_LOW_WATER*3)//75%
+#define AICWF_USB_AGGR_MAX_PKT_SIZE (2048*30)
+#define AICWF_USB_MAX_PKT_SIZE (2048)
+#define AICWF_USB_FC_PERSTA_HIGH_WATER 64
+#define AICWF_USB_FC_PERSTA_LOW_WATER 16
+
+
+typedef enum {
+ USB_TYPE_DATA = 0X00,
+ USB_TYPE_CFG = 0X10,
+ USB_TYPE_CFG_CMD_RSP = 0X11,
+ USB_TYPE_CFG_DATA_CFM = 0X12,
+ USB_TYPE_CFG_PRINT = 0X13
+} usb_type;
+
+enum aicwf_usb_state {
+ USB_DOWN_ST,
+ USB_UP_ST,
+ USB_SLEEP_ST
+};
+
+struct aicwf_usb_buf {
+ struct list_head list;
+ struct aic_usb_dev *usbdev;
+ struct urb *urb;
+ struct sk_buff *skb;
+#ifdef CONFIG_PREALLOC_RX_SKB
+ struct rx_buff *rx_buff;
+#endif
+ #ifdef CONFIG_USB_NO_TRANS_DMA_MAP
+ u8 *data_buf;
+ dma_addr_t data_dma_trans_addr;
+ #endif
+ bool cfm;
+ #ifdef CONFIG_USB_TX_AGGR
+ u8 aggr_cnt;
+ #endif
+ u8* usb_align_data;
+};
+
+struct aic_usb_dev {
+ struct rwnx_hw *rwnx_hw;
+ struct aicwf_bus *bus_if;
+ struct usb_device *udev;
+ struct device *dev;
+ struct aicwf_rx_priv* rx_priv;
+ enum aicwf_usb_state state;
+ struct rwnx_cmd_mgr cmd_mgr;
+
+#ifdef CONFIG_USB_TX_AGGR
+ struct aicwf_tx_priv *tx_priv;
+#endif
+
+ struct usb_anchor rx_submitted;
+ struct work_struct rx_urb_work;
+#ifdef CONFIG_USB_MSG_IN_EP
+ struct usb_anchor msg_rx_submitted;
+ struct work_struct msg_rx_urb_work;
+#endif
+
+ spinlock_t rx_free_lock;
+ spinlock_t tx_free_lock;
+ spinlock_t tx_post_lock;
+ spinlock_t tx_flow_lock;
+#ifdef CONFIG_USB_MSG_IN_EP
+ spinlock_t msg_rx_free_lock;
+#endif
+
+ struct list_head rx_free_list;
+ struct list_head tx_free_list;
+ struct list_head tx_post_list;
+#ifdef CONFIG_USB_MSG_IN_EP
+ struct list_head msg_rx_free_list;
+#endif
+
+ uint bulk_in_pipe;
+ uint bulk_out_pipe;
+#ifdef CONFIG_USB_MSG_OUT_EP
+ uint msg_out_pipe;
+#endif
+#ifdef CONFIG_USB_MSG_IN_EP
+ uint msg_in_pipe;
+#endif
+
+ int tx_free_count;
+ int tx_post_count;
+
+ struct aicwf_usb_buf usb_tx_buf[AICWF_USB_TX_URBS];
+ struct aicwf_usb_buf usb_rx_buf[AICWF_USB_RX_URBS];
+#ifdef CONFIG_USB_MSG_IN_EP
+ struct aicwf_usb_buf usb_msg_rx_buf[AICWF_USB_MSG_RX_URBS];
+#endif
+
+ int msg_finished;
+ wait_queue_head_t msg_wait;
+ ulong msg_busy;
+ struct urb *msg_out_urb;
+ #ifdef CONFIG_USB_NO_TRANS_DMA_MAP
+ dma_addr_t cmd_dma_trans_addr;
+ #endif
+
+#ifdef CONFIG_RX_TASKLET//AIDEN tasklet
+ struct tasklet_struct recv_tasklet;
+#endif
+#ifdef CONFIG_TX_TASKLET//AIDEN tasklet
+ struct tasklet_struct xmit_tasklet;
+#endif
+ u16 chipid;
+ bool tbusy;
+};
+
+extern void aicwf_usb_exit(void);
+extern void aicwf_usb_register(void);
+extern void aicwf_usb_tx_flowctrl(struct rwnx_hw *rwnx_hw, bool state);
+#ifdef CONFIG_USB_MSG_IN_EP
+int usb_msg_busrx_thread(void *data);
+#endif
+int usb_bustx_thread(void *data);
+int usb_busrx_thread(void *data);
+
+
+extern void aicwf_hostif_ready(void);
+
+#endif /* AICWF_USB_SUPPORT */
+#endif /* _AICWF_USB_H_ */
diff --git a/drivers/net/wireless/aic8800/aic8800_fdrv/aicwf_wext_linux.c b/drivers/net/wireless/aic8800/aic8800_fdrv/aicwf_wext_linux.c
new file mode 100644
index 000000000000..de591f69d384
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic8800_fdrv/aicwf_wext_linux.c
@@ -0,0 +1,1201 @@
+#include <linux/etherdevice.h>
+#include <linux/netdevice.h>
+#include <net/netlink.h>
+#include <linux/wireless.h>
+#include <linux/nl80211.h>
+#include <net/iw_handler.h>
+#include <uapi/linux/if_arp.h>
+#include <linux/vmalloc.h>
+#include "aicwf_debug.h"
+#include "rwnx_msg_tx.h"
+#include "aicwf_wext_linux.h"
+#include "rwnx_defs.h"
+
+#define WLAN_CAPABILITY_ESS (1<<0)
+#define WLAN_CAPABILITY_IBSS (1<<1)
+#define WLAN_CAPABILITY_PRIVACY (1<<4)
+
+#define IEEE80211_HT_CAP_SGI_20 0x0020
+#define IEEE80211_HT_CAP_SGI_40 0x0040
+
+#define IEEE80211_HE_CH_BW_SET_160_80P80 1 << 4
+#define IEEE80211_HE_CH_BW_SET_160_IN_5G 1 << 3
+#define IEEE80211_HE_CH_BW_SET_40_AND_80_IN_5G 1 << 2
+#define IEEE80211_HE_CH_BW_SET_40_IN_2_4G 1 << 1
+
+#if WIRELESS_EXT < 17
+ #define IW_QUAL_QUAL_INVALID 0x10
+ #define IW_QUAL_LEVEL_INVALID 0x20
+ #define IW_QUAL_NOISE_INVALID 0x40
+ #define IW_QUAL_QUAL_UPDATED 0x1
+ #define IW_QUAL_LEVEL_UPDATED 0x2
+ #define IW_QUAL_NOISE_UPDATED 0x4
+#endif
+
+#define MAX_WPA_IE_LEN (256)
+
+
+/* 20/40/80, ShortGI, MCS Rate */
+const u16 VHT_MCS_DATA_RATE[3][2][30] = {
+ {
+ {
+ 13, 26, 39, 52, 78, 104, 117, 130, 156, 156,
+ 26, 52, 78, 104, 156, 208, 234, 260, 312, 312,
+ 39, 78, 117, 156, 234, 312, 351, 390, 468, 520
+ }, /* Long GI, 20MHz */
+ {
+ 14, 29, 43, 58, 87, 116, 130, 144, 173, 173,
+ 29, 58, 87, 116, 173, 231, 260, 289, 347, 347,
+ 43, 87, 130, 173, 260, 347, 390, 433, 520, 578
+ }
+ }, /* Short GI, 20MHz */
+ {
+ {
+ 27, 54, 81, 108, 162, 216, 243, 270, 324, 360,
+ 54, 108, 162, 216, 324, 432, 486, 540, 648, 720,
+ 81, 162, 243, 324, 486, 648, 729, 810, 972, 1080
+ }, /* Long GI, 40MHz */
+ {
+ 30, 60, 90, 120, 180, 240, 270, 300, 360, 400,
+ 60, 120, 180, 240, 360, 480, 540, 600, 720, 800,
+ 90, 180, 270, 360, 540, 720, 810, 900, 1080, 1200
+ }
+ }, /* Short GI, 40MHz */
+ {
+ {
+ 59, 117, 176, 234, 351, 468, 527, 585, 702, 780,
+ 117, 234, 351, 468, 702, 936, 1053, 1170, 1404, 1560,
+ 176, 351, 527, 702, 1053, 1404, 1580, 1755, 2106, 2340
+ }, /* Long GI, 80MHz */
+ {
+ 65, 130, 195, 260, 390, 520, 585, 650, 780, 867,
+ 130, 260, 390, 520, 780, 1040, 1170, 1300, 1560, 1734,
+ 195, 390, 585, 780, 1170, 1560, 1755, 1950, 2340, 2600
+ } /* Short GI, 80MHz */
+ }
+};
+
+
+/*HE 20/40/80,MCS Rate */
+const u16 HE_MCS_DATA_RATE[3][30] = {
+ {
+ 9, 17, 26, 34, 52, 69, 77, 86, 103, 115,
+ 129, 143, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ }, /* 20MHz */
+ {
+ 17, 34, 52, 69, 103, 138, 155, 172, 207, 229,
+ 258, 286, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ }, /* 40MHz */
+ {
+ 36, 72, 108, 144, 216, 288, 324, 360, 432, 480,
+ 540, 601, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ }, /* 80MHz */
+
+};
+
+
+#if WIRELESS_EXT >= 17
+struct iw_statistics iwstats;
+
+static struct iw_statistics *aicwf_get_wireless_stats(struct net_device *dev)
+{
+ int tmp_level = -100;
+ int tmp_qual = 0;
+ int tmp_noise = 0;
+
+ struct rwnx_vif* rwnx_vif = netdev_priv(dev);
+ struct rwnx_hw* rwnx_hw = rwnx_vif->rwnx_hw;
+ struct rwnx_sta *sta = NULL;
+
+ union rwnx_rate_ctrl_info *rate_info;
+ struct mm_get_sta_info_cfm cfm;
+
+ AICWFDBG(LOGTRACE, "%s Enter", __func__);
+
+ if(rwnx_vif->sta.ap){
+ sta = rwnx_vif->sta.ap;
+ rwnx_send_get_sta_info_req(rwnx_hw, sta->sta_idx, &cfm);
+ rate_info = (union rwnx_rate_ctrl_info *)&cfm.rate_info;
+ tmp_level = cfm.rssi;
+ }
+
+
+ iwstats.qual.level = tmp_level;
+ iwstats.qual.qual = tmp_qual;
+ iwstats.qual.noise = tmp_noise;
+ iwstats.qual.updated = 0x07;
+ iwstats.qual.updated = iwstats.qual.updated | IW_QUAL_DBM;
+
+ return &iwstats;
+}
+#endif
+
+
+static int aicwf_get_name(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct rwnx_vif* rwnx_vif = netdev_priv(dev);
+ struct rwnx_hw* rwnx_hw = rwnx_vif->rwnx_hw;
+ struct rwnx_sta *sta = NULL;
+
+ union rwnx_rate_ctrl_info *rate_info;
+ struct mm_get_sta_info_cfm cfm;
+
+ AICWFDBG(LOGTRACE, "%s Enter", __func__);
+
+ if(rwnx_vif->sta.ap){
+ sta = rwnx_vif->sta.ap;
+ rwnx_send_get_sta_info_req(rwnx_hw, sta->sta_idx, &cfm);
+ rate_info = (union rwnx_rate_ctrl_info *)&cfm.rate_info;
+
+ switch (rate_info->formatModTx) {
+ case FORMATMOD_NON_HT:
+ case FORMATMOD_NON_HT_DUP_OFDM:
+ snprintf(wrqu->name, IFNAMSIZ, "IEEE 802.11bg");
+ break;
+ case FORMATMOD_HT_MF:
+ case FORMATMOD_HT_GF:
+ snprintf(wrqu->name, IFNAMSIZ, "IEEE 802.11n");
+ break;
+ case FORMATMOD_VHT:
+ snprintf(wrqu->name, IFNAMSIZ, "IEEE 802.11ac");
+ break;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)
+ case FORMATMOD_HE_MU:
+ case FORMATMOD_HE_SU:
+ case FORMATMOD_HE_ER:
+ snprintf(wrqu->name, IFNAMSIZ, "IEEE 802.11ax");
+ break;
+#else
+ //kernel not support he
+ case FORMATMOD_HE_MU:
+ case FORMATMOD_HE_SU:
+ case FORMATMOD_HE_ER:
+ snprintf(wrqu->name, IFNAMSIZ, "IEEE 802.11ax");
+ break;
+#endif
+ }
+
+
+ }else{
+ snprintf(wrqu->name, IFNAMSIZ, "unassociated");
+ }
+
+
+ return 0;
+}
+
+static int aicwf_get_freq(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct rwnx_vif* rwnx_vif = netdev_priv(dev);
+
+
+ AICWFDBG(LOGTRACE, "%s Enter", __func__);
+
+ if(rwnx_vif->sta.ap){
+ wrqu->freq.m = rwnx_vif->sta.ap->center_freq * 100000;
+ wrqu->freq.e = 1;
+ wrqu->freq.i = ieee80211_frequency_to_channel(rwnx_vif->sta.ap->center_freq);
+ }else{
+ wrqu->freq.m = 2412 * 100000;
+ wrqu->freq.e = 1;
+ wrqu->freq.i = 1;
+ }
+
+ return 0;
+}
+
+static int aicwf_get_mode(struct net_device *dev, struct iw_request_info *a,
+ union iwreq_data *wrqu, char *b)
+{
+
+ AICWFDBG(LOGTRACE, "%s Enter", __func__);
+
+ wrqu->mode = IW_MODE_AUTO;
+#if 0
+ if (check_fwstate(pmlmepriv, WIFI_STATION_STATE) == _TRUE)
+ wrqu->mode = IW_MODE_INFRA;
+ else if ((check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE) == _TRUE) ||
+ (check_fwstate(pmlmepriv, WIFI_ADHOC_STATE) == _TRUE))
+
+ wrqu->mode = IW_MODE_ADHOC;
+ else if (check_fwstate(pmlmepriv, WIFI_AP_STATE) == _TRUE)
+ wrqu->mode = IW_MODE_MASTER;
+ else if (check_fwstate(pmlmepriv, WIFI_MONITOR_STATE) == _TRUE)
+ wrqu->mode = IW_MODE_MONITOR;
+ else
+ wrqu->mode = IW_MODE_AUTO;
+#endif
+
+ return 0;
+
+}
+
+
+static int aicwf_get_range(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct iw_range *range = (struct iw_range *)extra;
+ u16 val = 0;
+
+ AICWFDBG(LOGTRACE, "%s Enter", __func__);
+
+
+ wrqu->data.length = sizeof(*range);
+ memset(range, 0, sizeof(*range));
+
+ /* Let's try to keep this struct in the same order as in
+ * linux/include/wireless.h
+ */
+
+ /* TODO: See what values we can set, and remove the ones we can't
+ * set, or fill them with some default data.
+ */
+
+ /* ~5 Mb/s real (802.11b) */
+ range->throughput = 5 * 1000 * 1000;
+
+ /* TODO: Not used in 802.11b?
+ * range->min_nwid; Minimal NWID we are able to set */
+ /* TODO: Not used in 802.11b?
+ * range->max_nwid; Maximal NWID we are able to set */
+
+ /* Old Frequency (backward compat - moved lower ) */
+ /* range->old_num_channels;
+ * range->old_num_frequency;
+ * range->old_freq[6]; Filler to keep "version" at the same offset */
+
+ /* signal level threshold range */
+
+ /* Quality of link & SNR stuff */
+ /* Quality range (link, level, noise)
+ * If the quality is absolute, it will be in the range [0 ; max_qual],
+ * if the quality is dBm, it will be in the range [max_qual ; 0].
+ * Don't forget that we use 8 bit arithmetics...
+ *
+ * If percentage range is 0~100
+ * Signal strength dbm range logical is -100 ~ 0
+ * but usually value is -90 ~ -20
+ */
+ range->max_qual.qual = 100;
+#ifdef CONFIG_SIGNAL_DISPLAY_DBM
+ range->max_qual.level = (u8)-100;
+ range->max_qual.noise = (u8)-100;
+ range->max_qual.updated = IW_QUAL_ALL_UPDATED; /* Updated all three */
+ range->max_qual.updated |= IW_QUAL_DBM;
+#else /* !CONFIG_SIGNAL_DISPLAY_DBM */
+ /* percent values between 0 and 100. */
+ range->max_qual.level = 100;
+ range->max_qual.noise = 100;
+ range->max_qual.updated = IW_QUAL_ALL_UPDATED; /* Updated all three */
+#endif /* !CONFIG_SIGNAL_DISPLAY_DBM */
+
+ /* This should contain the average/typical values of the quality
+ * indicator. This should be the threshold between a "good" and
+ * a "bad" link (example : monitor going from green to orange).
+ * Currently, user space apps like quality monitors don't have any
+ * way to calibrate the measurement. With this, they can split
+ * the range between 0 and max_qual in different quality level
+ * (using a geometric subdivision centered on the average).
+ * I expect that people doing the user space apps will feedback
+ * us on which value we need to put in each driver... */
+ range->avg_qual.qual = 92; /* > 8% missed beacons is 'bad' */
+#ifdef CONFIG_SIGNAL_DISPLAY_DBM
+ /* TODO: Find real 'good' to 'bad' threshold value for RSSI */
+ range->avg_qual.level = (u8)-70;
+ range->avg_qual.noise = 0;
+ range->avg_qual.updated = IW_QUAL_ALL_UPDATED; /* Updated all three */
+ range->avg_qual.updated |= IW_QUAL_DBM;
+#else /* !CONFIG_SIGNAL_DISPLAY_DBM */
+ /* TODO: Find real 'good' to 'bad' threshol value for RSSI */
+ range->avg_qual.level = 30;
+ range->avg_qual.noise = 100;
+ range->avg_qual.updated = IW_QUAL_ALL_UPDATED; /* Updated all three */
+#endif /* !CONFIG_SIGNAL_DISPLAY_DBM */
+#if 0
+ range->num_bitrates = RATE_COUNT;
+
+ for (i = 0; i < RATE_COUNT && i < IW_MAX_BITRATES; i++)
+ range->bitrate[i] = rtw_rates[i];
+
+ range->min_frag = MIN_FRAG_THRESHOLD;
+ range->max_frag = MAX_FRAG_THRESHOLD;
+#endif
+
+ range->pm_capa = 0;
+
+ range->we_version_compiled = WIRELESS_EXT;
+ range->we_version_source = 16;
+
+ /* range->retry_capa; What retry options are supported
+ * range->retry_flags; How to decode max/min retry limit
+ * range->r_time_flags; How to decode max/min retry life
+ * range->min_retry; Minimal number of retries
+ * range->max_retry; Maximal number of retries
+ * range->min_r_time; Minimal retry lifetime
+ * range->max_r_time; Maximal retry lifetime */
+#if 0
+ for (i = 0, val = 0; i < rfctl->max_chan_nums; i++) {
+
+ /* Include only legal frequencies for some countries */
+ if (rfctl->channel_set[i].ChannelNum != 0) {
+ range->freq[val].i = rfctl->channel_set[i].ChannelNum;
+ range->freq[val].m = rtw_ch2freq(rfctl->channel_set[i].ChannelNum) * 100000;
+ range->freq[val].e = 1;
+ val++;
+ }
+
+ if (val == IW_MAX_FREQUENCIES)
+ break;
+ }
+#endif
+ range->num_channels = val;
+ range->num_frequency = val;
+
+ /* Commented by Albert 2009/10/13
+ * The following code will proivde the security capability to network manager.
+ * If the driver doesn't provide this capability to network manager,
+ * the WPA/WPA2 routers can't be choosen in the network manager. */
+
+ /*
+ #define IW_SCAN_CAPA_NONE 0x00
+ #define IW_SCAN_CAPA_ESSID 0x01
+ #define IW_SCAN_CAPA_BSSID 0x02
+ #define IW_SCAN_CAPA_CHANNEL 0x04
+ #define IW_SCAN_CAPA_MODE 0x08
+ #define IW_SCAN_CAPA_RATE 0x10
+ #define IW_SCAN_CAPA_TYPE 0x20
+ #define IW_SCAN_CAPA_TIME 0x40
+ */
+
+#if WIRELESS_EXT > 17
+ range->enc_capa = IW_ENC_CAPA_WPA | IW_ENC_CAPA_WPA2 |
+ IW_ENC_CAPA_CIPHER_TKIP | IW_ENC_CAPA_CIPHER_CCMP;
+#endif
+
+#ifdef IW_SCAN_CAPA_ESSID /* WIRELESS_EXT > 21 */
+ range->scan_capa = IW_SCAN_CAPA_ESSID | IW_SCAN_CAPA_TYPE | IW_SCAN_CAPA_BSSID |
+ IW_SCAN_CAPA_CHANNEL | IW_SCAN_CAPA_MODE | IW_SCAN_CAPA_RATE;
+#endif
+
+
+
+ return 0;
+
+}
+
+
+static char *aicwf_get_iwe_stream_mac_addr(struct rwnx_hw* rwnx_hw,
+ struct iw_request_info *info, struct scanu_result_wext *scan_re,
+ char *start, char *stop, struct iw_event *iwe)
+{
+ /* AP MAC address */
+ iwe->cmd = SIOCGIWAP;
+ iwe->u.ap_addr.sa_family = ARPHRD_ETHER;
+
+ memcpy(iwe->u.ap_addr.sa_data, scan_re->bss->bssid, ETH_ALEN);
+ start = iwe_stream_add_event(info, start, stop, iwe, IW_EV_ADDR_LEN);
+ return start;
+}
+
+
+static inline char *aicwf_get_iwe_stream_essid(struct rwnx_hw* rwnx_hw,
+ struct iw_request_info *info, struct scanu_result_wext *scan_re,
+ char *start, char *stop, struct iw_event *iwe)
+{
+ struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt*)scan_re->payload;
+ const u8 *ie = mgmt->u.beacon.variable;
+ u8 *ssid;
+ int ssid_len = 0;
+
+ //get ssid len form ie
+ ssid_len = ie[1];
+
+ //get ssid form ie
+ ssid = (u8*)vmalloc(sizeof(char)* (ssid_len + 1));
+ memset(ssid, 0, (ssid_len + 1));
+ memcpy(ssid, &ie[2], ssid_len);
+
+
+ //AICWFDBG(LOGDEBUG, "%s len:%d ssid:%s\r\n", __func__, ssid_len, ssid);
+
+ /* Add the ESSID */
+ iwe->cmd = SIOCGIWESSID;
+ iwe->u.data.flags = 1;
+ iwe->u.data.length = min((u16)ssid_len, (u16)32);
+ start = iwe_stream_add_point(info, start, stop, iwe, ssid);
+
+ vfree(ssid);
+
+ return start;
+}
+
+
+
+static inline char *aicwf_get_iwe_stream_protocol(struct rwnx_hw* rwnx_hw,
+ struct iw_request_info *info, struct scanu_result_wext *scan_re,
+ char *start, char *stop, struct iw_event *iwe)
+{
+ u16 ht_cap = false;
+ u16 vht_cap = false;
+ u16 he_cap = false;
+ struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt*)scan_re->payload;
+ u8 *payload = mgmt->u.beacon.variable;
+ const u8 *ie_content;
+
+ /* parsing HT_CAP_IE */
+ ie_content = NULL;
+ ie_content = cfg80211_find_ie(WLAN_EID_HT_CAPABILITY, payload, scan_re->ind->length);
+ if (ie_content != NULL){
+ ht_cap = true;
+ }
+
+ /* parsing VHT_CAP_IE */
+ ie_content = NULL;
+ ie_content = cfg80211_find_ie(WLAN_EID_VHT_CAPABILITY, payload, scan_re->ind->length);
+ if (ie_content != NULL){
+ vht_cap = true;
+ }
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)|| defined(CONFIG_HE_FOR_OLD_KERNEL)
+ /* parsing HE_CAP_IE */
+ ie_content = NULL;
+ ie_content = cfg80211_find_ie(WLAN_EID_EXTENSION, payload, scan_re->ind->length);
+ if (ie_content != NULL && ie_content[2] == WLAN_EID_EXT_HE_CAPABILITY){
+ he_cap = true;
+ }
+#endif
+
+ /* Add the protocol name */
+ iwe->cmd = SIOCGIWNAME;
+
+ if (ieee80211_frequency_to_channel(scan_re->ind->center_freq) > 14) {
+ if (he_cap == true){
+ snprintf(iwe->u.name, IFNAMSIZ, "IEEE 802.11ax");
+ }else if(vht_cap == true){
+ snprintf(iwe->u.name, IFNAMSIZ, "IEEE 802.11ac");
+ }else{
+ if (ht_cap == true)
+ snprintf(iwe->u.name, IFNAMSIZ, "IEEE 802.11an");
+ else
+ snprintf(iwe->u.name, IFNAMSIZ, "IEEE 802.11a");
+ }
+ } else {
+ if(he_cap == true){
+ snprintf(iwe->u.name, IFNAMSIZ, "IEEE 802.11ax");
+ }else if (ht_cap == true){
+ snprintf(iwe->u.name, IFNAMSIZ, "IEEE 802.11bgn");
+ }else{
+ snprintf(iwe->u.name, IFNAMSIZ, "IEEE 802.11bg");
+ }
+ }
+
+ start = iwe_stream_add_event(info, start, stop, iwe, IW_EV_CHAR_LEN);
+ return start;
+}
+
+
+
+static inline char *aicwf_get_iwe_stream_rssi(struct rwnx_hw* rwnx_hw,
+ struct iw_request_info *info, struct scanu_result_wext *scan_re,
+ char *start, char *stop, struct iw_event *iwe)
+{
+ iwe->cmd = IWEVQUAL;
+
+ iwe->u.qual.updated = IW_QUAL_QUAL_UPDATED | IW_QUAL_LEVEL_UPDATED
+ | IW_QUAL_NOISE_INVALID
+ | IW_QUAL_DBM;
+
+ iwe->u.qual.level = (u8)scan_re->ind->rssi;
+ iwe->u.qual.qual = 100;//scan_re->bss->signal;
+ iwe->u.qual.noise = 0;
+
+ start = iwe_stream_add_event(info, start, stop, iwe, IW_EV_QUAL_LEN);
+
+ return start;
+}
+
+static inline char *aicwf_get_iwe_stream_chan(struct rwnx_hw* rwnx_hw,
+ struct iw_request_info *info, struct scanu_result_wext *scan_re,
+ char *start, char *stop, struct iw_event *iwe)
+{
+ /* Add frequency/channel */
+ iwe->cmd = SIOCGIWFREQ;
+ iwe->u.freq.m = scan_re->ind->center_freq * 100000;
+ iwe->u.freq.e = 1;
+ iwe->u.freq.i = ieee80211_frequency_to_channel(scan_re->ind->center_freq);
+ start = iwe_stream_add_event(info, start, stop, iwe, IW_EV_FREQ_LEN);
+
+ return start;
+}
+
+
+static inline char *aicwf_get_iwe_stream_mode(struct rwnx_hw* rwnx_hw,
+ struct iw_request_info *info, struct scanu_result_wext *scan_re,
+ char *start, char *stop, struct iw_event *iwe)
+{
+
+ u16 cap = scan_re->bss->capability;
+ /* Add mode */
+ if (cap & (WLAN_CAPABILITY_IBSS | WLAN_CAPABILITY_ESS)) {
+ iwe->cmd = SIOCGIWMODE;
+ if (cap & WLAN_CAPABILITY_ESS)
+ iwe->u.mode = IW_MODE_MASTER;
+ else
+ iwe->u.mode = IW_MODE_ADHOC;
+
+ start = iwe_stream_add_event(info, start, stop, iwe, IW_EV_UINT_LEN);
+ }
+ return start;
+
+}
+
+static inline char *aicwf_get_iwe_stream_encryption(struct rwnx_hw* rwnx_hw,
+ struct iw_request_info *info, struct scanu_result_wext *scan_re,
+ char *start, char *stop, struct iw_event *iwe)
+{
+ struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt*)scan_re->payload;
+ const u8 *ie = mgmt->u.beacon.variable;
+ u16 cap = scan_re->bss->capability;
+ u8 *ssid;
+ int ssid_len = 0;
+
+ //get ssid len form ie
+ ssid_len = ie[1];
+
+ //get ssid form ie
+ ssid = (u8*)vmalloc(sizeof(char)* (ssid_len + 1));
+ memset(ssid, 0, (ssid_len + 1));
+ memcpy(ssid, &ie[2], ssid_len);
+
+
+ /* Add encryption capability */
+ iwe->cmd = SIOCGIWENCODE;
+ if (cap & WLAN_CAPABILITY_PRIVACY)
+ iwe->u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
+ else
+ iwe->u.data.flags = IW_ENCODE_DISABLED;
+ iwe->u.data.length = 0;
+ start = iwe_stream_add_point(info, start, stop, iwe, ssid);
+ vfree(ssid);
+
+ return start;
+
+}
+
+static inline char *aicwf_get_iwe_stream_rate(struct rwnx_hw* rwnx_hw,
+ struct iw_request_info *info, struct scanu_result_wext *scan_re,
+ char *start, char *stop, struct iw_event *iwe)
+{
+ struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt*)scan_re->payload;
+ u8 *payload = mgmt->u.beacon.variable;
+ const u8 *ie_content;
+
+ u16 mcs_rate = 0;
+ u8 bw_40MHz = 0;
+ u8 short_GI = 0;
+ u16 max_rate = 0;
+ u8 bw_160MHz = 0;
+
+ u16 ht_cap = false;
+ struct ieee80211_ht_cap *ht_capie;
+
+ u16 vht_cap = false;
+ u8 tx_mcs_map[2];
+ u8 tx_mcs_index = 0;
+ u16 vht_data_rate = 0;
+
+ u16 he_cap = false;
+ u8 he_ch_width_set = 0;
+ u8 he_bw = 0;
+
+ /* parsing HT_CAP_IE */
+ ie_content = NULL;
+ ie_content = cfg80211_find_ie(WLAN_EID_HT_CAPABILITY, payload, scan_re->ind->length);
+ if (ie_content != NULL){
+ ht_cap = true;
+ ht_capie = (struct ieee80211_ht_cap *)(ie_content + 2);
+ bw_40MHz = (ht_capie->cap_info & NL80211_CHAN_WIDTH_40) ? 1 : 0;
+ short_GI = (ht_capie->cap_info & (IEEE80211_HT_CAP_SGI_20 | IEEE80211_HT_CAP_SGI_40)) ? 1 : 0;
+ memcpy(&mcs_rate, ht_capie->mcs.rx_mask, 2);
+ }
+
+
+
+ /* parsing VHT_CAP_IE */
+ ie_content = NULL;
+ ie_content = cfg80211_find_ie(WLAN_EID_VHT_CAPABILITY, payload, scan_re->ind->length);
+ if (ie_content != NULL){
+
+ vht_cap = true;
+ bw_160MHz = ((*(u8*)(ie_content + 2)) >> 2) & ((u8)(0xFF >> (8 - (2))));
+ if (bw_160MHz){
+ short_GI = ((*(u8*)(ie_content + 2)) >> 6) & ((u8)(0xFF >> (8 - (1))));
+ }else{
+ short_GI = ((*(u8*)(ie_content + 2)) >> 5) & ((u8)(0xFF >> (8 - (1))));
+ }
+
+ memcpy(tx_mcs_map, ((ie_content + 2)+8), 2);
+
+ tx_mcs_index = (tx_mcs_map[0] & 0x0F) - 1;
+ if(ieee80211_frequency_to_channel(scan_re->ind->center_freq) > 14){
+ vht_data_rate = VHT_MCS_DATA_RATE[2][short_GI][tx_mcs_index];
+ }else{
+ vht_data_rate = VHT_MCS_DATA_RATE[1][short_GI][tx_mcs_index];
+ }
+ //TO DO:
+ //need to counter antenna number for AC
+ }
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)|| defined(CONFIG_HE_FOR_OLD_KERNEL)
+ /* parsing HE_CAP_IE */
+ ie_content = NULL;
+ /*0xFF+len+WLAN_EID_EXTENSION+playload */
+ ie_content = cfg80211_find_ie(WLAN_EID_EXTENSION, payload, scan_re->ind->length);
+ if (ie_content != NULL && ie_content[2] == WLAN_EID_EXT_HE_CAPABILITY){
+ he_cap = true;
+ he_ch_width_set = ie_content[8];
+ if(he_ch_width_set & IEEE80211_HE_CH_BW_SET_160_80P80){
+ he_bw = NL80211_CHAN_WIDTH_80;
+ }else if(he_ch_width_set & IEEE80211_HE_CH_BW_SET_160_IN_5G){
+ he_bw = NL80211_CHAN_WIDTH_160;
+ }else if(he_ch_width_set & IEEE80211_HE_CH_BW_SET_40_AND_80_IN_5G){
+ he_bw = NL80211_CHAN_WIDTH_40;
+ }else if(he_ch_width_set & IEEE80211_HE_CH_BW_SET_40_IN_2_4G){
+ he_bw = NL80211_CHAN_WIDTH_40;
+ }else{
+ he_bw = NL80211_CHAN_WIDTH_20;
+ }
+ //TO DO:
+ //need to counter antenna number for AX
+ }
+#endif
+
+ if(he_cap == true){
+ if(he_bw == NL80211_CHAN_WIDTH_20){
+ max_rate = 144;
+ }else if(he_bw == NL80211_CHAN_WIDTH_40){
+ max_rate = 287;
+ }else if(he_bw == NL80211_CHAN_WIDTH_80){
+ max_rate = 601;
+ }else if(he_bw == NL80211_CHAN_WIDTH_160){
+ max_rate = 1147;
+ }
+ max_rate = max_rate * 2;
+ }else if(vht_cap == true){
+ max_rate = vht_data_rate;
+ }else if (ht_cap == true) {
+ if (mcs_rate & 0x8000) /* MCS15 */
+ max_rate = (bw_40MHz) ? ((short_GI) ? 300 : 270) : ((short_GI) ? 144 : 130);
+
+ else if (mcs_rate & 0x0080) /* MCS7 */
+ max_rate = (bw_40MHz) ? ((short_GI) ? 150 : 135) : ((short_GI) ? 72 : 65);
+ else { /* default MCS7 */
+ max_rate = (bw_40MHz) ? ((short_GI) ? 150 : 135) : ((short_GI) ? 72 : 65);
+ }
+ }
+
+ iwe->cmd = SIOCGIWRATE;
+ iwe->u.bitrate.fixed = iwe->u.bitrate.disabled = 0;
+ iwe->u.bitrate.value = max_rate * 1000000;
+ start = iwe_stream_add_event(info, start, stop, iwe, IW_EV_PARAM_LEN);
+ return start ;
+}
+
+
+
+int aic_get_sec_ie(u8 *in_ie, uint in_len, u8 *rsn_ie, u16 *rsn_len, u8 *wpa_ie, u16 *wpa_len)
+{
+
+ u8 authmode;
+ u8 sec_idx;
+ u8 wpa_oui[4] = {0x00, 0x50, 0xf2, 0x01};
+ uint cnt = 0;
+
+ /* Search required WPA or WPA2 IE and copy to sec_ie[ ] */
+
+ cnt = 0;
+
+ sec_idx = 0;
+
+ while (cnt < in_len) {
+ authmode = in_ie[cnt];
+
+ if ((authmode == 0xdd/*_WPA_IE_ID_*/) && (memcmp(&in_ie[cnt + 2], &wpa_oui[0], 4) == 0)) {
+ if (wpa_ie)
+ memcpy(wpa_ie, &in_ie[cnt], in_ie[cnt + 1] + 2);
+
+ *wpa_len = in_ie[cnt + 1] + 2;
+ cnt += in_ie[cnt + 1] + 2; /* get next */
+ } else {
+ if (authmode == 0x30/*_WPA2_IE_ID_*/) {
+ if (rsn_ie)
+ memcpy(rsn_ie, &in_ie[cnt], in_ie[cnt + 1] + 2);
+
+ *rsn_len = in_ie[cnt + 1] + 2;
+ cnt += in_ie[cnt + 1] + 2; /* get next */
+ } else {
+ cnt += in_ie[cnt + 1] + 2; /* get next */
+ }
+ }
+
+ }
+
+
+ return *rsn_len + *wpa_len;
+
+}
+
+
+static inline char *aicwf_get_iwe_stream_wpa_wpa2(struct rwnx_hw* rwnx_hw,
+ struct iw_request_info *info, struct scanu_result_wext *scan_re,
+ char *start, char *stop, struct iw_event *iwe)
+{
+ struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt*)scan_re->payload;
+ u8 *payload = mgmt->u.beacon.variable;
+ int buf_size = MAX_WPA_IE_LEN * 2;
+ u8 *pbuf;
+
+ u8 wpa_ie[255] = {0}, rsn_ie[255] = {0};
+ u16 i, wpa_len = 0, rsn_len = 0;
+ u8 *p;
+ int out_len = 0;
+
+ pbuf = (u8*)vmalloc(sizeof(u8) * buf_size);
+
+ if (pbuf) {
+ p = pbuf;
+
+ /* parsing WPA/WPA2 IE */
+ out_len = aic_get_sec_ie(payload , scan_re->ind->length, rsn_ie, &rsn_len, wpa_ie, &wpa_len);
+
+ if (wpa_len > 0) {
+
+ memset(pbuf, 0, buf_size);
+ p += sprintf(p, "wpa_ie=");
+ for (i = 0; i < wpa_len; i++)
+ p += sprintf(p, "%02x", wpa_ie[i]);
+
+ if (wpa_len > 100) {
+ printk("-----------------Len %d----------------\n", wpa_len);
+ for (i = 0; i < wpa_len; i++)
+ printk("%02x ", wpa_ie[i]);
+ printk("\n");
+ printk("-----------------Len %d----------------\n", wpa_len);
+ }
+
+ memset(iwe, 0, sizeof(*iwe));
+ iwe->cmd = IWEVCUSTOM;
+ iwe->u.data.length = strlen(pbuf);
+ start = iwe_stream_add_point(info, start, stop, iwe, pbuf);
+
+ memset(iwe, 0, sizeof(*iwe));
+ iwe->cmd = IWEVGENIE;
+ iwe->u.data.length = wpa_len;
+ start = iwe_stream_add_point(info, start, stop, iwe, wpa_ie);
+ }
+ if (rsn_len > 0) {
+
+ memset(pbuf, 0, buf_size);
+ p += sprintf(p, "rsn_ie=");
+ for (i = 0; i < rsn_len; i++)
+ p += sprintf(p, "%02x", rsn_ie[i]);
+ memset(iwe, 0, sizeof(*iwe));
+ iwe->cmd = IWEVCUSTOM;
+ iwe->u.data.length = strlen(pbuf);
+ start = iwe_stream_add_point(info, start, stop, iwe, pbuf);
+
+ memset(iwe, 0, sizeof(*iwe));
+ iwe->cmd = IWEVGENIE;
+ iwe->u.data.length = rsn_len;
+ start = iwe_stream_add_point(info, start, stop, iwe, rsn_ie);
+ }
+
+ vfree(pbuf);
+ }
+ return start;
+}
+
+
+u8 aicwf_get_is_wps_ie(u8 *ie_ptr, uint *wps_ielen)
+{
+ u8 match = false;
+ u8 eid, wps_oui[4] = {0x0, 0x50, 0xf2, 0x04};
+
+ if (ie_ptr == NULL)
+ return match;
+
+ eid = ie_ptr[0];
+
+ if ((eid == 0xdd/*_WPA_IE_ID_*/) && (memcmp(&ie_ptr[2], wps_oui, 4) == 0)) {
+ *wps_ielen = ie_ptr[1] + 2;
+ match = true;
+ }
+ return match;
+}
+
+
+static inline char *aicwf_get_iwe_stream_wps(struct rwnx_hw* rwnx_hw,
+ struct iw_request_info *info, struct scanu_result_wext *scan_re,
+ char *start, char *stop, struct iw_event *iwe)
+{
+
+ struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt*)scan_re->payload;
+ u8 *payload = mgmt->u.beacon.variable;
+
+ /* parsing WPS IE */
+ uint cnt = 0, total_ielen;
+ u8 *wpsie_ptr = NULL;
+ uint wps_ielen = 0;
+ u8 *ie_ptr = payload;
+
+ total_ielen = scan_re->ind->length;
+
+ while (cnt < total_ielen) {
+ if (aicwf_get_is_wps_ie(&ie_ptr[cnt], &wps_ielen) && (wps_ielen > 2)) {
+ wpsie_ptr = &ie_ptr[cnt];
+ iwe->cmd = IWEVGENIE;
+ iwe->u.data.length = (u16)wps_ielen;
+ start = iwe_stream_add_point(info, start, stop, iwe, wpsie_ptr);
+ }
+ cnt += ie_ptr[cnt + 1] + 2; /* goto next */
+ }
+ return start;
+}
+
+
+static char *translate_scan(struct rwnx_hw* rwnx_hw,
+ struct iw_request_info *info, struct scanu_result_wext *scan_re,
+ char *start, char *stop)
+{
+ struct iw_event iwe;
+ memset(&iwe, 0, sizeof(iwe));
+
+
+ start = aicwf_get_iwe_stream_mac_addr(rwnx_hw, info, scan_re, start, stop, &iwe);
+ start = aicwf_get_iwe_stream_essid(rwnx_hw, info, scan_re, start, stop, &iwe);
+ start = aicwf_get_iwe_stream_protocol(rwnx_hw, info, scan_re, start, stop, &iwe);
+ start = aicwf_get_iwe_stream_chan(rwnx_hw, info, scan_re, start, stop, &iwe);
+ start = aicwf_get_iwe_stream_mode(rwnx_hw, info, scan_re, start, stop, &iwe);
+ start = aicwf_get_iwe_stream_encryption(rwnx_hw, info, scan_re, start, stop, &iwe);
+ start = aicwf_get_iwe_stream_rate(rwnx_hw, info, scan_re, start, stop, &iwe);
+ start = aicwf_get_iwe_stream_wpa_wpa2(rwnx_hw, info, scan_re, start, stop, &iwe);
+ start = aicwf_get_iwe_stream_wps(rwnx_hw, info, scan_re, start, stop, &iwe);
+ start = aicwf_get_iwe_stream_rssi(rwnx_hw, info, scan_re, start, stop, &iwe);
+
+ return start;
+}
+
+
+
+static int aicwf_get_wap(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+
+ struct rwnx_vif* rwnx_vif = netdev_priv(dev);
+
+
+ AICWFDBG(LOGTRACE, "%s Enter", __func__);
+
+ wrqu->ap_addr.sa_family = ARPHRD_ETHER;
+
+ memset(wrqu->ap_addr.sa_data, 0, ETH_ALEN);
+
+ if(rwnx_vif->sta.ap){
+ memcpy(wrqu->ap_addr.sa_data, rwnx_vif->sta.ap->mac_addr, ETH_ALEN);
+ }else{
+ memset(wrqu->ap_addr.sa_data, 0, ETH_ALEN);
+ }
+
+ return 0;
+
+}
+
+
+extern uint8_t scanning;
+static int aicwf_set_scan(struct net_device *dev, struct iw_request_info *a,
+ union iwreq_data *wrqu, char *extra)
+{
+ int ret = 0;
+ struct rwnx_vif* rwnx_vif = netdev_priv(dev);
+ struct rwnx_hw* rwnx_hw = rwnx_vif->rwnx_hw;
+ struct cfg80211_scan_request *request;
+ int index = 0;
+ struct wiphy *wiphy = priv_to_wiphy(rwnx_hw);
+ unsigned long wext_scan_timeout;
+
+ AICWFDBG(LOGTRACE, "%s Enter", __func__);
+
+ if(wiphy == NULL){
+ printk("aic_wiphy error \r\n");
+
+ }
+
+ if (rwnx_hw->wext_scan || scanning) {
+ AICWFDBG(LOGINFO, "is scanning, abort\n");
+ ret = rwnx_send_scanu_cancel_req(rwnx_hw, NULL);
+ if (ret)
+ return ret;
+ msleep(150);
+ }
+
+ rwnx_hw->wext_scan = 1;
+
+ request = (struct cfg80211_scan_request *)vmalloc(sizeof(struct cfg80211_scan_request));
+
+ request->n_channels = rwnx_hw->support_freqs_number;
+ request->n_ssids = 0;
+ request->no_cck = false;
+ request->ie = NULL;
+ request->ie_len = 0;
+
+ for(index = 0;index < rwnx_hw->support_freqs_number; index++){
+ request->channels[index] = ieee80211_get_channel(wiphy,
+ rwnx_hw->support_freqs[index]);
+ if(request->channels[index] == NULL){
+ AICWFDBG(LOGERROR, "%s ERROR!!! channels is NULL", __func__);
+ continue;
+ }
+ }
+
+ if ((ret = rwnx_send_scanu_req(rwnx_hw, rwnx_vif, request))){
+ return ret;
+ }
+
+ rwnx_vif->rwnx_hw->scan_request = request;
+ wext_scan_timeout = msecs_to_jiffies(5000);
+
+ if (!wait_for_completion_killable_timeout(&rwnx_hw->wext_scan_com, wext_scan_timeout)) {
+ AICWFDBG(LOGERROR, "%s WEXT scan timeout", __func__);
+ }
+
+ return 0;
+}
+
+static int aicwf_get_scan(struct net_device *dev, struct iw_request_info *a,
+ union iwreq_data *wrqu, char *extra)
+{
+ int ret = 0;
+ struct rwnx_vif* rwnx_vif = netdev_priv(dev);
+ struct rwnx_hw* rwnx_hw = rwnx_vif->rwnx_hw;
+ struct scanu_result_wext *scan_re;
+ struct scanu_result_wext *tmp;
+ char *start = extra;
+ char *stop = start + wrqu->data.length;
+
+ AICWFDBG(LOGDEBUG, "%s Enter %p %p len:%d \r\n", __func__, start, stop, wrqu->data.length);
+
+ //TODO: spinlock
+ list_for_each_entry_safe(scan_re, tmp, &rwnx_hw->wext_scanre_list, scanu_re_list) {
+ start = translate_scan(rwnx_hw, a, scan_re, start, stop);
+ if ((stop - start) < 768) {
+ return -E2BIG;
+ }
+
+ }
+
+ list_for_each_entry_safe(scan_re, tmp, &rwnx_hw->wext_scanre_list, scanu_re_list) {
+ list_del(&scan_re->scanu_re_list);
+ vfree(scan_re->payload);
+ vfree(scan_re->ind);
+ vfree(scan_re);
+ scan_re = NULL;
+ }
+
+
+ wrqu->data.length = start - extra;
+ wrqu->data.flags = 0;
+
+ return ret ;
+
+}
+
+
+static int aicwf_get_essid(struct net_device *dev,
+ struct iw_request_info *a,
+ union iwreq_data *wrqu, char *extra)
+{
+ int ret = 0;
+ struct rwnx_vif* rwnx_vif = netdev_priv(dev);
+ struct rwnx_hw* rwnx_hw = rwnx_vif->rwnx_hw;
+
+ AICWFDBG(LOGTRACE, "%s Enter", __func__);
+
+ wrqu->essid.length = strlen(rwnx_hw->wext_essid);
+ memcpy(extra, rwnx_hw->wext_essid, strlen(rwnx_hw->wext_essid));
+ wrqu->essid.flags = 1;
+
+
+ return ret;
+
+}
+
+
+static int aicwf_get_nick(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+
+ AICWFDBG(LOGTRACE, "%s Enter", __func__);
+
+ if (extra) {
+ wrqu->data.length = 8;
+ wrqu->data.flags = 1;
+ memcpy(extra, "AIC@8800", 8);
+ }
+
+ return 0;
+
+}
+
+static int aicwf_get_rate(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ u16 max_rate = 0;
+ struct rwnx_vif* rwnx_vif = netdev_priv(dev);
+ struct rwnx_hw* rwnx_hw = rwnx_vif->rwnx_hw;
+ struct rwnx_sta *sta = NULL;
+
+ union rwnx_rate_ctrl_info *rate_info;
+ struct mm_get_sta_info_cfm cfm;
+
+ AICWFDBG(LOGTRACE, "%s Enter", __func__);
+
+ if(rwnx_vif->sta.ap){
+ sta = rwnx_vif->sta.ap;
+ rwnx_send_get_sta_info_req(rwnx_hw, sta->sta_idx, &cfm);
+ rate_info = (union rwnx_rate_ctrl_info *)&cfm.rate_info;
+
+ switch (rate_info->formatModTx) {
+ case FORMATMOD_NON_HT:
+ case FORMATMOD_NON_HT_DUP_OFDM:
+ //get bg mode datarate
+ max_rate = tx_legrates_lut_rate[rate_info->mcsIndexTx]/10;
+ break;
+ case FORMATMOD_HT_MF:
+ case FORMATMOD_HT_GF:
+ case FORMATMOD_VHT:
+ //get bg mode MCS index
+ max_rate = VHT_MCS_DATA_RATE[rate_info->bwTx][1][rate_info->mcsIndexTx]/2;
+ break;
+ case FORMATMOD_HE_MU:
+ case FORMATMOD_HE_SU:
+ case FORMATMOD_HE_ER:
+ //get bg mode MCS index
+ max_rate = HE_MCS_DATA_RATE[rate_info->bwTx][rate_info->mcsIndexTx];
+ break;
+ }
+ }
+
+ wrqu->bitrate.fixed = 0; /* no auto select */
+ wrqu->bitrate.value = max_rate * 1000000;
+
+ return 0;
+}
+
+
+static int aicwf_get_enc(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *keybuf)
+{
+ int ret = 0;
+ struct iw_point *erq = &(wrqu->encoding);
+
+ AICWFDBG(LOGTRACE, "%s Enter", __func__);
+
+ erq->length = 0;
+ erq->flags |= IW_ENCODE_ENABLED;
+
+
+ return ret;
+
+}
+
+
+static iw_handler aic_handlers[] = {
+ NULL, /* SIOCSIWCOMMIT */
+ aicwf_get_name, /* SIOCGIWNAME */
+ NULL, /* SIOCSIWNWID */
+ NULL, /* SIOCGIWNWID */
+ NULL, /* SIOCSIWFREQ */
+ aicwf_get_freq, /* SIOCGIWFREQ */
+ NULL, /* SIOCSIWMODE */
+ aicwf_get_mode, /* SIOCGIWMODE */
+ NULL, /* SIOCSIWSENS */
+ NULL, /* SIOCGIWSENS */
+ NULL, /* SIOCSIWRANGE */
+ aicwf_get_range, /* SIOCGIWRANGE */
+ NULL, /* SIOCSIWPRIV */
+ NULL, /* SIOCGIWPRIV */
+ NULL, /* SIOCSIWSTATS */
+ NULL, /* SIOCGIWSTATS */
+ NULL, /* SIOCSIWSPY */
+ NULL, /* SIOCGIWSPY */
+ NULL, /* SIOCGIWTHRSPY */
+ NULL, /* SIOCWIWTHRSPY */
+ NULL, /* SIOCSIWAP */
+ aicwf_get_wap, /* SIOCGIWAP */
+ NULL, /* request MLME operation; uses struct iw_mlme */
+ NULL, /* SIOCGIWAPLIST -- depricated */
+ aicwf_set_scan, /* SIOCSIWSCAN */
+ aicwf_get_scan, /* SIOCGIWSCAN */
+ NULL, /* SIOCSIWESSID */
+ aicwf_get_essid, /* SIOCGIWESSID */
+ NULL, /* SIOCSIWNICKN */
+ aicwf_get_nick, /* SIOCGIWNICKN */
+ NULL, /* -- hole -- */
+ NULL, /* -- hole -- */
+ NULL, /* SIOCSIWRATE */
+ aicwf_get_rate, /* SIOCGIWRATE */
+ NULL, /* SIOCSIWRTS */
+ NULL, /* SIOCGIWRTS */
+ NULL, /* SIOCSIWFRAG */
+ NULL, /* SIOCGIWFRAG */
+ NULL, /* SIOCSIWTXPOW */
+ NULL, /* SIOCGIWTXPOW */
+ NULL, /* SIOCSIWRETRY */
+ NULL, /* SIOCGIWRETRY */
+ NULL, /* SIOCSIWENCODE */
+ aicwf_get_enc, /* SIOCGIWENCODE */
+ NULL, /* SIOCSIWPOWER */
+ NULL, /* SIOCGIWPOWER */
+ NULL, /*---hole---*/
+ NULL, /*---hole---*/
+ NULL, /* SIOCSIWGENIE */
+ NULL, /* SIOCGWGENIE */
+ NULL, /* SIOCSIWAUTH */
+ NULL, /* SIOCGIWAUTH */
+ NULL, /* SIOCSIWENCODEEXT */
+ NULL, /* SIOCGIWENCODEEXT */
+ NULL, /* SIOCSIWPMKSA */
+ NULL, /*---hole---*/
+};
+
+struct iw_handler_def aic_handlers_def = {
+ .standard = aic_handlers,
+ .num_standard = sizeof(aic_handlers) / sizeof(iw_handler),
+#if WIRELESS_EXT >= 17
+ .get_wireless_stats = aicwf_get_wireless_stats,
+#endif
+};
+
+void aicwf_set_wireless_ext( struct net_device *ndev, struct rwnx_hw *rwnx_hw){
+
+ AICWFDBG(LOGINFO, "%s Enter", __func__);
+
+ init_completion(&rwnx_hw->wext_scan_com);
+ INIT_LIST_HEAD(&rwnx_hw->wext_scanre_list);
+
+ ndev->wireless_handlers = (struct iw_handler_def *)&aic_handlers_def;
+}
+
+
diff --git a/drivers/net/wireless/aic8800/aic8800_fdrv/aicwf_wext_linux.h b/drivers/net/wireless/aic8800/aic8800_fdrv/aicwf_wext_linux.h
new file mode 100644
index 000000000000..4a9c4ec16629
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic8800_fdrv/aicwf_wext_linux.h
@@ -0,0 +1,11 @@
+
+struct scanu_result_wext{
+ struct list_head scanu_re_list;
+ struct cfg80211_bss *bss;
+ struct scanu_result_ind *ind;
+ u32_l *payload;
+};
+
+void aicwf_set_wireless_ext( struct net_device *ndev, struct rwnx_hw *rwnx_hw);
+void aicwf_scan_complete_event(struct net_device *dev);
+
diff --git a/drivers/net/wireless/aic8800/aic8800_fdrv/hal_desc.h b/drivers/net/wireless/aic8800/aic8800_fdrv/hal_desc.h
new file mode 100644
index 000000000000..e0a13a8bfbd3
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic8800_fdrv/hal_desc.h
@@ -0,0 +1,376 @@
+/**
+ ******************************************************************************
+ *
+ * @file hal_desc.h
+ *
+ * @brief File containing the definition of HW descriptors.
+ *
+ * Contains the definition and structures used by HW
+ *
+ * Copyright (C) RivieraWaves 2011-2019
+ *
+ ******************************************************************************
+ */
+
+#ifndef _HAL_DESC_H_
+#define _HAL_DESC_H_
+
+#include "lmac_types.h"
+
+/* Rate and policy table */
+
+#define N_CCK 8
+#define N_OFDM 8
+#define N_HT (8 * 2 * 2 * 4)
+#define N_VHT (10 * 4 * 2 * 8)
+#define N_HE_SU (12 * 4 * 3 * 8)
+#define N_HE_MU (12 * 6 * 3 * 8)
+
+/* conversion table from NL80211 to MACHW enum */
+extern const int chnl2bw[];
+
+/* conversion table from MACHW to NL80211 enum */
+extern const int bw2chnl[];
+
+/* Rate cntrl info */
+#define MCS_INDEX_TX_RCX_OFT 0
+#define MCS_INDEX_TX_RCX_MASK (0x7F << MCS_INDEX_TX_RCX_OFT)
+#define BW_TX_RCX_OFT 7
+#define BW_TX_RCX_MASK (0x3 << BW_TX_RCX_OFT)
+#define SHORT_GI_TX_RCX_OFT 9
+#define SHORT_GI_TX_RCX_MASK (0x1 << SHORT_GI_TX_RCX_OFT)
+#define PRE_TYPE_TX_RCX_OFT 10
+#define PRE_TYPE_TX_RCX_MASK (0x1 << PRE_TYPE_TX_RCX_OFT)
+#define FORMAT_MOD_TX_RCX_OFT 11
+#define FORMAT_MOD_TX_RCX_MASK (0x7 << FORMAT_MOD_TX_RCX_OFT)
+
+/* Values for formatModTx */
+#define FORMATMOD_NON_HT 0
+#define FORMATMOD_NON_HT_DUP_OFDM 1
+#define FORMATMOD_HT_MF 2
+#define FORMATMOD_HT_GF 3
+#define FORMATMOD_VHT 4
+#define FORMATMOD_HE_SU 5
+#define FORMATMOD_HE_MU 6
+#define FORMATMOD_HE_ER 7
+
+/* Values for navProtFrmEx */
+#define NAV_PROT_NO_PROT_BIT 0
+#define NAV_PROT_SELF_CTS_BIT 1
+#define NAV_PROT_RTS_CTS_BIT 2
+#define NAV_PROT_RTS_CTS_WITH_QAP_BIT 3
+#define NAV_PROT_STBC_BIT 4
+
+/* THD MACCTRLINFO2 fields, used in struct umacdesc umac.flags */
+/// WhichDescriptor definition - contains aMPDU bit and position value
+/// Offset of WhichDescriptor field in the MAC CONTROL INFO 2 word
+#define WHICHDESC_OFT 19
+/// Mask of the WhichDescriptor field
+#define WHICHDESC_MSK (0x07 << WHICHDESC_OFT)
+/// Only 1 THD possible, describing an unfragmented MSDU
+#define WHICHDESC_UNFRAGMENTED_MSDU (0x00 << WHICHDESC_OFT)
+/// THD describing the first MPDU of a fragmented MSDU
+#define WHICHDESC_FRAGMENTED_MSDU_FIRST (0x01 << WHICHDESC_OFT)
+/// THD describing intermediate MPDUs of a fragmented MSDU
+#define WHICHDESC_FRAGMENTED_MSDU_INT (0x02 << WHICHDESC_OFT)
+/// THD describing the last MPDU of a fragmented MSDU
+#define WHICHDESC_FRAGMENTED_MSDU_LAST (0x03 << WHICHDESC_OFT)
+/// THD for extra descriptor starting an AMPDU
+#define WHICHDESC_AMPDU_EXTRA (0x04 << WHICHDESC_OFT)
+/// THD describing the first MPDU of an A-MPDU
+#define WHICHDESC_AMPDU_FIRST (0x05 << WHICHDESC_OFT)
+/// THD describing intermediate MPDUs of an A-MPDU
+#define WHICHDESC_AMPDU_INT (0x06 << WHICHDESC_OFT)
+/// THD describing the last MPDU of an A-MPDU
+#define WHICHDESC_AMPDU_LAST (0x07 << WHICHDESC_OFT)
+
+/// aMPDU bit offset
+#define AMPDU_OFT 21
+/// aMPDU bit
+#define AMPDU_BIT CO_BIT(AMPDU_OFT)
+
+enum {
+ HW_RATE_1MBPS = 0,
+ HW_RATE_2MBPS = 1,
+ HW_RATE_5_5MBPS = 2,
+ HW_RATE_11MBPS = 3,
+ HW_RATE_6MBPS = 4,
+ HW_RATE_9MBPS = 5,
+ HW_RATE_12MBPS = 6,
+ HW_RATE_18MBPS = 7,
+ HW_RATE_24MBPS = 8,
+ HW_RATE_36MBPS = 9,
+ HW_RATE_48MBPS = 10,
+ HW_RATE_54MBPS = 11,
+ HW_RATE_MAX
+};
+
+union rwnx_mcs_index {
+ struct {
+ u32 mcs : 3;
+ u32 nss : 2;
+ } ht;
+ struct {
+ u32 mcs : 4;
+ u32 nss : 3;
+ } vht;
+ struct {
+ u32 mcs : 4;
+ u32 nss : 3;
+ } he;
+ u32 legacy : 7;
+};
+
+/* c.f RW-WLAN-nX-MAC-HW-UM */
+union rwnx_rate_ctrl_info {
+ struct {
+ u32 mcsIndexTx : 7;
+ u32 bwTx : 2;
+ u32 giAndPreTypeTx : 2;
+ u32 formatModTx : 3;
+ u32 navProtFrmEx : 3;
+ u32 mcsIndexProtTx : 7;
+ u32 bwProtTx : 2;
+ u32 formatModProtTx : 3;
+ u32 nRetry : 3;
+ };
+ u32 value;
+};
+
+/* c.f RW-WLAN-nX-MAC-HW-UM */
+struct rwnx_power_ctrl_info {
+ u32 txPwrLevelPT : 8;
+ u32 txPwrLevelProtPT : 8;
+ u32 reserved :16;
+};
+
+/* c.f RW-WLAN-nX-MAC-HW-UM */
+union rwnx_pol_phy_ctrl_info_1 {
+ struct {
+ u32 rsvd1 : 3;
+ u32 bfFrmEx : 1;
+ u32 numExtnSS : 2;
+ u32 fecCoding : 1;
+ u32 stbc : 2;
+ u32 rsvd2 : 5;
+ u32 nTx : 3;
+ u32 nTxProt : 3;
+ };
+ u32 value;
+};
+
+/* c.f RW-WLAN-nX-MAC-HW-UM */
+union rwnx_pol_phy_ctrl_info_2 {
+ struct {
+ u32 antennaSet : 8;
+ u32 smmIndex : 8;
+ u32 beamFormed : 1;
+ };
+ u32 value;
+};
+
+/* c.f RW-WLAN-nX-MAC-HW-UM */
+union rwnx_pol_mac_ctrl_info_1 {
+ struct {
+ u32 keySRamIndex : 10;
+ u32 keySRamIndexRA : 10;
+ };
+ u32 value;
+};
+
+/* c.f RW-WLAN-nX-MAC-HW-UM */
+union rwnx_pol_mac_ctrl_info_2 {
+ struct {
+ u32 longRetryLimit : 8;
+ u32 shortRetryLimit : 8;
+ u32 rtsThreshold : 12;
+ };
+ u32 value;
+};
+
+#define POLICY_TABLE_PATTERN 0xBADCAB1E
+
+struct tx_policy_tbl {
+ /* Unique Pattern at the start of Policy Table */
+ u32 upatterntx;
+ /* PHY Control 1 Information used by MAC HW */
+ union rwnx_pol_phy_ctrl_info_1 phyctrlinfo_1;
+ /* PHY Control 2 Information used by MAC HW */
+ union rwnx_pol_phy_ctrl_info_2 phyctrlinfo_2;
+ /* MAC Control 1 Information used by MAC HW */
+ union rwnx_pol_mac_ctrl_info_1 macctrlinfo_1;
+ /* MAC Control 2 Information used by MAC HW */
+ union rwnx_pol_mac_ctrl_info_2 macctrlinfo_2;
+
+ union rwnx_rate_ctrl_info ratectrlinfos[NX_TX_MAX_RATES];
+ struct rwnx_power_ctrl_info powerctrlinfos[NX_TX_MAX_RATES];
+};
+
+#ifdef CONFIG_RWNX_FULLMAC
+
+/**
+ * struct rwnx_hw_txstatus - Bitfield of confirmation status
+ *
+ * @tx_done: packet has been processed by the firmware.
+ * @retry_required: packet has been transmitted but not acknoledged.
+ * Driver must repush it.
+ * @sw_retry_required: packet has not been transmitted (FW wasn't able to push
+ * it when it received it: not active channel ...). Driver must repush it.
+ * @acknowledged: packet has been acknowledged by peer
+ */
+union rwnx_hw_txstatus {
+ struct {
+ u32 tx_done : 1;
+ u32 retry_required : 1;
+ u32 sw_retry_required : 1;
+ u32 acknowledged : 1;
+ u32 reserved :28;
+ };
+ u32 value;
+};
+
+/**
+ * struct tx_cfm_tag - Structure indicating the status and other
+ * information about the transmission
+ *
+ * @pn: PN that was used for the transmission
+ * @sn: Sequence number of the packet
+ * @timestamp: Timestamp of first transmission of this MPDU
+ * @credits: Number of credits to be reallocated for the txq that push this
+ * buffer (can be 0 or 1)
+ * @ampdu_size: Size of the ampdu in which the frame has been transmitted if
+ * this was the last frame of the a-mpdu, and 0 if the frame is not the last
+ * frame on a a-mdpu.
+ * 1 means that the frame has been transmitted as a singleton.
+ * @amsdu_size: Size, in bytes, allowed to create a-msdu.
+ * @status: transmission status
+ */
+struct tx_cfm_tag
+{
+ u16_l pn[4];
+ u16_l sn;
+ u16_l timestamp;
+ s8_l credits;
+ u8_l ampdu_size;
+#ifdef CONFIG_RWNX_SPLIT_TX_BUF
+ u16_l amsdu_size;
+#endif
+ union rwnx_hw_txstatus status;
+};
+
+/**
+ * struct rwnx_hw_txhdr - Hardware part of tx header
+ *
+ * @cfm: Information updated by fw/hardware after sending a frame
+ */
+struct rwnx_hw_txhdr {
+ struct tx_cfm_tag cfm;
+};
+
+#endif /* CONFIG_RWNX_FULLMAC */
+
+/* Modem */
+
+#define MDM_PHY_CONFIG_TRIDENT 0
+#define MDM_PHY_CONFIG_ELMA 1
+#define MDM_PHY_CONFIG_KARST 2
+
+// MODEM features (from reg_mdm_stat.h)
+/// MUMIMOTX field bit
+#define MDM_MUMIMOTX_BIT ((u32)0x80000000)
+/// MUMIMOTX field position
+#define MDM_MUMIMOTX_POS 31
+/// MUMIMORX field bit
+#define MDM_MUMIMORX_BIT ((u32)0x40000000)
+/// MUMIMORX field position
+#define MDM_MUMIMORX_POS 30
+/// BFMER field bit
+#define MDM_BFMER_BIT ((u32)0x20000000)
+/// BFMER field position
+#define MDM_BFMER_POS 29
+/// BFMEE field bit
+#define MDM_BFMEE_BIT ((u32)0x10000000)
+/// BFMEE field position
+#define MDM_BFMEE_POS 28
+/// LDPCDEC field bit
+#define MDM_LDPCDEC_BIT ((u32)0x08000000)
+/// LDPCDEC field position
+#define MDM_LDPCDEC_POS 27
+/// LDPCENC field bit
+#define MDM_LDPCENC_BIT ((u32)0x04000000)
+/// LDPCENC field position
+#define MDM_LDPCENC_POS 26
+/// CHBW field mask
+#define MDM_CHBW_MASK ((u32)0x03000000)
+/// CHBW field LSB position
+#define MDM_CHBW_LSB 24
+/// CHBW field width
+#define MDM_CHBW_WIDTH ((u32)0x00000002)
+/// DSSSCCK field bit
+#define MDM_DSSSCCK_BIT ((u32)0x00800000)
+/// DSSSCCK field position
+#define MDM_DSSSCCK_POS 23
+/// VHT field bit
+#define MDM_VHT_BIT ((u32)0x00400000)
+/// VHT field position
+#define MDM_VHT_POS 22
+/// HE field bit
+#define MDM_HE_BIT ((u32)0x00200000)
+/// HE field position
+#define MDM_HE_POS 21
+/// ESS field bit
+#define MDM_ESS_BIT ((u32)0x00100000)
+/// ESS field position
+#define MDM_ESS_POS 20
+/// RFMODE field mask
+#define MDM_RFMODE_MASK ((u32)0x000F0000)
+/// RFMODE field LSB position
+#define MDM_RFMODE_LSB 16
+/// RFMODE field width
+#define MDM_RFMODE_WIDTH ((u32)0x00000004)
+/// NSTS field mask
+#define MDM_NSTS_MASK ((u32)0x0000F000)
+/// NSTS field LSB position
+#define MDM_NSTS_LSB 12
+/// NSTS field width
+#define MDM_NSTS_WIDTH ((u32)0x00000004)
+/// NSS field mask
+#define MDM_NSS_MASK ((u32)0x00000F00)
+/// NSS field LSB position
+#define MDM_NSS_LSB 8
+/// NSS field width
+#define MDM_NSS_WIDTH ((u32)0x00000004)
+/// NTX field mask
+#define MDM_NTX_MASK ((u32)0x000000F0)
+/// NTX field LSB position
+#define MDM_NTX_LSB 4
+/// NTX field width
+#define MDM_NTX_WIDTH ((u32)0x00000004)
+/// NRX field mask
+#define MDM_NRX_MASK ((u32)0x0000000F)
+/// NRX field LSB position
+#define MDM_NRX_LSB 0
+/// NRX field width
+#define MDM_NRX_WIDTH ((u32)0x00000004)
+
+#define __MDM_PHYCFG_FROM_VERS(v) (((v) & MDM_RFMODE_MASK) >> MDM_RFMODE_LSB)
+
+#define RIU_FCU_PRESENT_MASK ((u32)0xFF000000)
+#define RIU_FCU_PRESENT_LSB 24
+
+#define __RIU_FCU_PRESENT(v) (((v) & RIU_FCU_PRESENT_MASK) >> RIU_FCU_PRESENT_LSB == 5)
+
+/// AGC load version field mask
+#define RIU_AGC_LOAD_MASK ((u32)0x00C00000)
+/// AGC load version field LSB position
+#define RIU_AGC_LOAD_LSB 22
+
+#define __RIU_AGCLOAD_FROM_VERS(v) (((v) & RIU_AGC_LOAD_MASK) >> RIU_AGC_LOAD_LSB)
+
+#define __FPGA_TYPE(v) (((v) & 0xFFFF0000) >> 16)
+
+#define __MDM_MAJOR_VERSION(v) (((v) & 0xFF000000) >> 24)
+#define __MDM_MINOR_VERSION(v) (((v) & 0x00FF0000) >> 16)
+
+
+#endif // _HAL_DESC_H_
diff --git a/drivers/net/wireless/aic8800/aic8800_fdrv/ipc_compat.h b/drivers/net/wireless/aic8800/aic8800_fdrv/ipc_compat.h
new file mode 100644
index 000000000000..4601cfa846c6
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic8800_fdrv/ipc_compat.h
@@ -0,0 +1,25 @@
+/**
+ ****************************************************************************************
+ *
+ * @file ipc_compat.h
+ *
+ * Copyright (C) RivieraWaves 2011-2019
+ *
+ ****************************************************************************************
+ */
+
+#ifndef _IPC_H_
+#define _IPC_H_
+
+#define __INLINE static __attribute__((__always_inline__)) inline
+
+#define __ALIGN4 __aligned(4)
+
+#define ASSERT_ERR(condition) \
+ do { \
+ if (unlikely(!(condition))) { \
+ printk(KERN_ERR "%s:%d:ASSERT_ERR(" #condition ")\n", __FILE__, __LINE__); \
+ } \
+ } while(0)
+
+#endif /* _IPC_H_ */
diff --git a/drivers/net/wireless/aic8800/aic8800_fdrv/ipc_host.c b/drivers/net/wireless/aic8800/aic8800_fdrv/ipc_host.c
new file mode 100644
index 000000000000..7534be4c492f
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic8800_fdrv/ipc_host.c
@@ -0,0 +1,771 @@
+/**
+ ******************************************************************************
+ *
+ * @file ipc_host.c
+ *
+ * @brief IPC module.
+ *
+ * Copyright (C) RivieraWaves 2011-2019
+ *
+ ******************************************************************************
+ */
+
+/*
+ * INCLUDE FILES
+ ******************************************************************************
+ */
+#ifndef __KERNEL__
+#include <stdio.h>
+#define REG_SW_SET_PROFILING(env, value) do{ }while(0)
+#define REG_SW_CLEAR_PROFILING(env, value) do{ }while(0)
+#define REG_SW_CLEAR_HOSTBUF_IDX_PROFILING(env) do{ }while(0)
+#define REG_SW_SET_HOSTBUF_IDX_PROFILING(env, val) do{ }while(0)
+#else
+#include <linux/spinlock.h>
+#include "rwnx_defs.h"
+#include "rwnx_prof.h"
+#endif
+
+#include "reg_ipc_app.h"
+#include "ipc_host.h"
+
+/*
+ * TYPES DEFINITION
+ ******************************************************************************
+ */
+
+const int nx_txdesc_cnt[] =
+{
+ NX_TXDESC_CNT0,
+ NX_TXDESC_CNT1,
+ NX_TXDESC_CNT2,
+ NX_TXDESC_CNT3,
+ #if NX_TXQ_CNT == 5
+ NX_TXDESC_CNT4,
+ #endif
+};
+
+const int nx_txdesc_cnt_msk[] =
+{
+ NX_TXDESC_CNT0 - 1,
+ NX_TXDESC_CNT1 - 1,
+ NX_TXDESC_CNT2 - 1,
+ NX_TXDESC_CNT3 - 1,
+ #if NX_TXQ_CNT == 5
+ NX_TXDESC_CNT4 - 1,
+ #endif
+};
+
+const int nx_txuser_cnt[] =
+{
+ CONFIG_USER_MAX,
+ CONFIG_USER_MAX,
+ CONFIG_USER_MAX,
+ CONFIG_USER_MAX,
+ #if NX_TXQ_CNT == 5
+ 1,
+ #endif
+};
+
+
+/*
+ * FUNCTIONS DEFINITIONS
+ ******************************************************************************
+ */
+/**
+ * ipc_host_rxdesc_handler() - Handle the reception of a Rx Descriptor
+ *
+ * @env: pointer to the IPC Host environment
+ *
+ * Called from general IRQ handler when status %IPC_IRQ_E2A_RXDESC is set
+ */
+static void ipc_host_rxdesc_handler(struct ipc_host_env_tag *env)
+{
+ // For profiling
+ REG_SW_SET_PROFILING(env->pthis, SW_PROF_IRQ_E2A_RXDESC);
+
+ // LMAC has triggered an IT saying that a reception has occurred.
+ // Then we first need to check the validity of the current hostbuf, and the validity
+ // of the next hostbufs too, because it is likely that several hostbufs have been
+ // filled within the time needed for this irq handling
+ do {
+ #ifdef CONFIG_RWNX_FULLMAC
+ // call the external function to indicate that a RX descriptor is received
+ if (env->cb.recv_data_ind(env->pthis,
+ env->ipc_host_rxdesc_array[env->ipc_host_rxdesc_idx].hostid) != 0)
+ #else
+ // call the external function to indicate that a RX packet is received
+ if (env->cb.recv_data_ind(env->pthis,
+ env->ipc_host_rxbuf_array[env->ipc_host_rxbuf_idx].hostid) != 0)
+ #endif //(CONFIG_RWNX_FULLMAC)
+ break;
+
+ }while(1);
+
+ // For profiling
+ REG_SW_CLEAR_PROFILING(env->pthis, SW_PROF_IRQ_E2A_RXDESC);
+}
+
+/**
+ * ipc_host_radar_handler() - Handle the reception of radar events
+ *
+ * @env: pointer to the IPC Host environment
+ *
+ * Called from general IRQ handler when status %IPC_IRQ_E2A_RADAR is set
+ */
+static void ipc_host_radar_handler(struct ipc_host_env_tag *env)
+{
+#ifdef CONFIG_RWNX_RADAR
+ // LMAC has triggered an IT saying that a radar event has been sent to upper layer.
+ // Then we first need to check the validity of the current msg buf, and the validity
+ // of the next buffers too, because it is likely that several buffers have been
+ // filled within the time needed for this irq handling
+ // call the external function to indicate that a RX packet is received
+ spin_lock_bh(&((struct rwnx_hw *)env->pthis)->radar.lock);
+ while (env->cb.recv_radar_ind(env->pthis,
+ env->ipc_host_radarbuf_array[env->ipc_host_radarbuf_idx].hostid) == 0)
+ ;
+ spin_unlock_bh(&((struct rwnx_hw *)env->pthis)->radar.lock);
+#endif /* CONFIG_RWNX_RADAR */
+}
+
+/**
+ * ipc_host_unsup_rx_vec_handler() - Handle the reception of unsupported rx vector
+ *
+ * @env: pointer to the IPC Host environment
+ *
+ * Called from general IRQ handler when status %IPC_IRQ_E2A_UNSUP_RX_VEC is set
+ */
+static void ipc_host_unsup_rx_vec_handler(struct ipc_host_env_tag *env)
+{
+ while (env->cb.recv_unsup_rx_vec_ind(env->pthis,
+ env->ipc_host_unsuprxvecbuf_array[env->ipc_host_unsuprxvecbuf_idx].hostid) == 0)
+ ;
+}
+
+/**
+ * ipc_host_msg_handler() - Handler for firmware message
+ *
+ * @env: pointer to the IPC Host environment
+ *
+ * Called from general IRQ handler when status %IPC_IRQ_E2A_MSG is set
+ */
+static void ipc_host_msg_handler(struct ipc_host_env_tag *env)
+{
+ // For profiling
+ REG_SW_SET_PROFILING(env->pthis, SW_PROF_IRQ_E2A_MSG);
+
+ // LMAC has triggered an IT saying that a message has been sent to upper layer.
+ // Then we first need to check the validity of the current msg buf, and the validity
+ // of the next buffers too, because it is likely that several buffers have been
+ // filled within the time needed for this irq handling
+ // call the external function to indicate that a RX packet is received
+ while (env->cb.recv_msg_ind(env->pthis,
+ env->ipc_host_msgbuf_array[env->ipc_host_msge2a_idx].hostid) == 0)
+ ;
+
+
+ // For profiling
+ REG_SW_CLEAR_PROFILING(env->pthis, SW_PROF_IRQ_E2A_MSG);
+
+}
+
+/**
+ * ipc_host_msgack_handler() - Handle the reception of message acknowledgement
+ *
+ * @env: pointer to the IPC Host environment
+ *
+ * Called from general IRQ handler when status %IPC_IRQ_E2A_MSG_ACK is set
+ */
+static void ipc_host_msgack_handler(struct ipc_host_env_tag *env)
+{
+ void *hostid = env->msga2e_hostid;
+
+ ASSERT_ERR(hostid);
+ ASSERT_ERR(env->msga2e_cnt == (((struct lmac_msg *)(&env->shared->msg_a2e_buf.msg))->src_id & 0xFF));
+
+ env->msga2e_hostid = NULL;
+ env->msga2e_cnt++;
+ env->cb.recv_msgack_ind(env->pthis, hostid);
+}
+
+/**
+ * ipc_host_dbg_handler() - Handle the reception of Debug event
+ *
+ * @env: pointer to the IPC Host environment
+ *
+ * Called from general IRQ handler when status %IPC_IRQ_E2A_DBG is set
+ */
+static void ipc_host_dbg_handler(struct ipc_host_env_tag *env)
+{
+ // For profiling
+ REG_SW_SET_PROFILING(env->pthis, SW_PROF_IRQ_E2A_DBG);
+
+ // LMAC has triggered an IT saying that a DBG message has been sent to upper layer.
+ // Then we first need to check the validity of the current buffer, and the validity
+ // of the next buffers too, because it is likely that several buffers have been
+ // filled within the time needed for this irq handling
+ // call the external function to indicate that a RX packet is received
+ while(env->cb.recv_dbg_ind(env->pthis,
+ env->ipc_host_dbgbuf_array[env->ipc_host_dbg_idx].hostid) == 0)
+ ;
+
+ // For profiling
+ REG_SW_CLEAR_PROFILING(env->pthis, SW_PROF_IRQ_E2A_DBG);
+}
+
+/**
+ * ipc_host_tx_cfm_handler() - Handle the reception of TX confirmation
+ *
+ * @env: pointer to the IPC Host environment
+ * @queue_idx: index of the hardware on which the confirmation has been received
+ * @user_pos: index of the user position
+ *
+ * Called from general IRQ handler when status %IPC_IRQ_E2A_TXCFM is set
+ */
+static void ipc_host_tx_cfm_handler(struct ipc_host_env_tag *env,
+ const int queue_idx, const int user_pos)
+{
+ // TX confirmation descriptors have been received
+ REG_SW_SET_PROFILING(env->pthis, SW_PROF_IRQ_E2A_TXCFM);
+ while (1)
+ {
+ // Get the used index and increase it. We do the increase before knowing if the
+ // current buffer is confirmed because the callback function may call the
+ // ipc_host_txdesc_get() in case flow control was enabled and the index has to be
+ // already at the good value to ensure that the test of FIFO full is correct
+ uint32_t used_idx = env->txdesc_used_idx[queue_idx][user_pos]++;
+ uint32_t used_idx_mod = used_idx & nx_txdesc_cnt_msk[queue_idx];
+ void *host_id = env->tx_host_id[queue_idx][user_pos][used_idx_mod];
+
+ // Reset the host id in the array
+ env->tx_host_id[queue_idx][user_pos][used_idx_mod] = 0;
+
+ // call the external function to indicate that a TX packet is freed
+ if (host_id == 0)
+ {
+ // No more confirmations, so put back the used index at its initial value
+ env->txdesc_used_idx[queue_idx][user_pos] = used_idx;
+ break;
+ }
+
+ if (env->cb.send_data_cfm(env->pthis, host_id) != 0)
+ {
+ // No more confirmations, so put back the used index at its initial value
+ env->txdesc_used_idx[queue_idx][user_pos] = used_idx;
+ env->tx_host_id[queue_idx][user_pos][used_idx_mod] = host_id;
+ // and exit the loop
+ break;
+ }
+
+ REG_SW_SET_PROFILING_CHAN(env->pthis, SW_PROF_CHAN_CTXT_CFM_HDL_BIT);
+ REG_SW_CLEAR_PROFILING_CHAN(env->pthis, SW_PROF_CHAN_CTXT_CFM_HDL_BIT);
+ }
+
+ REG_SW_CLEAR_PROFILING(env->pthis, SW_PROF_IRQ_E2A_TXCFM);
+}
+
+/**
+ ******************************************************************************
+ */
+bool ipc_host_tx_frames_pending(struct ipc_host_env_tag *env)
+{
+ int i, j;
+ bool tx_frames_pending = false;
+
+ for (i = 0; (i < IPC_TXQUEUE_CNT) && !tx_frames_pending; i++)
+ {
+ for (j = 0; j < nx_txuser_cnt[i]; j++)
+ {
+ uint32_t used_idx = env->txdesc_used_idx[i][j];
+ uint32_t free_idx = env->txdesc_free_idx[i][j];
+
+ // Check if this queue is empty or not
+ if (used_idx != free_idx)
+ {
+ // The queue is not empty, update the flag and exit
+ tx_frames_pending = true;
+ break;
+ }
+ }
+ }
+
+ return (tx_frames_pending);
+}
+
+/**
+ ******************************************************************************
+ */
+void *ipc_host_tx_flush(struct ipc_host_env_tag *env, const int queue_idx, const int user_pos)
+{
+ uint32_t used_idx = env->txdesc_used_idx[queue_idx][user_pos];
+ void *host_id = env->tx_host_id[queue_idx][user_pos][used_idx & nx_txdesc_cnt_msk[queue_idx]];
+
+ // call the external function to indicate that a TX packet is freed
+ if (host_id != 0)
+ {
+ // Reset the host id in the array
+ env->tx_host_id[queue_idx][user_pos][used_idx & nx_txdesc_cnt_msk[queue_idx]] = 0;
+
+ // Increment the used index
+ env->txdesc_used_idx[queue_idx][user_pos]++;
+ }
+
+ return (host_id);
+}
+
+/**
+ ******************************************************************************
+ */
+void ipc_host_init(struct ipc_host_env_tag *env,
+ struct ipc_host_cb_tag *cb,
+ struct ipc_shared_env_tag *shared_env_ptr,
+ void *pthis)
+{
+ unsigned int i;
+ unsigned int size;
+ unsigned int * dst;
+
+ // Reset the environments
+ // Reset the IPC Shared memory
+#if 0
+ /* check potential platform bug on multiple stores */
+ memset(shared_env_ptr, 0, sizeof(struct ipc_shared_env_tag));
+#else
+ dst = (unsigned int *)shared_env_ptr;
+ size = (unsigned int)sizeof(struct ipc_shared_env_tag);
+ for (i=0; i < size; i+=4)
+ {
+ *dst++ = 0;
+ }
+#endif
+ // Reset the IPC Host environment
+ memset(env, 0, sizeof(struct ipc_host_env_tag));
+
+ // Initialize the shared environment pointer
+ env->shared = shared_env_ptr;
+
+ // Save the callbacks in our own environment
+ env->cb = *cb;
+
+ // Save the pointer to the register base
+ env->pthis = pthis;
+
+ // Initialize buffers numbers and buffers sizes needed for DMA Receptions
+ env->rx_bufnb = IPC_RXBUF_CNT;
+ #ifdef CONFIG_RWNX_FULLMAC
+ env->rxdesc_nb = IPC_RXDESC_CNT;
+ #endif //(CONFIG_RWNX_FULLMAC)
+ env->radar_bufnb = IPC_RADARBUF_CNT;
+ env->radar_bufsz = sizeof(struct radar_pulse_array_desc);
+ env->unsuprxvec_bufnb = IPC_UNSUPRXVECBUF_CNT;
+ env->unsuprxvec_bufsz = max(sizeof(struct rx_vector_desc), (size_t) RADIOTAP_HDR_MAX_LEN) +
+ RADIOTAP_HDR_VEND_MAX_LEN + UNSUP_RX_VEC_DATA_LEN;
+ env->ipc_e2amsg_bufnb = IPC_MSGE2A_BUF_CNT;
+ env->ipc_e2amsg_bufsz = sizeof(struct ipc_e2a_msg);
+ env->ipc_dbg_bufnb = IPC_DBGBUF_CNT;
+ env->ipc_dbg_bufsz = sizeof(struct ipc_dbg_msg);
+
+ for (i = 0; i < CONFIG_USER_MAX; i++)
+ {
+ // Initialize the pointers to the hostid arrays
+ env->tx_host_id[0][i] = env->tx_host_id0[i];
+ env->tx_host_id[1][i] = env->tx_host_id1[i];
+ env->tx_host_id[2][i] = env->tx_host_id2[i];
+ env->tx_host_id[3][i] = env->tx_host_id3[i];
+ #if NX_TXQ_CNT == 5
+ env->tx_host_id[4][i] = NULL;
+ #endif
+
+ // Initialize the pointers to the TX descriptor arrays
+ env->txdesc[0][i] = shared_env_ptr->txdesc0[i];
+ env->txdesc[1][i] = shared_env_ptr->txdesc1[i];
+ env->txdesc[2][i] = shared_env_ptr->txdesc2[i];
+ env->txdesc[3][i] = shared_env_ptr->txdesc3[i];
+ #if NX_TXQ_CNT == 5
+ env->txdesc[4][i] = NULL;
+ #endif
+ }
+
+ #if NX_TXQ_CNT == 5
+ env->tx_host_id[4][0] = env->tx_host_id4[0];
+ env->txdesc[4][0] = shared_env_ptr->txdesc4[0];
+ #endif
+}
+
+/**
+ ******************************************************************************
+ */
+void ipc_host_patt_addr_push(struct ipc_host_env_tag *env, uint32_t addr)
+{
+ struct ipc_shared_env_tag *shared_env_ptr = env->shared;
+
+ // Copy the address
+ shared_env_ptr->pattern_addr = addr;
+}
+
+/**
+ ******************************************************************************
+ */
+int ipc_host_rxbuf_push(struct ipc_host_env_tag *env,
+#ifdef CONFIG_RWNX_FULLMAC
+ uint32_t hostid,
+#endif
+ uint32_t hostbuf)
+{
+ struct ipc_shared_env_tag *shared_env_ptr = env->shared;
+
+ REG_SW_CLEAR_HOSTBUF_IDX_PROFILING(env->pthis);
+ REG_SW_SET_HOSTBUF_IDX_PROFILING(env->pthis, env->ipc_host_rxbuf_idx);
+
+#ifdef CONFIG_RWNX_FULLMAC
+ // Copy the hostbuf (DMA address) in the ipc shared memory
+ shared_env_ptr->host_rxbuf[env->ipc_host_rxbuf_idx].hostid = hostid;
+ shared_env_ptr->host_rxbuf[env->ipc_host_rxbuf_idx].dma_addr = hostbuf;
+#else
+ // Save the hostid and the hostbuf in global array
+ env->ipc_host_rxbuf_array[env->ipc_host_rxbuf_idx].hostid = hostid;
+ env->ipc_host_rxbuf_array[env->ipc_host_rxbuf_idx].dma_addr = hostbuf;
+
+ shared_env_ptr->host_rxbuf[env->ipc_host_rxbuf_idx] = hostbuf;
+#endif //(CONFIG_RWNX_FULLMAC)
+
+ // Signal to the embedded CPU that at least one buffer is available
+ ipc_app2emb_trigger_set(shared_env_ptr, IPC_IRQ_A2E_RXBUF_BACK);
+
+ // Increment the array index
+ env->ipc_host_rxbuf_idx = (env->ipc_host_rxbuf_idx +1)%IPC_RXBUF_CNT;
+
+ return (0);
+}
+
+#ifdef CONFIG_RWNX_FULLMAC
+/**
+ ******************************************************************************
+ */
+int ipc_host_rxdesc_push(struct ipc_host_env_tag *env, void *hostid,
+ uint32_t hostbuf)
+{
+ struct ipc_shared_env_tag *shared_env_ptr = env->shared;
+
+ // Reset the RX Descriptor DMA Address and increment the counter
+ env->ipc_host_rxdesc_array[env->ipc_host_rxdesc_idx].dma_addr = hostbuf;
+ env->ipc_host_rxdesc_array[env->ipc_host_rxdesc_idx].hostid = hostid;
+
+ shared_env_ptr->host_rxdesc[env->ipc_host_rxdesc_idx].dma_addr = hostbuf;
+
+ // Signal to the embedded CPU that at least one descriptor is available
+ ipc_app2emb_trigger_set(shared_env_ptr, IPC_IRQ_A2E_RXDESC_BACK);
+
+ env->ipc_host_rxdesc_idx = (env->ipc_host_rxdesc_idx + 1) % IPC_RXDESC_CNT;
+
+ return (0);
+}
+#endif /* CONFIG_RWNX_FULLMAC */
+
+/**
+ ******************************************************************************
+ */
+int ipc_host_radarbuf_push(struct ipc_host_env_tag *env, void *hostid,
+ uint32_t hostbuf)
+{
+ struct ipc_shared_env_tag *shared_env_ptr = env->shared;
+
+ // Save the hostid and the hostbuf in global array
+ env->ipc_host_radarbuf_array[env->ipc_host_radarbuf_idx].hostid = hostid;
+ env->ipc_host_radarbuf_array[env->ipc_host_radarbuf_idx].dma_addr = hostbuf;
+
+ // Copy the hostbuf (DMA address) in the ipc shared memory
+ shared_env_ptr->radarbuf_hostbuf[env->ipc_host_radarbuf_idx] = hostbuf;
+
+ // Increment the array index
+ env->ipc_host_radarbuf_idx = (env->ipc_host_radarbuf_idx +1)%IPC_RADARBUF_CNT;
+
+ return (0);
+}
+
+/**
+ ******************************************************************************
+ */
+
+int ipc_host_unsup_rx_vec_buf_push(struct ipc_host_env_tag *env,
+ void *hostid,
+ uint32_t hostbuf)
+{
+ struct ipc_shared_env_tag *shared_env_ptr = env->shared;
+
+ env->ipc_host_unsuprxvecbuf_array[env->ipc_host_unsuprxvecbuf_idx].hostid = hostid;
+ env->ipc_host_unsuprxvecbuf_array[env->ipc_host_unsuprxvecbuf_idx].dma_addr = hostbuf;
+
+ // Copy the hostbuf (DMA address) in the ipc shared memory
+ shared_env_ptr->unsuprxvecbuf_hostbuf[env->ipc_host_unsuprxvecbuf_idx] = hostbuf;
+
+ // Increment the array index
+ env->ipc_host_unsuprxvecbuf_idx = (env->ipc_host_unsuprxvecbuf_idx + 1)%IPC_UNSUPRXVECBUF_CNT;
+
+ return (0);
+}
+
+/**
+ ******************************************************************************
+ */
+int ipc_host_msgbuf_push(struct ipc_host_env_tag *env, void *hostid,
+ uint32_t hostbuf)
+{
+ struct ipc_shared_env_tag *shared_env_ptr = env->shared;
+
+ // Save the hostid and the hostbuf in global array
+ env->ipc_host_msgbuf_array[env->ipc_host_msge2a_idx].hostid = hostid;
+ env->ipc_host_msgbuf_array[env->ipc_host_msge2a_idx].dma_addr = hostbuf;
+
+ // Copy the hostbuf (DMA address) in the ipc shared memory
+ shared_env_ptr->msg_e2a_hostbuf_addr[env->ipc_host_msge2a_idx] = hostbuf;
+
+ // Increment the array index
+ env->ipc_host_msge2a_idx = (env->ipc_host_msge2a_idx +1)%IPC_MSGE2A_BUF_CNT;
+
+ return (0);
+}
+
+/**
+ ******************************************************************************
+ */
+int ipc_host_dbgbuf_push(struct ipc_host_env_tag *env, void *hostid,
+ uint32_t hostbuf)
+{
+ struct ipc_shared_env_tag *shared_env_ptr = env->shared;
+
+ // Save the hostid and the hostbuf in global array
+ env->ipc_host_dbgbuf_array[env->ipc_host_dbg_idx].hostid = hostid;
+ env->ipc_host_dbgbuf_array[env->ipc_host_dbg_idx].dma_addr = hostbuf;
+
+ // Copy the hostbuf (DMA address) in the ipc shared memory
+ shared_env_ptr->dbg_hostbuf_addr[env->ipc_host_dbg_idx] = hostbuf;
+
+ // Increment the array index
+ env->ipc_host_dbg_idx = (env->ipc_host_dbg_idx +1)%IPC_DBGBUF_CNT;
+
+ return (0);
+}
+
+/**
+ ******************************************************************************
+ */
+void ipc_host_dbginfobuf_push(struct ipc_host_env_tag *env, uint32_t infobuf)
+{
+ struct ipc_shared_env_tag *shared_env_ptr = env->shared;
+
+ // Copy the hostbuf (DMA address) in the ipc shared memory
+ shared_env_ptr->la_dbginfo_addr = infobuf;
+}
+
+/**
+ ******************************************************************************
+ */
+volatile struct txdesc_host *ipc_host_txdesc_get(struct ipc_host_env_tag *env, const int queue_idx, const int user_pos)
+{
+ volatile struct txdesc_host *txdesc_free;
+ uint32_t used_idx = env->txdesc_used_idx[queue_idx][user_pos];
+ uint32_t free_idx = env->txdesc_free_idx[queue_idx][user_pos];
+
+ ASSERT_ERR(queue_idx < IPC_TXQUEUE_CNT);
+ ASSERT_ERR((free_idx - used_idx) <= nx_txdesc_cnt[queue_idx]);
+
+ // Check if a free descriptor is available
+ if (free_idx != (used_idx + nx_txdesc_cnt[queue_idx]))
+ {
+ // Get the pointer to the first free descriptor
+ txdesc_free = env->txdesc[queue_idx][user_pos] + (free_idx & nx_txdesc_cnt_msk[queue_idx]);
+ }
+ else
+ {
+ txdesc_free = NULL;
+ }
+
+ return txdesc_free;
+}
+
+/**
+ ******************************************************************************
+ */
+void ipc_host_txdesc_push(struct ipc_host_env_tag *env, const int queue_idx,
+ const int user_pos, void *host_id)
+{
+ uint32_t free_idx = env->txdesc_free_idx[queue_idx][user_pos] & nx_txdesc_cnt_msk[queue_idx];
+ volatile struct txdesc_host *txdesc_pushed = env->txdesc[queue_idx][user_pos] + free_idx;
+
+
+ // Descriptor is now ready
+ txdesc_pushed->ready = 0xFFFFFFFF;
+
+ // Save the host id in the environment
+ env->tx_host_id[queue_idx][user_pos][free_idx] = host_id;
+
+ // Increment the index
+ env->txdesc_free_idx[queue_idx][user_pos]++;
+
+ // trigger interrupt!!!
+ //REG_SW_SET_PROFILING(env->pthis, CO_BIT(queue_idx+SW_PROF_IRQ_A2E_TXDESC_FIRSTBIT));
+ ipc_app2emb_trigger_setf(env->shared, CO_BIT(user_pos + queue_idx * CONFIG_USER_MAX +
+ IPC_IRQ_A2E_TXDESC_FIRSTBIT));
+}
+
+/**
+ ******************************************************************************
+ */
+void ipc_host_irq(struct ipc_host_env_tag *env, uint32_t status)
+{
+ // Acknowledge the pending interrupts
+ ipc_emb2app_ack_clear(env->shared, status);
+ // And re-read the status, just to be sure that the acknowledgment is
+ // effective when we start the interrupt handling
+ ipc_emb2app_status_get(env->shared);
+
+ // Optimized for only one IRQ at a time
+ if (status & IPC_IRQ_E2A_RXDESC)
+ {
+ // handle the RX descriptor reception
+ ipc_host_rxdesc_handler(env);
+ }
+ if (status & IPC_IRQ_E2A_MSG_ACK)
+ {
+ ipc_host_msgack_handler(env);
+ }
+ if (status & IPC_IRQ_E2A_MSG)
+ {
+ ipc_host_msg_handler(env);
+ }
+ if (status & IPC_IRQ_E2A_TXCFM)
+ {
+ int i;
+
+#ifdef __KERNEL__
+ spin_lock_bh(&((struct rwnx_hw *)env->pthis)->tx_lock);
+#endif
+ // handle the TX confirmation reception
+ for (i = 0; i < IPC_TXQUEUE_CNT; i++)
+ {
+ int j = 0;
+#ifdef CONFIG_RWNX_MUMIMO_TX
+ for (; j < nx_txuser_cnt[i]; j++)
+#endif
+ {
+ uint32_t q_bit = CO_BIT(j + i * CONFIG_USER_MAX + IPC_IRQ_E2A_TXCFM_POS);
+ if (status & q_bit)
+ {
+ // handle the confirmation
+ ipc_host_tx_cfm_handler(env, i, j);
+ }
+ }
+ }
+#ifdef __KERNEL__
+ spin_unlock_bh(&((struct rwnx_hw *)env->pthis)->tx_lock);
+#endif
+ }
+ if (status & IPC_IRQ_E2A_RADAR)
+ {
+ // handle the radar event reception
+ ipc_host_radar_handler(env);
+ }
+
+ if (status & IPC_IRQ_E2A_UNSUP_RX_VEC)
+ {
+ // handle the unsupported rx vector reception
+ ipc_host_unsup_rx_vec_handler(env);
+ }
+
+ if (status & IPC_IRQ_E2A_DBG)
+ {
+ ipc_host_dbg_handler(env);
+ }
+
+ if (status & IPC_IRQ_E2A_TBTT_PRIM)
+ {
+ env->cb.prim_tbtt_ind(env->pthis);
+ }
+
+ if (status & IPC_IRQ_E2A_TBTT_SEC)
+ {
+ env->cb.sec_tbtt_ind(env->pthis);
+ }
+}
+
+/**
+ ******************************************************************************
+ */
+int ipc_host_msg_push(struct ipc_host_env_tag *env, void *msg_buf, uint16_t len)
+{
+ int i;
+ uint32_t *src, *dst;
+
+ REG_SW_SET_PROFILING(env->pthis, SW_PROF_IPC_MSGPUSH);
+
+ ASSERT_ERR(!env->msga2e_hostid);
+ ASSERT_ERR(round_up(len, 4) <= sizeof(env->shared->msg_a2e_buf.msg));
+
+ // Copy the message into the IPC MSG buffer
+#ifdef __KERNEL__
+ src = (uint32_t*)((struct rwnx_cmd *)msg_buf)->a2e_msg;
+#else
+ src = (uint32_t*) msg_buf;
+#endif
+ dst = (uint32_t*)&(env->shared->msg_a2e_buf.msg);
+
+ // Copy the message in the IPC queue
+ for (i=0; i<len; i+=4)
+ {
+ *dst++ = *src++;
+ }
+
+ env->msga2e_hostid = msg_buf;
+
+ // Trigger the irq to send the message to EMB
+ ipc_app2emb_trigger_set(env->shared, IPC_IRQ_A2E_MSG);
+
+ REG_SW_CLEAR_PROFILING(env->pthis, SW_PROF_IPC_MSGPUSH);
+
+ return (0);
+}
+
+/**
+ ******************************************************************************
+ */
+void ipc_host_enable_irq(struct ipc_host_env_tag *env, uint32_t value)
+{
+ // Enable the handled interrupts
+ ipc_emb2app_unmask_set(env->shared, value);
+}
+
+/**
+ ******************************************************************************
+ */
+void ipc_host_disable_irq(struct ipc_host_env_tag *env, uint32_t value)
+{
+ // Enable the handled interrupts
+ ipc_emb2app_unmask_clear(env->shared, value);
+}
+
+/**
+ ******************************************************************************
+ */
+uint32_t ipc_host_get_status(struct ipc_host_env_tag *env)
+{
+ volatile uint32_t status;
+
+ status = ipc_emb2app_status_get(env->shared);
+
+ return status;
+}
+
+/**
+ ******************************************************************************
+ */
+uint32_t ipc_host_get_rawstatus(struct ipc_host_env_tag *env)
+{
+ volatile uint32_t rawstatus;
+
+ rawstatus = ipc_emb2app_rawstatus_get(env->shared);
+
+ return rawstatus;
+}
+
diff --git a/drivers/net/wireless/aic8800/aic8800_fdrv/ipc_host.h b/drivers/net/wireless/aic8800/aic8800_fdrv/ipc_host.h
new file mode 100644
index 000000000000..c008a316c010
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic8800_fdrv/ipc_host.h
@@ -0,0 +1,508 @@
+/**
+ ******************************************************************************
+ *
+ * @file ipc_host.h
+ *
+ * @brief IPC module.
+ *
+ * Copyright (C) RivieraWaves 2011-2019
+ *
+ ******************************************************************************
+ */
+#ifndef _IPC_HOST_H_
+#define _IPC_HOST_H_
+
+/*
+ * INCLUDE FILES
+ ******************************************************************************
+ */
+#include "ipc_shared.h"
+#ifndef __KERNEL__
+#include "arch.h"
+#else
+#include "ipc_compat.h"
+#endif
+
+/*
+ * ENUMERATION
+ ******************************************************************************
+ */
+
+enum ipc_host_desc_status
+{
+ /// Descriptor is IDLE
+ IPC_HOST_DESC_IDLE = 0,
+ /// Data can be forwarded
+ IPC_HOST_DESC_FORWARD,
+ /// Data has to be kept in UMAC memory
+ IPC_HOST_DESC_KEEP,
+ /// Delete stored packet
+ IPC_HOST_DESC_DELETE,
+ /// Update Frame Length status
+ IPC_HOST_DESC_LEN_UPDATE,
+};
+
+/**
+ ******************************************************************************
+ * @brief This structure is used to initialize the MAC SW
+ *
+ * The WLAN device driver provides functions call-back with this structure
+ ******************************************************************************
+ */
+struct ipc_host_cb_tag
+{
+ /// WLAN driver call-back function: send_data_cfm
+ int (*send_data_cfm)(void *pthis, void *host_id);
+
+ /// WLAN driver call-back function: recv_data_ind
+ uint8_t (*recv_data_ind)(void *pthis, void *host_id);
+
+ /// WLAN driver call-back function: recv_radar_ind
+ uint8_t (*recv_radar_ind)(void *pthis, void *host_id);
+
+ /// WLAN driver call-back function: recv_unsup_rx_vec_ind
+ uint8_t (*recv_unsup_rx_vec_ind)(void *pthis, void *host_id);
+
+ /// WLAN driver call-back function: recv_msg_ind
+ uint8_t (*recv_msg_ind)(void *pthis, void *host_id);
+
+ /// WLAN driver call-back function: recv_msgack_ind
+ uint8_t (*recv_msgack_ind)(void *pthis, void *host_id);
+
+ /// WLAN driver call-back function: recv_dbg_ind
+ uint8_t (*recv_dbg_ind)(void *pthis, void *host_id);
+
+ /// WLAN driver call-back function: prim_tbtt_ind
+ void (*prim_tbtt_ind)(void *pthis);
+
+ /// WLAN driver call-back function: sec_tbtt_ind
+ void (*sec_tbtt_ind)(void *pthis);
+
+};
+
+/*
+ * Struct used to store information about host buffers (DMA Address and local pointer)
+ */
+struct ipc_hostbuf
+{
+ void *hostid; ///< ptr to hostbuf client (ipc_host client) structure
+ uint32_t dma_addr; ///< ptr to real hostbuf dma address
+};
+
+/// Definition of the IPC Host environment structure.
+struct ipc_host_env_tag
+{
+ /// Structure containing the callback pointers
+ struct ipc_host_cb_tag cb;
+
+ /// Pointer to the shared environment
+ struct ipc_shared_env_tag *shared;
+
+ #ifdef CONFIG_RWNX_FULLMAC
+ // Array used to store the descriptor addresses
+ struct ipc_hostbuf ipc_host_rxdesc_array[IPC_RXDESC_CNT];
+ // Index of the host RX descriptor array (ipc_shared environment)
+ uint8_t ipc_host_rxdesc_idx;
+ /// Store the number of RX Descriptors
+ uint8_t rxdesc_nb;
+ #endif //(CONFIG_RWNX_FULLMAC)
+
+ /// Fields for Data Rx handling
+ // Index used for ipc_host_rxbuf_array to point to current buffer
+ uint8_t ipc_host_rxbuf_idx;
+ // Store the number of Rx Data buffers
+ uint32_t rx_bufnb;
+ // Store the size of the Rx Data buffers
+ uint32_t rx_bufsz;
+
+ /// Fields for Radar events handling
+ // Global array used to store the hostid and hostbuf addresses
+ struct ipc_hostbuf ipc_host_radarbuf_array[IPC_RADARBUF_CNT];
+ // Index used for ipc_host_rxbuf_array to point to current buffer
+ uint8_t ipc_host_radarbuf_idx;
+ // Store the number of radar event buffers
+ uint32_t radar_bufnb;
+ // Store the size of the radar event buffers
+ uint32_t radar_bufsz;
+
+ ///Fields for Unsupported frame handling
+ // Global array used to store the hostid and hostbuf addresses
+ struct ipc_hostbuf ipc_host_unsuprxvecbuf_array[IPC_UNSUPRXVECBUF_CNT];
+ // Index used for ipc_host_unsuprxvecbuf_array to point to current buffer
+ uint8_t ipc_host_unsuprxvecbuf_idx;
+ // Store the number of unsupported rx vector buffers
+ uint32_t unsuprxvec_bufnb;
+ // Store the size of unsupported rx vector buffers
+ uint32_t unsuprxvec_bufsz;
+
+ // Index used that points to the first free TX desc
+ uint32_t txdesc_free_idx[IPC_TXQUEUE_CNT][CONFIG_USER_MAX];
+ // Index used that points to the first used TX desc
+ uint32_t txdesc_used_idx[IPC_TXQUEUE_CNT][CONFIG_USER_MAX];
+ // Array storing the currently pushed host ids for the BK queue
+ void *tx_host_id0[CONFIG_USER_MAX][NX_TXDESC_CNT0];
+ // Array storing the currently pushed host ids for the BE queue
+ void *tx_host_id1[CONFIG_USER_MAX][NX_TXDESC_CNT1];
+ // Array storing the currently pushed host ids for the VI queue
+ void *tx_host_id2[CONFIG_USER_MAX][NX_TXDESC_CNT2];
+ // Array storing the currently pushed host ids for the VO queue
+ void *tx_host_id3[CONFIG_USER_MAX][NX_TXDESC_CNT3];
+ #if NX_TXQ_CNT == 5
+ // Array storing the currently pushed host ids for the BCN queue
+ void *tx_host_id4[1][NX_TXDESC_CNT4];
+ #endif
+ // Pointer to the different host ids arrays, per IPC queue
+ void **tx_host_id[IPC_TXQUEUE_CNT][CONFIG_USER_MAX];
+ // Pointer to the different TX descriptor arrays, per IPC queue
+ volatile struct txdesc_host *txdesc[IPC_TXQUEUE_CNT][CONFIG_USER_MAX];
+
+ /// Fields for Emb->App MSGs handling
+ // Global array used to store the hostid and hostbuf addresses for msg/ind
+ struct ipc_hostbuf ipc_host_msgbuf_array[IPC_MSGE2A_BUF_CNT];
+ // Index of the MSG E2A buffers array to point to current buffer
+ uint8_t ipc_host_msge2a_idx;
+ // Store the number of E2A MSG buffers
+ uint32_t ipc_e2amsg_bufnb;
+ // Store the size of the E2A MSG buffers
+ uint32_t ipc_e2amsg_bufsz;
+
+ /// E2A ACKs of A2E MSGs
+ uint8_t msga2e_cnt;
+ void *msga2e_hostid;
+
+ /// Fields for Debug MSGs handling
+ // Global array used to store the hostid and hostbuf addresses for Debug messages
+ struct ipc_hostbuf ipc_host_dbgbuf_array[IPC_DBGBUF_CNT];
+ // Index of the Debug messages buffers array to point to current buffer
+ uint8_t ipc_host_dbg_idx;
+ // Store the number of Debug messages buffers
+ uint32_t ipc_dbg_bufnb;
+ // Store the size of the Debug messages buffers
+ uint32_t ipc_dbg_bufsz;
+
+ /// Pointer to the attached object (used in callbacks and register accesses)
+ void *pthis;
+};
+
+extern const int nx_txdesc_cnt[];
+extern const int nx_txuser_cnt[];
+
+/**
+ ******************************************************************************
+ * @brief Returns the full/not full status of the queue the index of which is
+ * passed as parameter.
+ *
+ * @param[in] env Pointer to the IPC host environment
+ * @param[in] queue_idx Index of the queue to be checked
+ *
+ * @return true if the queue is full, false otherwise
+ *
+ ******************************************************************************
+ */
+__INLINE bool ipc_host_queue_full(struct ipc_host_env_tag *env,
+ const int queue_idx)
+{
+ return (env->txdesc_free_idx[queue_idx] ==
+ (env->txdesc_used_idx[queue_idx] + nx_txdesc_cnt[queue_idx]));
+}
+
+/**
+ ******************************************************************************
+ * @brief Initialize the IPC running on the Application CPU.
+ *
+ * This function:
+ * - initializes the IPC software environments
+ * - enables the interrupts in the IPC block
+ *
+ * @param[in] env Pointer to the IPC host environment
+ *
+ * @warning Since this function resets the IPC Shared memory, it must be called
+ * before the LMAC FW is launched because LMAC sets some init values in IPC
+ * Shared memory at boot.
+ *
+ ******************************************************************************
+ */
+void ipc_host_init(struct ipc_host_env_tag *env,
+ struct ipc_host_cb_tag *cb,
+ struct ipc_shared_env_tag *shared_env_ptr,
+ void *pthis);
+
+/** @addtogroup IPC_TX
+ * @{
+ */
+
+/**
+ ******************************************************************************
+ * @brief Retrieve a new free Tx descriptor (host side).
+ *
+ * This function returns a pointer to the next Tx descriptor available from the
+ * queue queue_idx to the host driver. The driver will have to fill it with the
+ * appropriate endianness and to send it to the
+ * emb side with ipc_host_txdesc_push().
+ *
+ * This function should only be called once until ipc_host_txdesc_push() is called.
+ *
+ * This function will return NULL if the queue is full.
+ *
+ * @param[in] env Pointer to the IPC host environment
+ * @param[in] queue_idx Queue index. The index can be inferred from the
+ * user priority of the incoming packet.
+ * @param[in] user_pos User position. If MU-MIMO is not used, this value
+ * shall be 0.
+ * @return Pointer to the next Tx descriptor free. This can
+ * point to the host memory or to shared memory,
+ * depending on IPC implementation.
+ *
+ ******************************************************************************
+ */
+volatile struct txdesc_host *ipc_host_txdesc_get(struct ipc_host_env_tag *env,
+ const int queue_idx,
+ const int user_pos);
+
+
+/**
+ ******************************************************************************
+ * @brief Push a filled Tx descriptor (host side).
+ *
+ * This function sets the next Tx descriptor available by the host side:
+ * - as used for the host side
+ * - as available for the emb side.
+ * The Tx descriptor must be correctly filled before calling this function.
+ *
+ * This function may trigger an IRQ to the emb CPU depending on the interrupt
+ * mitigation policy and on the push count.
+ *
+ * @param[in] env Pointer to the IPC host environment
+ * @param[in] queue_idx Queue index. Same value than ipc_host_txdesc_get()
+ * @param[in] user_pos User position. If MU-MIMO is not used, this value
+ * shall be 0.
+ * @param[in] host_id Parameter indicated by the IPC at TX confirmation,
+ * that allows the driver finding the buffer
+ *
+ ******************************************************************************
+ */
+void ipc_host_txdesc_push(struct ipc_host_env_tag *env, const int queue_idx,
+ const int user_pos, void *host_id);
+
+
+/**
+ ******************************************************************************
+ * @brief Check if there are TX frames pending in the TX queues.
+ *
+ * @param[in] env Pointer to the IPC host environment
+ *
+ * @return true if there are frames pending, false otherwise.
+ *
+ ******************************************************************************
+ */
+bool ipc_host_tx_frames_pending(struct ipc_host_env_tag *env);
+
+/**
+ ******************************************************************************
+ * @brief Get and flush a packet from the IPC queue passed as parameter.
+ *
+ * @param[in] env Pointer to the IPC host environment
+ * @param[in] queue_idx Index of the queue to flush
+ * @param[in] user_pos User position to flush
+ *
+ * @return The flushed hostid if there is one, 0 otherwise.
+ *
+ ******************************************************************************
+ */
+void *ipc_host_tx_flush(struct ipc_host_env_tag *env, const int queue_idx,
+ const int user_pos);
+
+/// @} IPC_TX
+
+/** @addtogroup IPC_RX
+ * @{
+ */
+void ipc_host_patt_addr_push(struct ipc_host_env_tag *env, uint32_t addr);
+
+/**
+ ******************************************************************************
+ * @brief Push a pre-allocated buffer descriptor for Rx packet (host side)
+ *
+ * This function should be called by the host IRQ handler to supply the
+ * embedded side with new empty buffer.
+ *
+ * @param[in] env Pointer to the IPC host environment
+ * @param[in] hostid Packet ID used by the host (skbuff pointer on Linux)
+ * @param[in] hostbuf Pointer to the start of the buffer payload in the
+ * host memory (this may be inferred from the skbuff?)
+ * The length of this buffer should be predefined
+ * between host and emb statically (constant needed?).
+ *
+ ******************************************************************************
+ */
+int ipc_host_rxbuf_push(struct ipc_host_env_tag *env,
+#ifdef CONFIG_RWNX_FULLMAC
+ uint32_t hostid,
+#endif
+ uint32_t hostbuf);
+
+/**
+ ******************************************************************************
+ * @brief Push a pre-allocated Descriptor
+ *
+ * This function should be called by the host IRQ handler to supply the
+ * embedded side with new empty buffer.
+ *
+ * @param[in] env Pointer to the IPC host environment
+ * @param[in] hostid Address of packet for host
+ * @param[in] hostbuf Pointer to the start of the buffer payload in the
+ * host memory. The length of this buffer should be
+ * predefined between host and emb statically.
+ *
+ ******************************************************************************
+ */
+int ipc_host_rxdesc_push(struct ipc_host_env_tag *env, void *hostid,
+ uint32_t hostbuf);
+
+/**
+ ******************************************************************************
+ * @brief Push a pre-allocated radar event buffer descriptor
+ *
+ * This function is called at Init time to initialize all radar event buffers.
+ * Then each time embedded send a radar event, this function is used to push
+ * back the same buffer once it has been handled.
+ *
+ * @param[in] env Pointer to the IPC host environment
+ * @param[in] hostid Address of packet for host
+ * @param[in] hostbuf Pointer to the start of the buffer payload in the
+ * host memory. The length of this buffer should be
+ * predefined between host and emb statically.
+ *
+ ******************************************************************************
+ */
+int ipc_host_radarbuf_push(struct ipc_host_env_tag *env, void *hostid,
+ uint32_t hostbuf);
+
+/**
+ ******************************************************************************
+ * @brief Push a pre-allocated unsupported rx vector buffer descriptor
+ *
+ * This function is called at Init time to initialize all unsupported rx vector
+ * buffers. Then each time the embedded sends a unsupported rx vector, this
+ * function is used to push a new unsupported rx vector buffer.
+ *
+ * @param[in] env Pointer to the IPC host environment
+ * @param[in] hostid Address of packet for host
+ * @param[in] hostbuf Pointer to the start of the buffer payload in the
+ * host memory. The length of this buffer should be
+ * predefined between host and emb statically.
+ *
+ ******************************************************************************
+ */
+int ipc_host_unsup_rx_vec_buf_push(struct ipc_host_env_tag *env, void *hostid,
+ uint32_t hostbuf);
+
+/**
+ ******************************************************************************
+ * @brief Push a pre-allocated buffer descriptor for IPC MSGs (host side)
+ *
+ * This function is called at Init time to initialize all Emb2App messages
+ * buffers. Then each time embedded send a IPC message, this function is used
+ * to push back the same buffer once it has been handled.
+ *
+ * @param[in] env Pointer to the IPC host environment
+ * @param[in] hostid Address of buffer for host
+ * @param[in] hostbuf Address of buffer for embedded
+ * The length of this buffer should be predefined
+ * between host and emb statically.
+ *
+ ******************************************************************************
+ */
+int ipc_host_msgbuf_push(struct ipc_host_env_tag *env, void *hostid,
+ uint32_t hostbuf);
+
+/**
+ ******************************************************************************
+ * @brief Push a pre-allocated buffer descriptor for Debug messages (host side)
+ *
+ * This function is called at Init time to initialize all debug messages.
+ * Then each time embedded send a debug message, this function is used to push
+ * back the same buffer once it has been handled.
+ *
+ * @param[in] env Pointer to the IPC host environment
+ * @param[in] hostid Address of buffer for host
+ * @param[in] hostbuf Address of buffer for embedded
+ * The length of this buffer should be predefined
+ * between host and emb statically.
+ *
+ ******************************************************************************
+ */
+int ipc_host_dbgbuf_push(struct ipc_host_env_tag *env, void *hostid,
+ uint32_t hostbuf);
+
+/**
+ ******************************************************************************
+ * @brief Push the pre-allocated logic analyzer and debug information buffer
+ *
+ * @param[in] env Pointer to the IPC host environment
+ * @param[in] infobuf Address of buffer for embedded
+ * The length of this buffer should be predefined
+ * between host and emb statically.
+ *
+ ******************************************************************************
+ */
+void ipc_host_dbginfobuf_push(struct ipc_host_env_tag *env, uint32_t infobuf);
+
+/// @} IPC_RX
+
+
+
+/** @addtogroup IPC_MISC
+ * @{
+ */
+
+/**
+ ******************************************************************************
+ * @brief Handle all IPC interrupts on the host side.
+ *
+ * The following interrupts should be handled:
+ * Tx confirmation, Rx buffer requests, Rx packet ready and kernel messages
+ *
+ * @param[in] env Pointer to the IPC host environment
+ *
+ ******************************************************************************
+ */
+void ipc_host_irq(struct ipc_host_env_tag *env, uint32_t status);
+
+/**
+ ******************************************************************************
+ * @brief Send a message to the embedded side
+ *
+ * @param[in] env Pointer to the IPC host environment
+ * @param[in] msg_buf Pointer to the message buffer
+ * @param[in] msg_len Length of the message to be transmitted
+ *
+ * @return Non-null value on failure
+ *
+ ******************************************************************************
+ */
+int ipc_host_msg_push(struct ipc_host_env_tag *env, void *msg_buf, uint16_t len);
+
+/**
+ ******************************************************************************
+ * @brief Enable IPC interrupts
+ *
+ * @param[in] env Global ipc_host environment pointer
+ * @param[in] value Bitfield of the interrupts to enable
+ *
+ * @warning After calling this function, IPC interrupts can be triggered at any
+ * time. Potentially, an interrupt could happen even before returning from the
+ * function if there is a request pending from the embedded side.
+ *
+ ******************************************************************************
+ */
+void ipc_host_enable_irq(struct ipc_host_env_tag *env, uint32_t value);
+void ipc_host_disable_irq(struct ipc_host_env_tag *env, uint32_t value);
+
+uint32_t ipc_host_get_status(struct ipc_host_env_tag *env);
+uint32_t ipc_host_get_rawstatus(struct ipc_host_env_tag *env);
+
+/// @} IPC_MISC
+
+
+#endif // _IPC_HOST_H_
diff --git a/drivers/net/wireless/aic8800/aic8800_fdrv/ipc_shared.h b/drivers/net/wireless/aic8800/aic8800_fdrv/ipc_shared.h
new file mode 100644
index 000000000000..4860490b0560
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic8800_fdrv/ipc_shared.h
@@ -0,0 +1,802 @@
+/**
+ ****************************************************************************************
+ *
+ * @file ipc_shared.h
+ *
+ * @brief Shared data between both IPC modules.
+ *
+ * Copyright (C) RivieraWaves 2011-2019
+ *
+ ****************************************************************************************
+ */
+
+#ifndef _IPC_SHARED_H_
+#define _IPC_SHARED_H_
+
+/*
+ * INCLUDE FILES
+ ****************************************************************************************
+ */
+#include "ipc_compat.h"
+#include "lmac_mac.h"
+
+/*
+ * DEFINES AND MACROS
+ ****************************************************************************************
+ */
+#define CO_BIT(pos) (1U<<(pos))
+
+#define IPC_TXQUEUE_CNT NX_TXQ_CNT
+#define NX_TXDESC_CNT0 8
+#define NX_TXDESC_CNT1 64
+#define NX_TXDESC_CNT2 64
+#define NX_TXDESC_CNT3 32
+#if NX_TXQ_CNT == 5
+#define NX_TXDESC_CNT4 8
+#endif
+
+/*
+ * Number of Host buffers available for Data Rx handling (through DMA)
+ */
+#define IPC_RXBUF_CNT 128
+
+/*
+ * Number of shared descriptors available for Data RX handling
+ */
+#define IPC_RXDESC_CNT 128
+
+/*
+ * Number of Host buffers available for Radar events handling (through DMA)
+ */
+#define IPC_RADARBUF_CNT 16
+
+/*
+ * Number of Host buffers available for unsupported Rx vectors handling (through DMA)
+ */
+#define IPC_UNSUPRXVECBUF_CNT 8
+
+/*
+ * Size of RxVector
+ */
+#define IPC_RXVEC_SIZE 16
+
+/*
+ * Number of Host buffers available for Emb->App MSGs sending (through DMA)
+ */
+#ifdef CONFIG_RWNX_FULLMAC
+#define IPC_MSGE2A_BUF_CNT 64
+#endif
+/*
+ * Number of Host buffers available for Debug Messages sending (through DMA)
+ */
+#define IPC_DBGBUF_CNT 32
+
+/*
+ * Length used in MSGs structures
+ */
+#define IPC_A2E_MSG_BUF_SIZE 127 // size in 4-byte words
+#ifdef CONFIG_RWNX_FULLMAC
+#define IPC_E2A_MSG_SIZE_BASE 256 // size in 4-byte words
+#endif
+
+#ifdef CONFIG_RWNX_TL4
+#define IPC_E2A_MSG_PARAM_SIZE (IPC_E2A_MSG_SIZE_BASE + (IPC_E2A_MSG_SIZE_BASE / 2))
+#else
+#define IPC_E2A_MSG_PARAM_SIZE IPC_E2A_MSG_SIZE_BASE
+#endif
+
+/*
+ * Debug messages buffers size (in bytes)
+ */
+#define IPC_DBG_PARAM_SIZE 256
+
+/*
+ * Define used for Rx hostbuf validity.
+ * This value should appear only when hostbuf was used for a Reception.
+ */
+#define RX_DMA_OVER_PATTERN 0xAAAAAA00
+
+/*
+ * Define used for MSG buffers validity.
+ * This value will be written only when a MSG buffer is used for sending from Emb to App.
+ */
+#define IPC_MSGE2A_VALID_PATTERN 0xADDEDE2A
+
+/*
+ * Define used for Debug messages buffers validity.
+ * This value will be written only when a DBG buffer is used for sending from Emb to App.
+ */
+#define IPC_DBG_VALID_PATTERN 0x000CACA0
+
+/*
+ * Length of the receive vectors, in bytes
+ */
+#define DMA_HDR_PHYVECT_LEN 36
+
+/*
+ * Maximum number of payload addresses and lengths present in the descriptor
+ */
+#define NX_TX_PAYLOAD_MAX 6
+
+/*
+ * Message struct/ID API version
+ */
+#define MSG_API_VER 15
+
+/*
+ ****************************************************************************************
+ */
+// c.f LMAC/src/tx/tx_swdesc.h
+/// Descriptor filled by the Host
+struct hostdesc
+{
+ /// Pointer to packet payload
+ //u32_l packet_addr;
+ /// Size of the payload
+ u16_l packet_len;
+ u16_l flags_ext;
+
+#ifdef CONFIG_RWNX_FULLMAC
+ /// Address of the status descriptor in host memory (used for confirmation upload)
+ u32_l status_desc_addr;
+ /// Destination Address
+ struct mac_addr eth_dest_addr;
+ /// Source Address
+ struct mac_addr eth_src_addr;
+ /// Ethernet Type
+ u16_l ethertype;
+#else /* ! CONFIG_RWNX_FULLMAC */
+#ifdef CONFIG_RWNX_AGG_TX
+ ///Sequence Number for AMPDU MPDUs - for quick check if it's allowed within window
+ u16_l sn;
+#endif /* CONFIG_RWNX_AGG_TX */
+ /// Padding between the buffer control structure and the MPDU in host memory
+ u8_l padding;
+#endif /* CONFIG_RWNX_FULLMAC */
+ u8_l ac;
+ /// Packet TID (0xFF if not a QoS frame)
+ u8_l tid;
+ /// Interface Id
+ u8_l vif_idx;
+ /// Station Id (0xFF if station is unknown)
+ u8_l staid;
+#ifdef CONFIG_RWNX_MUMIMO_TX
+ /// MU-MIMO information (GroupId and User Position in the group) - The GroupId
+ /// is located on bits 0-5 and the User Position on bits 6-7. The GroupId value is set
+ /// to 63 if MU-MIMO shall not be used
+ u8_l mumimo_info;
+#endif /* CONFIG_RWNX_MUMIMO_TX */
+#ifdef CONFIG_RWNX_FULLMAC
+ /// TX flags
+ u16_l flags;
+#endif /* CONFIG_RWNX_FULLMAC */
+};
+
+/// Descriptor filled by the UMAC
+struct umacdesc
+{
+#ifdef CONFIG_RWNX_AGG_TX
+ ///First Sequence Number of the BlockAck window
+ u16_l sn_win;
+ /// Flags from UMAC (match tx_hd.macctrlinfo2 format)
+ u32_l flags;
+ /// PHY related flags field - rate, GI type, BW type - filled by driver
+ u32_l phy_flags;
+#endif //(CONFIG_RWNX_AGG_TX)
+};
+
+struct txdesc_api
+{
+ /// Information provided by Host
+ struct hostdesc host;
+};
+
+
+struct txdesc_host
+{
+ u32_l ready;
+
+ /// API of the embedded part
+ struct txdesc_api api;
+};
+
+/// Comes from ipc_dma.h
+/// Element in the pool of TX DMA bridge descriptors.
+struct dma_desc
+{
+ /** Application subsystem address which is used as source address for DMA payload
+ * transfer*/
+ u32_l src;
+ /** Points to the start of the embedded data buffer associated with this descriptor.
+ * This address acts as the destination address for the DMA payload transfer*/
+ u32_l dest;
+ /// Complete length of the buffer in memory
+ u16_l length;
+ /// Control word for the DMA engine (e.g. for interrupt generation)
+ u16_l ctrl;
+ /// Pointer to the next element of the chained list
+ u32_l next;
+};
+
+// Comes from la.h
+/// Length of the configuration data of a logic analyzer
+#define LA_CONF_LEN 10
+
+/// Structure containing the configuration data of a logic analyzer
+struct la_conf_tag
+{
+ u32_l conf[LA_CONF_LEN];
+ u32_l trace_len;
+ u32_l diag_conf;
+};
+
+/// Size of a logic analyzer memory
+#define LA_MEM_LEN (1024 * 1024)
+
+/// Type of errors
+enum
+{
+ /// Recoverable error, not requiring any action from Upper MAC
+ DBG_ERROR_RECOVERABLE = 0,
+ /// Fatal error, requiring Upper MAC to reset Lower MAC and HW and restart operation
+ DBG_ERROR_FATAL
+};
+
+/// Maximum length of the SW diag trace
+#define DBG_SW_DIAG_MAX_LEN 1024
+
+/// Maximum length of the error trace
+#define DBG_ERROR_TRACE_SIZE 256
+
+/// Number of MAC diagnostic port banks
+#define DBG_DIAGS_MAC_MAX 48
+
+/// Number of PHY diagnostic port banks
+#define DBG_DIAGS_PHY_MAX 32
+
+/// Maximum size of the RX header descriptor information in the debug dump
+#define DBG_RHD_MEM_LEN (5 * 1024)
+
+/// Maximum size of the RX buffer descriptor information in the debug dump
+#define DBG_RBD_MEM_LEN (5 * 1024)
+
+/// Maximum size of the TX header descriptor information in the debug dump
+#define DBG_THD_MEM_LEN (10 * 1024)
+
+/// Structure containing the information about the PHY channel that is used
+struct phy_channel_info
+{
+ /// PHY channel information 1
+ u32_l info1;
+ /// PHY channel information 2
+ u32_l info2;
+};
+
+/// Debug information forwarded to host when an error occurs
+struct dbg_debug_info_tag
+{
+ /// Type of error (0: recoverable, 1: fatal)
+ u32_l error_type;
+ /// Pointer to the first RX Header Descriptor chained to the MAC HW
+ u32_l rhd;
+ /// Size of the RX header descriptor buffer
+ u32_l rhd_len;
+ /// Pointer to the first RX Buffer Descriptor chained to the MAC HW
+ u32_l rbd;
+ /// Size of the RX buffer descriptor buffer
+ u32_l rbd_len;
+ /// Pointer to the first TX Header Descriptors chained to the MAC HW
+ u32_l thd[NX_TXQ_CNT];
+ /// Size of the TX header descriptor buffer
+ u32_l thd_len[NX_TXQ_CNT];
+ /// MAC HW diag configuration
+ u32_l hw_diag;
+ /// Error message
+ u32_l error[DBG_ERROR_TRACE_SIZE/4];
+ /// SW diag configuration length
+ u32_l sw_diag_len;
+ /// SW diag configuration
+ u32_l sw_diag[DBG_SW_DIAG_MAX_LEN/4];
+ /// PHY channel information
+ struct phy_channel_info chan_info;
+ /// Embedded LA configuration
+ struct la_conf_tag la_conf;
+ /// MAC diagnostic port state
+ u16_l diags_mac[DBG_DIAGS_MAC_MAX];
+ /// PHY diagnostic port state
+ u16_l diags_phy[DBG_DIAGS_PHY_MAX];
+ /// MAC HW RX Header descriptor pointer
+ u32_l rhd_hw_ptr;
+ /// MAC HW RX Buffer descriptor pointer
+ u32_l rbd_hw_ptr;
+};
+
+/// Full debug dump that is forwarded to host in case of error
+struct dbg_debug_dump_tag
+{
+ /// Debug information
+ struct dbg_debug_info_tag dbg_info;
+
+ /// RX header descriptor memory
+ u32_l rhd_mem[DBG_RHD_MEM_LEN/4];
+
+ /// RX buffer descriptor memory
+ u32_l rbd_mem[DBG_RBD_MEM_LEN/4];
+
+ /// TX header descriptor memory
+ u32_l thd_mem[NX_TXQ_CNT][DBG_THD_MEM_LEN/4];
+
+ /// Logic analyzer memory
+ u32_l la_mem[LA_MEM_LEN/4];
+};
+
+
+/// Number of pulses in a radar event structure
+#define RADAR_PULSE_MAX 4
+
+/// Definition of an array of radar pulses
+struct radar_pulse_array_desc
+{
+ /// Buffer containing the radar pulses
+ u32_l pulse[RADAR_PULSE_MAX];
+ /// Index of the radar detection chain that detected those pulses
+ u32_l idx;
+ /// Number of valid pulses in the buffer
+ u32_l cnt;
+};
+
+/// Bit mapping inside a radar pulse element
+struct radar_pulse {
+ s32_l freq:6; /** Freq (resolution is 2Mhz range is [-Fadc/4 .. Fadc/4]) */
+ u32_l fom:4; /** Figure of Merit */
+ u32_l len:6; /** Length of the current radar pulse (resolution is 2us) */
+ u32_l rep:16; /** Time interval between the previous radar event
+ and the current one (in us) */
+};
+
+/// Definition of a RX vector descriptor
+struct rx_vector_desc
+{
+ /// PHY channel information
+ struct phy_channel_info phy_info;
+
+ /// RX vector 1
+ u32_l rx_vect1[IPC_RXVEC_SIZE/4];
+
+ /// Used to print a valid rx vector
+ u32_l pattern;
+};
+
+///
+struct rxdesc_tag
+{
+ /// Host Buffer Address
+ u32_l host_id;
+ /// Length
+ u32_l frame_len;
+ /// Status
+ u16_l status;
+};
+
+/**
+ ****************************************************************************************
+ * @defgroup IPC IPC
+ * @ingroup NXMAC
+ * @brief Inter Processor Communication module.
+ *
+ * The IPC module implements the protocol to communicate between the Host CPU
+ * and the Embedded CPU.
+ *
+ * @see http://en.wikipedia.org/wiki/Circular_buffer
+ * For more information about the ring buffer typical use and difficulties.
+ ****************************************************************************************
+ */
+
+
+/**
+ ****************************************************************************************
+ * @addtogroup IPC_TX IPC Tx path
+ * @ingroup IPC
+ * @brief IPC Tx path structures and functions
+ *
+ * A typical use case of the IPC Tx path API:
+ * @msc
+ * hscale = "2";
+ *
+ * a [label=Driver],
+ * b [label="IPC host"],
+ * c [label="IPC emb"],
+ * d [label=Firmware];
+ *
+ * --- [label="Tx descriptor queue example"];
+ * a=>a [label="Driver receives a Tx packet from OS"];
+ * a=>b [label="ipc_host_txdesc_get()"];
+ * a<<b [label="struct txdesc_host *"];
+ * a=>a [label="Driver fill the descriptor"];
+ * a=>b [label="ipc_host_txdesc_push()"];
+ * ... [label="(several Tx desc can be pushed)"];
+ * b:>c [label="Tx desc queue filled IRQ"];
+ * c=>>d [label="EDCA sub-scheduler callback"];
+ * c<<d [label="Tx desc queue to pop"];
+ * c=>>d [label="UMAC Tx desc callback"];
+ * ... [label="(several Tx desc can be popped)"];
+ * d=>d [label="Packets are sent or discarded"];
+ * --- [label="Tx confirm queue example"];
+ * c<=d [label="ipc_emb_txcfm_push()"];
+ * c>>d [label="Request accepted"];
+ * ... [label="(several Tx cfm can be pushed)"];
+ * b<:c [label="Tx cfm queue filled IRQ"];
+ * a<<=b [label="Driver's Tx Confirm callback"];
+ * a=>b [label="ipc_host_txcfm_pop()"];
+ * a<<b [label="struct ipc_txcfm"];
+ * a<=a [label="Packets are freed by the driver"];
+ * @endmsc
+ *
+ * @{
+ ****************************************************************************************
+ */
+
+/// @} IPC_TX
+
+/**
+ ****************************************************************************************
+ * @defgroup IPC_RX IPC Rx path
+ * @ingroup IPC
+ * @brief IPC Rx path functions and structures
+ *
+ * A typical use case of the IPC Rx path API:
+ * @msc
+ * hscale = "2";
+ *
+ * a [label=Firmware],
+ * b [label="IPC emb"],
+ * c [label="IPC host"],
+ * d [label=Driver];
+ *
+ * --- [label="Rx buffer and desc queues usage example"];
+ * d=>c [label="ipc_host_rxbuf_push()"];
+ * d=>c [label="ipc_host_rxbuf_push()"];
+ * d=>c [label="ipc_host_rxbuf_push()"];
+ * ... [label="(several Rx buffer are pushed)"];
+ * a=>a [label=" Frame is received\n from the medium"];
+ * a<<b [label="struct ipc_rxbuf"];
+ * a=>a [label=" Firmware fill the buffer\n with received frame"];
+ * a<<b [label="Push accepted"];
+ * ... [label="(several Rx desc can be pushed)"];
+ * b:>c [label="Rx desc queue filled IRQ"];
+ * c=>>d [label="Driver Rx packet callback"];
+ * c<=d [label="ipc_host_rxdesc_pop()"];
+ * d=>d [label="Rx packet is handed \nover to the OS "];
+ * ... [label="(several Rx desc can be poped)"];
+ * --- [label="Rx buffer request exemple"];
+ * b:>c [label="Low Rx buffer count IRQ"];
+ * a<<b [label="struct ipc_rxbuf"];
+ * c=>>d [label="Driver Rx buffer callback"];
+ * d=>c [label="ipc_host_rxbuf_push()"];
+ * d=>c [label="ipc_host_rxbuf_push()"];
+ * d=>c [label="ipc_host_rxbuf_push()"];
+ * ... [label="(several Rx buffer are pushed)"];
+ * @endmsc
+ *
+ * @addtogroup IPC_RX
+ * @{
+ ****************************************************************************************
+ */
+
+/// @} IPC_RX
+
+
+
+/**
+ ****************************************************************************************
+ * @defgroup IPC_MISC IPC Misc
+ * @ingroup IPC
+ * @brief IPC miscellaneous functions
+ ****************************************************************************************
+ */
+/** IPC header structure. This structure is stored at the beginning of every IPC message.
+ * @warning This structure's size must NOT exceed 4 bytes in length.
+ */
+struct ipc_header
+{
+ /// IPC message type.
+ u16_l type;
+ /// IPC message size in number of bytes.
+ u16_l size;
+};
+
+struct ipc_msg_elt
+{
+ /// Message header (alignment forced on word size, see allocation in shared env).
+ struct ipc_header header __ALIGN4;
+};
+
+/// Message structure for MSGs from Emb to App
+struct ipc_e2a_msg
+{
+ u16_l id; ///< Message id.
+ u16_l dummy_dest_id;
+ u16_l dummy_src_id;
+ u16_l param_len; ///< Parameter embedded struct length.
+ u32_l pattern; ///< Used to stamp a valid MSG buffer
+ u32_l param[IPC_E2A_MSG_PARAM_SIZE]; ///< Parameter embedded struct. Must be word-aligned.
+};
+
+/// Message structure for Debug messages from Emb to App
+struct ipc_dbg_msg
+{
+ u32_l string[IPC_DBG_PARAM_SIZE/4]; ///< Debug string
+ u32_l pattern; ///< Used to stamp a valid buffer
+};
+
+/// Message structure for MSGs from App to Emb.
+/// Actually a sub-structure will be used when filling the messages.
+struct ipc_a2e_msg
+{
+ u32_l dummy_word; // used to cope with kernel message structure
+ u32_l msg[IPC_A2E_MSG_BUF_SIZE]; // body of the msg
+};
+
+struct ipc_shared_rx_buf
+{
+ /// < ptr to hostbuf client (ipc_host client) structure
+ u32_l hostid;
+ /// < ptr to real hostbuf dma address
+ u32_l dma_addr;
+};
+
+struct ipc_shared_rx_desc
+{
+ /// DMA Address
+ u32_l dma_addr;
+};
+
+/// Structure containing FW characteristics for compatibility checking
+struct compatibility_tag {
+ /// Size of IPC shared memory
+ u16_l ipc_shared_size;
+ /// Message struct/ID API version
+ u16_l msg_api;
+ /// Version of IPC shared
+ u8_l ipc_shared_version;
+ /// Number of host buffers available for Emb->App MSGs sending
+ u8_l msge2a_buf_cnt;
+ /// Number of host buffers available for Debug Messages sending
+ u8_l dbgbuf_cnt;
+ /// Number of host buffers available for Radar events handling
+ u8_l radarbuf_cnt;
+ /// Number of host buffers available for unsupported Rx vectors handling
+ u8_l unsuprxvecbuf_cnt;
+ /// Number of shared descriptors available for Data RX handling
+ u8_l rxdesc_cnt;
+ /// Number of host buffers available for Data Rx handling
+ u8_l rxbuf_cnt;
+ /// Number of descriptors in BK TX queue (power of 2, min 4, max 64)
+ u8_l bk_txq;
+ /// Number of descriptors in BE TX queue (power of 2, min 4, max 64)
+ u8_l be_txq;
+ /// Number of descriptors in VI TX queue (power of 2, min 4, max 64)
+ u8_l vi_txq;
+ /// Number of descriptors in VO TX queue (power of 2, min 4, max 64)
+ u8_l vo_txq;
+ /// Number of descriptors in BCN TX queue (power of 2, min 4, max 64)
+ u8_l bcn_txq;
+};
+
+/*
+ * TYPE and STRUCT DEFINITIONS
+ ****************************************************************************************
+ */
+
+
+// Indexes are defined in the MIB shared structure
+struct ipc_shared_env_tag
+{
+ volatile struct compatibility_tag comp_info; //FW characteristics
+
+ volatile struct ipc_a2e_msg msg_a2e_buf; // room for MSG to be sent from App to Emb
+
+ // Fields for MSGs sending from Emb to App
+ volatile struct ipc_e2a_msg msg_e2a_buf; // room to build the MSG to be DMA Xferred
+ volatile struct dma_desc msg_dma_desc; // DMA descriptor for Emb->App MSGs Xfers
+ volatile u32_l msg_e2a_hostbuf_addr [IPC_MSGE2A_BUF_CNT]; // buffers @ for DMA Xfers
+
+ // Fields for Debug MSGs sending from Emb to App
+ volatile struct ipc_dbg_msg dbg_buf; // room to build the MSG to be DMA Xferred
+ volatile struct dma_desc dbg_dma_desc; // DMA descriptor for Emb->App MSGs Xfers
+ volatile u32_l dbg_hostbuf_addr [IPC_DBGBUF_CNT]; // buffers @ for MSGs DMA Xfers
+ volatile u32_l la_dbginfo_addr; // Host buffer address for the debug information
+ volatile u32_l pattern_addr;
+ volatile u32_l radarbuf_hostbuf [IPC_RADARBUF_CNT]; // buffers @ for Radar Events
+ volatile u32_l unsuprxvecbuf_hostbuf [IPC_UNSUPRXVECBUF_CNT]; // buffers @ for unsupported Rx vectors
+ volatile struct txdesc_host txdesc0[CONFIG_USER_MAX][NX_TXDESC_CNT0];
+ volatile struct txdesc_host txdesc1[CONFIG_USER_MAX][NX_TXDESC_CNT1];
+ volatile struct txdesc_host txdesc2[CONFIG_USER_MAX][NX_TXDESC_CNT2];
+ volatile struct txdesc_host txdesc3[CONFIG_USER_MAX][NX_TXDESC_CNT3];
+ #if NX_TXQ_CNT == 5
+ volatile struct txdesc_host txdesc4[1][NX_TXDESC_CNT4];
+ #endif
+ #ifdef CONFIG_RWNX_FULLMAC
+ // RX Descriptors Array
+ volatile struct ipc_shared_rx_desc host_rxdesc[IPC_RXDESC_CNT];
+ // RX Buffers Array
+ volatile struct ipc_shared_rx_buf host_rxbuf[IPC_RXBUF_CNT];
+ #else
+ // buffers @ for Data Rx
+ volatile u32_l host_rxbuf[IPC_RXBUF_CNT];
+ #endif /* CONFIG_RWNX_FULLMAC */
+
+ u32_l buffered[NX_REMOTE_STA_MAX][TID_MAX];
+
+ volatile uint16_t trace_pattern;
+ volatile uint32_t trace_start;
+ volatile uint32_t trace_end;
+ volatile uint32_t trace_size;
+ volatile uint32_t trace_offset;
+ volatile uint32_t trace_nb_compo;
+ volatile uint32_t trace_offset_compo;
+};
+
+extern struct ipc_shared_env_tag ipc_shared_env;
+
+
+/*
+ * TYPE and STRUCT DEFINITIONS
+ ****************************************************************************************
+ */
+
+// IRQs from app to emb
+/// Interrupts bits used for the TX descriptors of the AC queues
+#ifdef CONFIG_RWNX_MUMIMO_TX
+#ifdef CONFIG_RWNX_OLD_IPC
+#error "MU-MIMO cannot be compiled for old IPC"
+#endif
+/// Interrupts bits used
+#if CONFIG_USER_MAX > 3
+#define IPC_IRQ_A2E_USER_MSK 0xF
+#elif CONFIG_USER_MAX > 2
+#define IPC_IRQ_A2E_USER_MSK 0x7
+#else
+#define IPC_IRQ_A2E_USER_MSK 0x3
+#endif
+
+/// Offset of the interrupts for AC0
+#define IPC_IRQ_A2E_AC0_OFT 8
+/// Mask of the interrupts for AC0
+#define IPC_IRQ_A2E_AC0_MSK (IPC_IRQ_A2E_USER_MSK << IPC_IRQ_A2E_AC0_OFT)
+/// Offset of the interrupts for AC1
+#define IPC_IRQ_A2E_AC1_OFT (IPC_IRQ_A2E_AC0_OFT + CONFIG_USER_MAX)
+/// Mask of the interrupts for AC1
+#define IPC_IRQ_A2E_AC1_MSK (IPC_IRQ_A2E_USER_MSK << IPC_IRQ_A2E_AC1_OFT)
+/// Offset of the interrupts for AC2
+#define IPC_IRQ_A2E_AC2_OFT (IPC_IRQ_A2E_AC1_OFT + CONFIG_USER_MAX)
+/// Mask of the interrupts for AC2
+#define IPC_IRQ_A2E_AC2_MSK (IPC_IRQ_A2E_USER_MSK << IPC_IRQ_A2E_AC2_OFT)
+/// Offset of the interrupts for AC3
+#define IPC_IRQ_A2E_AC3_OFT (IPC_IRQ_A2E_AC2_OFT + CONFIG_USER_MAX)
+/// Mask of the interrupts for AC3
+#define IPC_IRQ_A2E_AC3_MSK (IPC_IRQ_A2E_USER_MSK << IPC_IRQ_A2E_AC3_OFT)
+/// Offset of the interrupts for BCN
+#define IPC_IRQ_A2E_BCN_OFT (IPC_IRQ_A2E_AC3_OFT + CONFIG_USER_MAX)
+/// Mask of the interrupts for BCN
+#define IPC_IRQ_A2E_BCN_MSK CO_BIT(IPC_IRQ_A2E_BCN_OFT)
+
+#define IPC_IRQ_A2E_AC_TXDESC (IPC_IRQ_A2E_AC0_MSK | IPC_IRQ_A2E_AC1_MSK | \
+ IPC_IRQ_A2E_AC2_MSK | IPC_IRQ_A2E_AC3_MSK)
+
+/// Interrupts bits used for the TX descriptors of the BCN queue
+#if NX_TXQ_CNT < 5
+#define IPC_IRQ_A2E_BCN_TXDESC 0
+#else
+#define IPC_IRQ_A2E_BCN_TXDESC (0x01 << IPC_IRQ_A2E_BCN_OFT)
+#endif
+
+/// IPC TX descriptor interrupt mask
+#define IPC_IRQ_A2E_TXDESC (IPC_IRQ_A2E_AC_TXDESC | IPC_IRQ_A2E_BCN_TXDESC)
+#else
+/// IPC TX descriptor interrupt mask
+#define IPC_IRQ_A2E_TXDESC 0xFF00
+#endif
+
+#define IPC_IRQ_A2E_TXDESC_FIRSTBIT (8)
+#define IPC_IRQ_A2E_RXBUF_BACK CO_BIT(5)
+#define IPC_IRQ_A2E_RXDESC_BACK CO_BIT(4)
+
+#define IPC_IRQ_A2E_MSG CO_BIT(1)
+#define IPC_IRQ_A2E_DBG CO_BIT(0)
+
+#define IPC_IRQ_A2E_ALL (IPC_IRQ_A2E_TXDESC|IPC_IRQ_A2E_MSG|IPC_IRQ_A2E_DBG)
+
+// IRQs from emb to app
+#define IPC_IRQ_E2A_TXCFM_POS 7
+
+#ifdef CONFIG_RWNX_MUMIMO_TX
+#ifdef CONFIG_RWNX_OLD_IPC
+#error "MU-MIMO cannot be compiled for old IPC"
+#endif
+/// Interrupts bits used
+#if CONFIG_USER_MAX > 3
+#define IPC_IRQ_E2A_USER_MSK 0xF
+#elif CONFIG_USER_MAX > 2
+#define IPC_IRQ_E2A_USER_MSK 0x7
+#else
+#define IPC_IRQ_E2A_USER_MSK 0x3
+#endif
+
+/// Offset of the interrupts for AC0
+#define IPC_IRQ_E2A_AC0_OFT IPC_IRQ_E2A_TXCFM_POS
+/// Mask of the interrupts for AC0
+#define IPC_IRQ_E2A_AC0_MSK (IPC_IRQ_E2A_USER_MSK << IPC_IRQ_E2A_AC0_OFT)
+/// Offset of the interrupts for AC1
+#define IPC_IRQ_E2A_AC1_OFT (IPC_IRQ_E2A_AC0_OFT + CONFIG_USER_MAX)
+/// Mask of the interrupts for AC1
+#define IPC_IRQ_E2A_AC1_MSK (IPC_IRQ_E2A_USER_MSK << IPC_IRQ_E2A_AC1_OFT)
+/// Offset of the interrupts for AC2
+#define IPC_IRQ_E2A_AC2_OFT (IPC_IRQ_E2A_AC1_OFT + CONFIG_USER_MAX)
+/// Mask of the interrupts for AC2
+#define IPC_IRQ_E2A_AC2_MSK (IPC_IRQ_E2A_USER_MSK << IPC_IRQ_E2A_AC2_OFT)
+/// Offset of the interrupts for AC3
+#define IPC_IRQ_E2A_AC3_OFT (IPC_IRQ_E2A_AC2_OFT + CONFIG_USER_MAX)
+/// Mask of the interrupts for AC3
+#define IPC_IRQ_E2A_AC3_MSK (IPC_IRQ_E2A_USER_MSK << IPC_IRQ_E2A_AC3_OFT)
+/// Offset of the interrupts for BCN
+#define IPC_IRQ_E2A_BCN_OFT (IPC_IRQ_E2A_AC3_OFT + CONFIG_USER_MAX)
+/// Mask of the interrupts for BCN
+#define IPC_IRQ_E2A_BCN_MSK CO_BIT(IPC_IRQ_E2A_BCN_OFT)
+
+#define IPC_IRQ_E2A_AC_TXCFM (IPC_IRQ_E2A_AC0_MSK | IPC_IRQ_E2A_AC1_MSK | \
+ IPC_IRQ_E2A_AC2_MSK | IPC_IRQ_E2A_AC3_MSK)
+
+/// Interrupts bits used for the TX descriptors of the BCN queue
+#if NX_TXQ_CNT < 5
+#define IPC_IRQ_E2A_BCN_TXCFM 0
+#else
+#define IPC_IRQ_E2A_BCN_TXCFM (0x01 << IPC_IRQ_E2A_BCN_OFT)
+#endif
+
+/// IPC TX descriptor interrupt mask
+#define IPC_IRQ_E2A_TXCFM (IPC_IRQ_E2A_AC_TXCFM | IPC_IRQ_E2A_BCN_TXCFM)
+
+#else
+
+#define IPC_IRQ_E2A_TXCFM ((1 << NX_TXQ_CNT) - 1 ) << IPC_IRQ_E2A_TXCFM_POS
+
+#endif /* CONFIG_RWNX_MUMIMO_TX */
+
+#define IPC_IRQ_E2A_UNSUP_RX_VEC CO_BIT(7)
+#define IPC_IRQ_E2A_RADAR CO_BIT(6)
+#define IPC_IRQ_E2A_TBTT_SEC CO_BIT(5)
+#define IPC_IRQ_E2A_TBTT_PRIM CO_BIT(4)
+#define IPC_IRQ_E2A_RXDESC CO_BIT(3)
+#define IPC_IRQ_E2A_MSG_ACK CO_BIT(2)
+#define IPC_IRQ_E2A_MSG CO_BIT(1)
+#define IPC_IRQ_E2A_DBG CO_BIT(0)
+
+#define IPC_IRQ_E2A_ALL ( IPC_IRQ_E2A_TXCFM \
+ | IPC_IRQ_E2A_RXDESC \
+ | IPC_IRQ_E2A_MSG_ACK \
+ | IPC_IRQ_E2A_MSG \
+ | IPC_IRQ_E2A_DBG \
+ | IPC_IRQ_E2A_TBTT_PRIM \
+ | IPC_IRQ_E2A_TBTT_SEC \
+ | IPC_IRQ_E2A_RADAR \
+ | IPC_IRQ_E2A_UNSUP_RX_VEC)
+
+// FLAGS for RX desc
+#define IPC_RX_FORWARD CO_BIT(1)
+#define IPC_RX_INTRABSS CO_BIT(0)
+
+
+// IPC message TYPE
+enum
+{
+ IPC_MSG_NONE = 0,
+ IPC_MSG_WRAP,
+ IPC_MSG_KMSG,
+
+ IPC_DBG_STRING,
+
+};
+
+#endif // _IPC_SHARED_H_
+
diff --git a/drivers/net/wireless/aic8800/aic8800_fdrv/lmac_mac.h b/drivers/net/wireless/aic8800/aic8800_fdrv/lmac_mac.h
new file mode 100644
index 000000000000..985121080606
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic8800_fdrv/lmac_mac.h
@@ -0,0 +1,588 @@
+/**
+ ****************************************************************************************
+ *
+ * @file lmac_mac_types.h
+ *
+ * @brief MAC related definitions.
+ *
+ * Adapted from mac_types.h to used lmac_types.h instead of standard types
+ * eg: perl -pi -e '$_ =~ s/uint(\d{1,2})_t/u$1_l/g; \
+ * $_ =~ s/int(\d{1,2})_t/s$1_l/g; \
+ * $_ =~ s/CO_BIT/BIT/g;' lmac_mac.h
+ *
+ * Copyright (C) RivieraWaves 2011-2019
+ *
+ ****************************************************************************************
+ */
+
+#ifndef LMAC_MAC_H_
+#define LMAC_MAC_H_
+
+#include "lmac_types.h"
+
+/// Interface types
+enum mac_vif_type
+{
+ /// ESS STA interface
+ VIF_STA,
+ /// IBSS STA interface
+ VIF_IBSS,
+ /// AP interface
+ VIF_AP,
+ /// Mesh Point interface
+ VIF_MESH_POINT,
+ /// Monitor interface
+ VIF_MONITOR,
+ /// Unknown type
+ VIF_UNKNOWN
+};
+
+/// MAC address length in bytes.
+#define MAC_ADDR_LEN 6
+
+/// MAC address structure.
+struct mac_addr
+{
+ /// Array of 16-bit words that make up the MAC address.
+ u16_l array[MAC_ADDR_LEN/2];
+};
+
+/// SSID maximum length.
+#define MAC_SSID_LEN 32
+
+/// SSID.
+struct mac_ssid
+{
+ /// Actual length of the SSID.
+ u8_l length;
+ /// Array containing the SSID name.
+ u8_l array[MAC_SSID_LEN];
+};
+
+/// BSS type
+enum mac_bss_type
+{
+ INFRASTRUCTURE_MODE = 1,
+ INDEPENDENT_BSS_MODE,
+ ANY_BSS_MODE
+};
+
+/// Channel Band
+enum mac_chan_band
+{
+ /// 2.4GHz Band
+ PHY_BAND_2G4,
+ /// 5GHz band
+ PHY_BAND_5G,
+ /// Number of bands
+ PHY_BAND_MAX,
+};
+
+/// Operating Channel Bandwidth
+enum mac_chan_bandwidth
+{
+ /// 20MHz BW
+ PHY_CHNL_BW_20,
+ /// 40MHz BW
+ PHY_CHNL_BW_40,
+ /// 80MHz BW
+ PHY_CHNL_BW_80,
+ /// 160MHz BW
+ PHY_CHNL_BW_160,
+ /// 80+80MHz BW
+ PHY_CHNL_BW_80P80,
+ /// Reserved BW
+ PHY_CHNL_BW_OTHER,
+};
+
+/// max number of channels in the 2.4 GHZ band
+#define MAC_DOMAINCHANNEL_24G_MAX 14
+
+/// max number of channels in the 5 GHZ band
+#define MAC_DOMAINCHANNEL_5G_MAX 28
+
+/// Channel Flag
+enum mac_chan_flags
+{
+ /// Cannot initiate radiation on this channel
+ CHAN_NO_IR = BIT(0),
+ /// Channel is not allowed
+ CHAN_DISABLED = BIT(1),
+ /// Radar detection required on this channel
+ CHAN_RADAR = BIT(2),
+};
+
+/// Primary Channel definition
+struct mac_chan_def
+{
+ /// Frequency of the channel (in MHz)
+ u16_l freq;
+ /// RF band (@ref mac_chan_band)
+ u8_l band;
+ /// Additional information (@ref mac_chan_flags)
+ u8_l flags;
+ /// Max transmit power allowed on this channel (dBm)
+ s8_l tx_power;
+};
+
+/// Operating Channel
+struct mac_chan_op
+{
+ /// Band (@ref mac_chan_band)
+ u8_l band;
+ /// Channel type (@ref mac_chan_bandwidth)
+ u8_l type;
+ /// Frequency for Primary 20MHz channel (in MHz)
+ u16_l prim20_freq;
+ /// Frequency center of the contiguous channel or center of Primary 80+80 (in MHz)
+ u16_l center1_freq;
+ /// Frequency center of the non-contiguous secondary 80+80 (in MHz)
+ u16_l center2_freq;
+ /// Max transmit power allowed on this channel (dBm)
+ s8_l tx_power;
+ /// Additional information (@ref mac_chan_flags)
+ u8_l flags;
+};
+
+/// Cipher suites (order is important as it is used by MACHW)
+enum mac_cipher_suite
+{
+ /// 00-0F-AC 1
+ MAC_CIPHER_WEP40 = 0,
+ /// 00-0F-AC 2
+ MAC_CIPHER_TKIP = 1,
+ /// 00-0F-AC 4
+ MAC_CIPHER_CCMP = 2,
+ /// 00-0F-AC 5
+ MAC_CIPHER_WEP104 = 3,
+ /// 00-14-72 1
+ MAC_CIPHER_WPI_SMS4 = 4,
+ /// 00-0F-AC 6 (aka AES_CMAC)
+ MAC_CIPHER_BIP_CMAC_128 = 5,
+
+ // following cipher are not supported by MACHW
+ /// 00-0F-AC 08
+ MAC_CIPHER_GCMP_128,
+ /// 00-0F-AC 09
+ MAC_CIPHER_GCMP_256,
+ /// 00-0F-AC 10
+ MAC_CIPHER_CCMP_256,
+ /// 00-0F-AC 11
+ MAC_CIPHER_BIP_GMAC_128,
+ /// 00-0F-AC 12
+ MAC_CIPHER_BIP_GMAC_256,
+ /// 00-0F-AC 13
+ MAC_CIPHER_BIP_CMAC_256,
+
+ MAC_CIPHER_INVALID = 0xFF
+};
+
+/// Authentication and Key Management suite
+enum mac_akm_suite
+{
+ /// No security
+ MAC_AKM_NONE,
+ /// Pre RSN (WEP or WPA)
+ MAC_AKM_PRE_RSN,
+ /// 00-0F-AC 1
+ MAC_AKM_8021X,
+ /// 00-0F-AC 2
+ MAC_AKM_PSK,
+ /// 00-0F-AC 3
+ MAC_AKM_FT_8021X,
+ /// 00-0F-AC 4
+ MAC_AKM_FT_PSK,
+ /// 00-0F-AC 5
+ MAC_AKM_8021X_SHA256,
+ /// 00-0F-AC 6
+ MAC_AKM_PSK_SHA256,
+ /// 00-0F-AC 7
+ MAC_AKM_TDLS,
+ /// 00-0F-AC 8
+ MAC_AKM_SAE,
+ /// 00-0F-AC 9
+ MAC_AKM_FT_OVER_SAE,
+ /// 00-0F-AC 11
+ MAC_AKM_8021X_SUITE_B,
+ /// 00-0F-AC 12
+ MAC_AKM_8021X_SUITE_B_192,
+ /// 00-0F-AC 14
+ MAC_AKM_FILS_SHA256,
+ /// 00-0F-AC 15
+ MAC_AKM_FILS_SHA384,
+ /// 00-0F-AC 16
+ MAC_AKM_FT_FILS_SHA256,
+ /// 00-0F-AC 17
+ MAC_AKM_FT_FILS_SHA384,
+ /// 00-0F-AC 18
+ MAC_AKM_OWE,
+
+ /// 00-14-72 1
+ MAC_AKM_WAPI_CERT,
+ /// 00-14-72 2
+ MAC_AKM_WAPI_PSK,
+};
+
+/// Scan result element, parsed from beacon or probe response frames.
+struct mac_scan_result
+{
+ /// Scan result is valid
+ bool valid_flag;
+ /// Network BSSID.
+ struct mac_addr bssid;
+ /// Network name.
+ struct mac_ssid ssid;
+ /// Network type (@ref mac_bss_type).
+ u16_l bsstype;
+ /// Network channel.
+ struct mac_chan_def *chan;
+ /// Network beacon period (in TU).
+ u16_l beacon_period;
+ /// Capability information
+ u16_l cap_info;
+ /// Supported AKM (bit-field of @ref mac_akm_suite)
+ u32_l akm;
+ /// Group cipher (bit-field of @ref mac_cipher_suite)
+ u16_l group_cipher;
+ /// Group cipher (bit-field of @ref mac_cipher_suite)
+ u16_l pairwise_cipher;
+ /// RSSI of the scanned BSS (in dBm)
+ s8_l rssi;
+ ///Multi-BSSID index (0 if this is the reference (i.e. transmitted) BSSID)
+ u8_l mluti_bssid_index;
+ ///Maximum BSSID indicator
+ u8_l max_bssid_indicator;
+};
+
+/// Legacy rate 802.11 definitions
+enum mac_legacy_rates
+{
+ /// DSSS/CCK 1Mbps
+ MAC_RATE_1MBPS = 2,
+ /// DSSS/CCK 2Mbps
+ MAC_RATE_2MBPS = 4,
+ /// DSSS/CCK 5.5Mbps
+ MAC_RATE_5_5MBPS = 11,
+ /// OFDM 6Mbps
+ MAC_RATE_6MBPS = 12,
+ /// OFDM 9Mbps
+ MAC_RATE_9MBPS = 18,
+ /// DSSS/CCK 11Mbps
+ MAC_RATE_11MBPS = 22,
+ /// OFDM 12Mbps
+ MAC_RATE_12MBPS = 24,
+ /// OFDM 18Mbps
+ MAC_RATE_18MBPS = 36,
+ /// OFDM 24Mbps
+ MAC_RATE_24MBPS = 48,
+ /// OFDM 36Mbps
+ MAC_RATE_36MBPS = 72,
+ /// OFDM 48Mbps
+ MAC_RATE_48MBPS = 96,
+ /// OFDM 54Mbps
+ MAC_RATE_54MBPS = 108
+};
+
+/// BSS Membership Selector definitions
+enum mac_bss_membership
+{
+ /// HT PHY
+ MAC_BSS_MEMBERSHIP_HT_PHY = 127,
+ /// VHT PHY
+ MAC_BSS_MEMBERSHIP_VHT_PHY = 126,
+};
+
+/// MAC rateset maximum length
+#define MAC_RATESET_LEN 12
+
+/// Structure containing the legacy rateset of a station
+struct mac_rateset
+{
+ /// Number of legacy rates supported
+ u8_l length;
+ /// Array of legacy rates
+ u8_l array[MAC_RATESET_LEN];
+};
+
+/// MAC Security Key maximum length
+#define MAC_SEC_KEY_LEN 32 // TKIP keys 256 bits (max length) with MIC keys
+
+/// Structure defining a security key
+struct mac_sec_key
+{
+ /// Key material length
+ u8_l length;
+ /// Key material
+ u32_l array[MAC_SEC_KEY_LEN/4];
+};
+
+/// Access Category enumeration
+enum mac_ac
+{
+ /// Background
+ AC_BK = 0,
+ /// Best-effort
+ AC_BE,
+ /// Video
+ AC_VI,
+ /// Voice
+ AC_VO,
+ /// Number of access categories
+ AC_MAX
+};
+
+/// Traffic ID enumeration
+enum mac_tid
+{
+ /// TID_0. Mapped to @ref AC_BE as per 802.11 standard.
+ TID_0,
+ /// TID_1. Mapped to @ref AC_BK as per 802.11 standard.
+ TID_1,
+ /// TID_2. Mapped to @ref AC_BK as per 802.11 standard.
+ TID_2,
+ /// TID_3. Mapped to @ref AC_BE as per 802.11 standard.
+ TID_3,
+ /// TID_4. Mapped to @ref AC_VI as per 802.11 standard.
+ TID_4,
+ /// TID_5. Mapped to @ref AC_VI as per 802.11 standard.
+ TID_5,
+ /// TID_6. Mapped to @ref AC_VO as per 802.11 standard.
+ TID_6,
+ /// TID_7. Mapped to @ref AC_VO as per 802.11 standard.
+ TID_7,
+ /// Non standard Management TID used internally
+ TID_MGT,
+ /// Number of TID supported
+ TID_MAX
+};
+
+/// MCS bitfield maximum size (in bytes)
+#define MAX_MCS_LEN 16 // 16 * 8 = 128
+
+/// MAC HT capability information element
+struct mac_htcapability
+{
+ /// HT capability information
+ u16_l ht_capa_info;
+ /// A-MPDU parameters
+ u8_l a_mpdu_param;
+ /// Supported MCS
+ u8_l mcs_rate[MAX_MCS_LEN];
+ /// HT extended capability information
+ u16_l ht_extended_capa;
+ /// Beamforming capability information
+ u32_l tx_beamforming_capa;
+ /// Antenna selection capability information
+ u8_l asel_capa;
+};
+
+/// MAC VHT capability information element
+struct mac_vhtcapability
+{
+ /// VHT capability information
+ u32_l vht_capa_info;
+ /// RX MCS map
+ u16_l rx_mcs_map;
+ /// RX highest data rate
+ u16_l rx_highest;
+ /// TX MCS map
+ u16_l tx_mcs_map;
+ /// TX highest data rate
+ u16_l tx_highest;
+};
+
+/// Length (in bytes) of the MAC HE capability field
+#define MAC_HE_MAC_CAPA_LEN 6
+/// Length (in bytes) of the PHY HE capability field
+#define MAC_HE_PHY_CAPA_LEN 11
+/// Maximum length (in bytes) of the PPE threshold data
+#define MAC_HE_PPE_THRES_MAX_LEN 25
+
+/// Structure listing the per-NSS, per-BW supported MCS combinations
+struct mac_he_mcs_nss_supp
+{
+ /// per-NSS supported MCS in RX, for BW <= 80MHz
+ u16_l rx_mcs_80;
+ /// per-NSS supported MCS in TX, for BW <= 80MHz
+ u16_l tx_mcs_80;
+ /// per-NSS supported MCS in RX, for BW = 160MHz
+ u16_l rx_mcs_160;
+ /// per-NSS supported MCS in TX, for BW = 160MHz
+ u16_l tx_mcs_160;
+ /// per-NSS supported MCS in RX, for BW = 80+80MHz
+ u16_l rx_mcs_80p80;
+ /// per-NSS supported MCS in TX, for BW = 80+80MHz
+ u16_l tx_mcs_80p80;
+};
+
+/// MAC HE capability information element
+struct mac_hecapability
+{
+ /// MAC HE capabilities
+ u8_l mac_cap_info[MAC_HE_MAC_CAPA_LEN];
+ /// PHY HE capabilities
+ u8_l phy_cap_info[MAC_HE_PHY_CAPA_LEN];
+ /// Supported MCS combinations
+ struct mac_he_mcs_nss_supp mcs_supp;
+ /// PPE Thresholds data
+ u8_l ppe_thres[MAC_HE_PPE_THRES_MAX_LEN];
+};
+
+/// Station flags
+enum mac_sta_flags
+{
+ /// Bit indicating that a STA has QoS (WMM) capability
+ STA_QOS_CAPA = BIT(0),
+ /// Bit indicating that a STA has HT capability
+ STA_HT_CAPA = BIT(1),
+ /// Bit indicating that a STA has VHT capability
+ STA_VHT_CAPA = BIT(2),
+ /// Bit indicating that a STA has MFP capability
+ STA_MFP_CAPA = BIT(3),
+ /// Bit indicating that the STA included the Operation Notification IE
+ STA_OPMOD_NOTIF = BIT(4),
+ /// Bit indicating that a STA has HE capability
+ STA_HE_CAPA = BIT(5),
+};
+
+/// Connection flags
+enum mac_connection_flags
+{
+ /// Flag indicating whether the control port is controlled by host or not
+ CONTROL_PORT_HOST = BIT(0),
+ /// Flag indicating whether the control port frame shall be sent unencrypted
+ CONTROL_PORT_NO_ENC = BIT(1),
+ /// Flag indicating whether HT and VHT shall be disabled or not
+ DISABLE_HT = BIT(2),
+ /// Flag indicating whether WPA or WPA2 authentication is in use
+ WPA_WPA2_IN_USE = BIT(3),
+ /// Flag indicating whether MFP is in use
+ MFP_IN_USE = BIT(4),
+ // Flag indicating Roam
+ REASSOCIATION = BIT(5),
+};
+
+#ifdef CONFIG_HE_FOR_OLD_KERNEL
+#define IEEE80211_HE_MAC_CAP2_ALL_ACK 0x02
+#define IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G 0x02
+#define IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G 0x04
+#define IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD 0x20
+#define IEEE80211_HE_PHY_CAP1_HE_LTF_AND_GI_FOR_HE_PPDUS_0_8US 0x40
+#define IEEE80211_HE_PHY_CAP1_MIDAMBLE_RX_TX_MAX_NSTS 0x80
+#define IEEE80211_HE_PHY_CAP2_MIDAMBLE_RX_TX_MAX_NSTS 0x01
+#define IEEE80211_HE_PHY_CAP2_NDP_4x_LTF_AND_3_2US 0x02
+#define IEEE80211_HE_PHY_CAP2_DOPPLER_RX 0x20
+#define IEEE80211_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ 0x08
+#define IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_16_QAM 0x18
+#define IEEE80211_HE_PHY_CAP3_DCM_MAX_RX_NSS_1 0x00
+#define IEEE80211_HE_PHY_CAP3_RX_HE_MU_PPDU_FROM_NON_AP_STA 0x40
+#define IEEE80211_HE_PHY_CAP4_SU_BEAMFORMEE 0x01
+#define IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_UNDER_80MHZ_4 0x0c
+#define IEEE80211_HE_PHY_CAP5_NG16_SU_FEEDBACK 0x40
+#define IEEE80211_HE_PHY_CAP5_NG16_MU_FEEDBACK 0x80
+#define IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_42_SU 0x01
+#define IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_75_MU 0x02
+#define IEEE80211_HE_PHY_CAP6_TRIG_SU_BEAMFORMER_FB 0x04
+#define IEEE80211_HE_PHY_CAP6_TRIG_MU_BEAMFORMER_FB 0x08
+#define IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT 0x80
+#define IEEE80211_HE_PHY_CAP6_PARTIAL_BANDWIDTH_DL_MUMIMO 0x40
+#define IEEE80211_HE_PHY_CAP7_HE_SU_MU_PPDU_4XLTF_AND_08_US_GI 0x04
+#define IEEE80211_HE_PHY_CAP8_20MHZ_IN_40MHZ_HE_PPDU_IN_2G 0x02
+#define IEEE80211_HE_PHY_CAP9_RX_FULL_BW_SU_USING_MU_WITH_COMP_SIGB 0x10
+#define IEEE80211_HE_PHY_CAP9_RX_FULL_BW_SU_USING_MU_WITH_NON_COMP_SIGB 0x20
+
+struct ieee80211_he_cap_elem {
+ u8 mac_cap_info[6];
+ u8 phy_cap_info[11];
+} __packed;
+
+struct ieee80211_he_mcs_nss_supp {
+ __le16 rx_mcs_80;
+ __le16 tx_mcs_80;
+ __le16 rx_mcs_160;
+ __le16 tx_mcs_160;
+ __le16 rx_mcs_80p80;
+ __le16 tx_mcs_80p80;
+} __packed;
+
+#define IEEE80211_HE_PPE_THRES_MAX_LEN 25
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0)
+#define WLAN_EID_EXTENSION 255
+/* Element ID Extensions for Element ID 255 */
+
+enum ieee80211_eid_ext {
+ WLAN_EID_EXT_ASSOC_DELAY_INFO = 1,
+ WLAN_EID_EXT_FILS_REQ_PARAMS = 2,
+ WLAN_EID_EXT_FILS_KEY_CONFIRM = 3,
+ WLAN_EID_EXT_FILS_SESSION = 4,
+ WLAN_EID_EXT_FILS_HLP_CONTAINER = 5,
+ WLAN_EID_EXT_FILS_IP_ADDR_ASSIGN = 6,
+ WLAN_EID_EXT_KEY_DELIVERY = 7,
+ WLAN_EID_EXT_FILS_WRAPPED_DATA = 8,
+ WLAN_EID_EXT_FILS_PUBLIC_KEY = 12,
+ WLAN_EID_EXT_FILS_NONCE = 13,
+ WLAN_EID_EXT_FUTURE_CHAN_GUIDANCE = 14,
+
+};
+
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 19, 0)
+#define WLAN_EID_EXT_HE_CAPABILITY 35
+#define WLAN_EID_EXT_HE_OPERATION 36
+#define WLAN_EID_EXT_UORA 37
+#define WLAN_EID_EXT_HE_MU_EDCA 38
+#define WLAN_EID_EXT_HE_SPR 39
+#define WLAN_EID_EXT_NDP_FEEDBACK_REPORT_PARAMSET 41
+#define WLAN_EID_EXT_BSS_COLOR_CHG_ANN 42
+#define WLAN_EID_EXT_QUIET_TIME_PERIOD_SETUP 43
+#define WLAN_EID_EXT_ESS_REPORT 45
+#define WLAN_EID_EXT_OPS 46
+#define WLAN_EID_EXT_HE_BSS_LOAD 47
+#define WLAN_EID_EXT_MAX_CHANNEL_SWITCH_TIME 52
+#define WLAN_EID_EXT_MULTIPLE_BSSID_CONFIGURATION 55
+#define WLAN_EID_EXT_NON_INHERITANCE 56
+#define WLAN_EID_EXT_KNOWN_BSSID 57
+#define WLAN_EID_EXT_SHORT_SSID_LIST 58
+#define WLAN_EID_EXT_HE_6GHZ_CAPA 59
+#define WLAN_EID_EXT_UL_MU_POWER_CAPA 60
+#define WLAN_EID_EXT_EHT_OPERATION 106
+#define WLAN_EID_EXT_EHT_MULTI_LINK 107
+#define WLAN_EID_EXT_EHT_CAPABILITY 108
+
+#endif
+
+struct ieee80211_sta_he_cap {
+ bool has_he;
+ struct ieee80211_he_cap_elem he_cap_elem;
+ struct ieee80211_he_mcs_nss_supp he_mcs_nss_supp;
+ u8 ppe_thres[IEEE80211_HE_PPE_THRES_MAX_LEN];
+};
+
+struct ieee80211_sband_iftype_data {
+ u16 types_mask;
+ struct ieee80211_sta_he_cap he_cap;
+};
+#endif
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 8, 0) || defined(CONFIG_VHT_FOR_OLD_KERNEL)
+struct ieee80211_vht_mcs_info {
+ __le16 rx_mcs_map;
+ __le16 rx_highest;
+ __le16 tx_mcs_map;
+ __le16 tx_highest;
+} __packed;
+
+struct ieee80211_vht_cap {
+ __le32 vht_cap_info;
+ struct ieee80211_vht_mcs_info supp_mcs;
+};
+#define WLAN_EID_VHT_CAPABILITY 191
+
+struct ieee80211_sta_vht_cap {
+ bool vht_supported;
+ u32 cap; /* use IEEE80211_VHT_CAP_ */
+ struct ieee80211_vht_mcs_info vht_mcs;
+};
+#endif
+#endif // LMAC_MAC_H_
diff --git a/drivers/net/wireless/aic8800/aic8800_fdrv/lmac_msg.h b/drivers/net/wireless/aic8800/aic8800_fdrv/lmac_msg.h
new file mode 100644
index 000000000000..5367dc61d947
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic8800_fdrv/lmac_msg.h
@@ -0,0 +1,3023 @@
+/**
+ ****************************************************************************************
+ *
+ * @file lmac_msg.h
+ *
+ * @brief Main definitions for message exchanges with LMAC
+ *
+ * Copyright (C) RivieraWaves 2011-2019
+ *
+ ****************************************************************************************
+ */
+
+#ifndef LMAC_MSG_H_
+#define LMAC_MSG_H_
+
+/*
+ * INCLUDE FILES
+ ****************************************************************************************
+ */
+// for MAC related elements (mac_addr, mac_ssid...)
+#include "lmac_mac.h"
+
+/*
+ ****************************************************************************************
+ */
+/////////////////////////////////////////////////////////////////////////////////
+// COMMUNICATION WITH LMAC LAYER
+/////////////////////////////////////////////////////////////////////////////////
+/* Task identifiers for communication between LMAC and DRIVER */
+enum
+{
+ TASK_NONE = (u8_l) -1,
+
+ // MAC Management task.
+ TASK_MM = 0,
+ // DEBUG task
+ TASK_DBG,
+ /// SCAN task
+ TASK_SCAN,
+ /// TDLS task
+ TASK_TDLS,
+ /// SCANU task
+ TASK_SCANU,
+ /// ME task
+ TASK_ME,
+ /// SM task
+ TASK_SM,
+ /// APM task
+ TASK_APM,
+ /// BAM task
+ TASK_BAM,
+ /// MESH task
+ TASK_MESH,
+ /// RXU task
+ TASK_RXU,
+ /// RM task
+ TASK_RM,
+#if defined CONFIG_RWNX_FULLMAC || defined CONFIG_RWNX_FHOST
+ // This is used to define the last task that is running on the EMB processor
+ TASK_LAST_EMB = TASK_RM,
+#else
+#error "Need to define SOFTMAC or FULLMAC"
+#endif
+ // nX API task
+ TASK_API,
+ TASK_MAX,
+};
+
+
+/// For MAC HW States copied from "hal_machw.h"
+enum
+{
+ /// MAC HW IDLE State.
+ HW_IDLE = 0,
+ /// MAC HW RESERVED State.
+ HW_RESERVED,
+ /// MAC HW DOZE State.
+ HW_DOZE,
+ /// MAC HW ACTIVE State.
+ HW_ACTIVE
+};
+
+/// Power Save mode setting
+enum mm_ps_mode_state
+{
+ MM_PS_MODE_OFF,
+ MM_PS_MODE_ON,
+ MM_PS_MODE_ON_DYN,
+};
+
+/// Status/error codes used in the MAC software.
+enum
+{
+ CO_OK,
+ CO_FAIL,
+ CO_EMPTY,
+ CO_FULL,
+ CO_BAD_PARAM,
+ CO_NOT_FOUND,
+ CO_NO_MORE_ELT_AVAILABLE,
+ CO_NO_ELT_IN_USE,
+ CO_BUSY,
+ CO_OP_IN_PROGRESS,
+};
+
+/// Remain on channel operation codes
+enum mm_remain_on_channel_op
+{
+ MM_ROC_OP_START = 0,
+ MM_ROC_OP_CANCEL,
+};
+
+#define DRV_TASK_ID 100
+
+/// Message Identifier. The number of messages is limited to 0xFFFF.
+/// The message ID is divided in two parts:
+/// - bits[15..10] : task index (no more than 64 tasks supported).
+/// - bits[9..0] : message index (no more that 1024 messages per task).
+typedef u16 lmac_msg_id_t;
+
+typedef u16 lmac_task_id_t;
+
+/// Build the first message ID of a task.
+#define LMAC_FIRST_MSG(task) ((lmac_msg_id_t)((task) << 10))
+
+#define MSG_T(msg) ((lmac_task_id_t)((msg) >> 10))
+#define MSG_I(msg) ((msg) & ((1<<10)-1))
+
+/// Message structure.
+struct lmac_msg
+{
+ lmac_msg_id_t id; ///< Message id.
+ lmac_task_id_t dest_id; ///< Destination kernel identifier.
+ lmac_task_id_t src_id; ///< Source kernel identifier.
+ u16 param_len; ///< Parameter embedded struct length.
+ u32 param[]; ///< Parameter embedded struct. Must be word-aligned.
+};
+
+/// List of messages related to the task.
+enum mm_msg_tag
+{
+ /// RESET Request.
+ MM_RESET_REQ = LMAC_FIRST_MSG(TASK_MM),
+ /// RESET Confirmation.
+ MM_RESET_CFM,
+ /// START Request.
+ MM_START_REQ,
+ /// START Confirmation.
+ MM_START_CFM,
+ /// Read Version Request.
+ MM_VERSION_REQ,
+ /// Read Version Confirmation.
+ MM_VERSION_CFM,
+ /// ADD INTERFACE Request.
+ MM_ADD_IF_REQ,
+ /// ADD INTERFACE Confirmation.
+ MM_ADD_IF_CFM,
+ /// REMOVE INTERFACE Request.
+ MM_REMOVE_IF_REQ,
+ /// REMOVE INTERFACE Confirmation.
+ MM_REMOVE_IF_CFM,
+ /// STA ADD Request.
+ MM_STA_ADD_REQ,
+ /// STA ADD Confirm.
+ MM_STA_ADD_CFM,
+ /// STA DEL Request.
+ MM_STA_DEL_REQ,
+ /// STA DEL Confirm.
+ MM_STA_DEL_CFM,
+ /// RX FILTER CONFIGURATION Request.
+ MM_SET_FILTER_REQ,
+ /// RX FILTER CONFIGURATION Confirmation.
+ MM_SET_FILTER_CFM,
+ /// CHANNEL CONFIGURATION Request.
+ MM_SET_CHANNEL_REQ,
+ /// CHANNEL CONFIGURATION Confirmation.
+ MM_SET_CHANNEL_CFM,
+ /// DTIM PERIOD CONFIGURATION Request.
+ MM_SET_DTIM_REQ,
+ /// DTIM PERIOD CONFIGURATION Confirmation.
+ MM_SET_DTIM_CFM,
+ /// BEACON INTERVAL CONFIGURATION Request.
+ MM_SET_BEACON_INT_REQ,
+ /// BEACON INTERVAL CONFIGURATION Confirmation.
+ MM_SET_BEACON_INT_CFM,
+ /// BASIC RATES CONFIGURATION Request.
+ MM_SET_BASIC_RATES_REQ,
+ /// BASIC RATES CONFIGURATION Confirmation.
+ MM_SET_BASIC_RATES_CFM,
+ /// BSSID CONFIGURATION Request.
+ MM_SET_BSSID_REQ,
+ /// BSSID CONFIGURATION Confirmation.
+ MM_SET_BSSID_CFM,
+ /// EDCA PARAMETERS CONFIGURATION Request.
+ MM_SET_EDCA_REQ,
+ /// EDCA PARAMETERS CONFIGURATION Confirmation.
+ MM_SET_EDCA_CFM,
+ /// ABGN MODE CONFIGURATION Request.
+ MM_SET_MODE_REQ,
+ /// ABGN MODE CONFIGURATION Confirmation.
+ MM_SET_MODE_CFM,
+ /// Request setting the VIF active state (i.e associated or AP started)
+ MM_SET_VIF_STATE_REQ,
+ /// Confirmation of the @ref MM_SET_VIF_STATE_REQ message.
+ MM_SET_VIF_STATE_CFM,
+ /// SLOT TIME PARAMETERS CONFIGURATION Request.
+ MM_SET_SLOTTIME_REQ,
+ /// SLOT TIME PARAMETERS CONFIGURATION Confirmation.
+ MM_SET_SLOTTIME_CFM,
+ /// Power Mode Change Request.
+ MM_SET_IDLE_REQ,
+ /// Power Mode Change Confirm.
+ MM_SET_IDLE_CFM,
+ /// KEY ADD Request.
+ MM_KEY_ADD_REQ,
+ /// KEY ADD Confirm.
+ MM_KEY_ADD_CFM,
+ /// KEY DEL Request.
+ MM_KEY_DEL_REQ,
+ /// KEY DEL Confirm.
+ MM_KEY_DEL_CFM,
+ /// Block Ack agreement info addition
+ MM_BA_ADD_REQ,
+ /// Block Ack agreement info addition confirmation
+ MM_BA_ADD_CFM,
+ /// Block Ack agreement info deletion
+ MM_BA_DEL_REQ,
+ /// Block Ack agreement info deletion confirmation
+ MM_BA_DEL_CFM,
+ /// Indication of the primary TBTT to the upper MAC. Upon the reception of this
+ // message the upper MAC has to push the beacon(s) to the beacon transmission queue.
+ MM_PRIMARY_TBTT_IND,
+ /// Indication of the secondary TBTT to the upper MAC. Upon the reception of this
+ // message the upper MAC has to push the beacon(s) to the beacon transmission queue.
+ MM_SECONDARY_TBTT_IND,
+ /// Request for changing the TX power
+ MM_SET_POWER_REQ,
+ /// Confirmation of the TX power change
+ MM_SET_POWER_CFM,
+ /// Request to the LMAC to trigger the embedded logic analyzer and forward the debug
+ /// dump.
+ MM_DBG_TRIGGER_REQ,
+ /// Set Power Save mode
+ MM_SET_PS_MODE_REQ,
+ /// Set Power Save mode confirmation
+ MM_SET_PS_MODE_CFM,
+ /// Request to add a channel context
+ MM_CHAN_CTXT_ADD_REQ,
+ /// Confirmation of the channel context addition
+ MM_CHAN_CTXT_ADD_CFM,
+ /// Request to delete a channel context
+ MM_CHAN_CTXT_DEL_REQ,
+ /// Confirmation of the channel context deletion
+ MM_CHAN_CTXT_DEL_CFM,
+ /// Request to link a channel context to a VIF
+ MM_CHAN_CTXT_LINK_REQ,
+ /// Confirmation of the channel context link
+ MM_CHAN_CTXT_LINK_CFM,
+ /// Request to unlink a channel context from a VIF
+ MM_CHAN_CTXT_UNLINK_REQ,
+ /// Confirmation of the channel context unlink
+ MM_CHAN_CTXT_UNLINK_CFM,
+ /// Request to update a channel context
+ MM_CHAN_CTXT_UPDATE_REQ,
+ /// Confirmation of the channel context update
+ MM_CHAN_CTXT_UPDATE_CFM,
+ /// Request to schedule a channel context
+ MM_CHAN_CTXT_SCHED_REQ,
+ /// Confirmation of the channel context scheduling
+ MM_CHAN_CTXT_SCHED_CFM,
+ /// Request to change the beacon template in LMAC
+ MM_BCN_CHANGE_REQ,
+ /// Confirmation of the beacon change
+ MM_BCN_CHANGE_CFM,
+ /// Request to update the TIM in the beacon (i.e to indicate traffic bufferized at AP)
+ MM_TIM_UPDATE_REQ,
+ /// Confirmation of the TIM update
+ MM_TIM_UPDATE_CFM,
+ /// Connection loss indication
+ MM_CONNECTION_LOSS_IND,
+ /// Channel context switch indication to the upper layers
+ MM_CHANNEL_SWITCH_IND,
+ /// Channel context pre-switch indication to the upper layers
+ MM_CHANNEL_PRE_SWITCH_IND,
+ /// Request to remain on channel or cancel remain on channel
+ MM_REMAIN_ON_CHANNEL_REQ,
+ /// Confirmation of the (cancel) remain on channel request
+ MM_REMAIN_ON_CHANNEL_CFM,
+ /// Remain on channel expired indication
+ MM_REMAIN_ON_CHANNEL_EXP_IND,
+ /// Indication of a PS state change of a peer device
+ MM_PS_CHANGE_IND,
+ /// Indication that some buffered traffic should be sent to the peer device
+ MM_TRAFFIC_REQ_IND,
+ /// Request to modify the STA Power-save mode options
+ MM_SET_PS_OPTIONS_REQ,
+ /// Confirmation of the PS options setting
+ MM_SET_PS_OPTIONS_CFM,
+ /// Indication of PS state change for a P2P VIF
+ MM_P2P_VIF_PS_CHANGE_IND,
+ /// Indication that CSA counter has been updated
+ MM_CSA_COUNTER_IND,
+ /// Channel occupation report indication
+ MM_CHANNEL_SURVEY_IND,
+ /// Message containing Beamformer Information
+ MM_BFMER_ENABLE_REQ,
+ /// Request to Start/Stop/Update NOA - GO Only
+ MM_SET_P2P_NOA_REQ,
+ /// Request to Start/Stop/Update Opportunistic PS - GO Only
+ MM_SET_P2P_OPPPS_REQ,
+ /// Start/Stop/Update NOA Confirmation
+ MM_SET_P2P_NOA_CFM,
+ /// Start/Stop/Update Opportunistic PS Confirmation
+ MM_SET_P2P_OPPPS_CFM,
+ /// P2P NoA Update Indication - GO Only
+ MM_P2P_NOA_UPD_IND,
+ /// Request to set RSSI threshold and RSSI hysteresis
+ MM_CFG_RSSI_REQ,
+ /// Indication that RSSI level is below or above the threshold
+ MM_RSSI_STATUS_IND,
+ /// Indication that CSA is done
+ MM_CSA_FINISH_IND,
+ /// Indication that CSA is in prorgess (resp. done) and traffic must be stopped (resp. restarted)
+ MM_CSA_TRAFFIC_IND,
+ /// Request to update the group information of a station
+ MM_MU_GROUP_UPDATE_REQ,
+ /// Confirmation of the @ref MM_MU_GROUP_UPDATE_REQ message
+ MM_MU_GROUP_UPDATE_CFM,
+ /// Request to initialize the antenna diversity algorithm
+ MM_ANT_DIV_INIT_REQ,
+ /// Request to stop the antenna diversity algorithm
+ MM_ANT_DIV_STOP_REQ,
+ /// Request to update the antenna switch status
+ MM_ANT_DIV_UPDATE_REQ,
+ /// Request to switch the antenna connected to path_0
+ MM_SWITCH_ANTENNA_REQ,
+ /// Indication that a packet loss has occurred
+ MM_PKTLOSS_IND,
+
+ MM_SET_ARPOFFLOAD_REQ,
+ MM_SET_ARPOFFLOAD_CFM,
+ MM_SET_AGG_DISABLE_REQ,
+ MM_SET_AGG_DISABLE_CFM,
+ MM_SET_COEX_REQ,
+ MM_SET_COEX_CFM,
+ MM_SET_RF_CONFIG_REQ,
+ MM_SET_RF_CONFIG_CFM,
+ MM_SET_RF_CALIB_REQ,
+ MM_SET_RF_CALIB_CFM,
+
+ /// MU EDCA PARAMETERS Configuration Request.
+ MM_SET_MU_EDCA_REQ,
+ /// MU EDCA PARAMETERS Configuration Confirmation.
+ MM_SET_MU_EDCA_CFM,
+ /// UORA PARAMETERS Configuration Request.
+ MM_SET_UORA_REQ,
+ /// UORA PARAMETERS Configuration Confirmation.
+ MM_SET_UORA_CFM,
+ /// TXOP RTS THRESHOLD Configuration Request.
+ MM_SET_TXOP_RTS_THRES_REQ,
+ /// TXOP RTS THRESHOLD Configuration Confirmation.
+ MM_SET_TXOP_RTS_THRES_CFM,
+ /// HE BSS Color Configuration Request.
+ MM_SET_BSS_COLOR_REQ,
+ /// HE BSS Color Configuration Confirmation.
+ MM_SET_BSS_COLOR_CFM,
+
+ MM_GET_MAC_ADDR_REQ,
+ MM_GET_MAC_ADDR_CFM,
+
+ MM_GET_STA_INFO_REQ,
+ MM_GET_STA_INFO_CFM,
+
+ MM_SET_TXPWR_IDX_LVL_REQ,
+ MM_SET_TXPWR_IDX_LVL_CFM,
+
+ MM_SET_TXPWR_OFST_REQ,
+ MM_SET_TXPWR_OFST_CFM,
+
+ MM_SET_STACK_START_REQ,
+ MM_SET_STACK_START_CFM,
+
+ MM_APM_STALOSS_IND,
+
+ MM_SET_VENDOR_HWCONFIG_REQ,
+ MM_SET_VENDOR_HWCONFIG_CFM,
+
+ MM_GET_FW_VERSION_REQ,
+ MM_GET_FW_VERSION_CFM,
+
+ /// MAX number of messages
+ MM_MAX,
+};
+
+/// Interface types
+enum
+{
+ /// ESS STA interface
+ MM_STA,
+ /// IBSS STA interface
+ MM_IBSS,
+ /// AP interface
+ MM_AP,
+ // Mesh Point interface
+ MM_MESH_POINT,
+ // Monitor interface
+ MM_MONITOR,
+};
+
+///BA agreement types
+enum
+{
+ ///BlockAck agreement for TX
+ BA_AGMT_TX,
+ ///BlockAck agreement for RX
+ BA_AGMT_RX,
+};
+
+///BA agreement related status
+enum
+{
+ ///Correct BA agreement establishment
+ BA_AGMT_ESTABLISHED,
+ ///BA agreement already exists for STA+TID requested, cannot override it (should have been deleted first)
+ BA_AGMT_ALREADY_EXISTS,
+ ///Correct BA agreement deletion
+ BA_AGMT_DELETED,
+ ///BA agreement for the (STA, TID) doesn't exist so nothing to delete
+ BA_AGMT_DOESNT_EXIST,
+};
+
+/// Features supported by LMAC - Positions
+enum mm_features
+{
+ /// Beaconing
+ MM_FEAT_BCN_BIT = 0,
+ /// Autonomous Beacon Transmission
+ MM_FEAT_AUTOBCN_BIT,
+ /// Scan in LMAC
+ MM_FEAT_HWSCAN_BIT,
+ /// Connection Monitoring
+ MM_FEAT_CMON_BIT,
+ /// Multi Role
+ MM_FEAT_MROLE_BIT,
+ /// Radar Detection
+ MM_FEAT_RADAR_BIT,
+ /// Power Save
+ MM_FEAT_PS_BIT,
+ /// UAPSD
+ MM_FEAT_UAPSD_BIT,
+ /// DPSM
+ MM_FEAT_DPSM_BIT,
+ /// A-MPDU
+ MM_FEAT_AMPDU_BIT,
+ /// A-MSDU
+ MM_FEAT_AMSDU_BIT,
+ /// Channel Context
+ MM_FEAT_CHNL_CTXT_BIT,
+ /// Packet reordering
+ MM_FEAT_REORD_BIT,
+ /// P2P
+ MM_FEAT_P2P_BIT,
+ /// P2P Go
+ MM_FEAT_P2P_GO_BIT,
+ /// UMAC Present
+ MM_FEAT_UMAC_BIT,
+ /// VHT support
+ MM_FEAT_VHT_BIT,
+ /// Beamformee
+ MM_FEAT_BFMEE_BIT,
+ /// Beamformer
+ MM_FEAT_BFMER_BIT,
+ /// WAPI
+ MM_FEAT_WAPI_BIT,
+ /// MFP
+ MM_FEAT_MFP_BIT,
+ /// Mu-MIMO RX support
+ MM_FEAT_MU_MIMO_RX_BIT,
+ /// Mu-MIMO TX support
+ MM_FEAT_MU_MIMO_TX_BIT,
+ /// Wireless Mesh Networking
+ MM_FEAT_MESH_BIT,
+ /// TDLS support
+ MM_FEAT_TDLS_BIT,
+ /// Antenna Diversity support
+ MM_FEAT_ANT_DIV_BIT,
+ /// UF support
+ MM_FEAT_UF_BIT,
+ /// A-MSDU maximum size (bit0)
+ MM_AMSDU_MAX_SIZE_BIT0,
+ /// A-MSDU maximum size (bit1)
+ MM_AMSDU_MAX_SIZE_BIT1,
+ /// MON_DATA support
+ MM_FEAT_MON_DATA_BIT,
+ /// HE (802.11ax) support
+ MM_FEAT_HE_BIT,
+};
+
+/// Maximum number of words in the configuration buffer
+#define PHY_CFG_BUF_SIZE 16
+
+/// Structure containing the parameters of the PHY configuration
+struct phy_cfg_tag
+{
+ /// Buffer containing the parameters specific for the PHY used
+ u32_l parameters[PHY_CFG_BUF_SIZE];
+};
+
+/// Structure containing the parameters of the Trident PHY configuration
+struct phy_trd_cfg_tag
+{
+ /// MDM type(nxm)(upper nibble) and MDM2RF path mapping(lower nibble)
+ u8_l path_mapping;
+ /// TX DC offset compensation
+ u32_l tx_dc_off_comp;
+};
+
+/// Structure containing the parameters of the Karst PHY configuration
+struct phy_karst_cfg_tag
+{
+ /// TX IQ mismatch compensation in 2.4GHz
+ u32_l tx_iq_comp_2_4G[2];
+ /// RX IQ mismatch compensation in 2.4GHz
+ u32_l rx_iq_comp_2_4G[2];
+ /// TX IQ mismatch compensation in 5GHz
+ u32_l tx_iq_comp_5G[2];
+ /// RX IQ mismatch compensation in 5GHz
+ u32_l rx_iq_comp_5G[2];
+ /// RF path used by default (0 or 1)
+ u8_l path_used;
+};
+
+/// Structure containing the parameters of the @ref MM_START_REQ message
+struct mm_start_req
+{
+ /// PHY configuration
+ struct phy_cfg_tag phy_cfg;
+ /// UAPSD timeout
+ u32_l uapsd_timeout;
+ /// Local LP clock accuracy (in ppm)
+ u16_l lp_clk_accuracy;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_CHANNEL_REQ message
+struct mm_set_channel_req
+{
+ /// Channel information
+ struct mac_chan_op chan;
+ /// Index of the RF for which the channel has to be set (0: operating (primary), 1: secondary
+ /// RF (used for additional radar detection). This parameter is reserved if no secondary RF
+ /// is available in the system
+ u8_l index;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_CHANNEL_CFM message
+struct mm_set_channel_cfm
+{
+ /// Radio index to be used in policy table
+ u8_l radio_idx;
+ /// TX power configured (in dBm)
+ s8_l power;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_DTIM_REQ message
+struct mm_set_dtim_req
+{
+ /// DTIM period
+ u8_l dtim_period;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_POWER_REQ message
+struct mm_set_power_req
+{
+ /// Index of the interface for which the parameter is configured
+ u8_l inst_nbr;
+ /// TX power (in dBm)
+ s8_l power;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_POWER_CFM message
+struct mm_set_power_cfm
+{
+ /// Radio index to be used in policy table
+ u8_l radio_idx;
+ /// TX power configured (in dBm)
+ s8_l power;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_BEACON_INT_REQ message
+struct mm_set_beacon_int_req
+{
+ /// Beacon interval
+ u16_l beacon_int;
+ /// Index of the interface for which the parameter is configured
+ u8_l inst_nbr;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_BASIC_RATES_REQ message
+struct mm_set_basic_rates_req
+{
+ /// Basic rate set (as expected by bssBasicRateSet field of Rates MAC HW register)
+ u32_l rates;
+ /// Index of the interface for which the parameter is configured
+ u8_l inst_nbr;
+ /// Band on which the interface will operate
+ u8_l band;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_BSSID_REQ message
+struct mm_set_bssid_req
+{
+ /// BSSID to be configured in HW
+ struct mac_addr bssid;
+ /// Index of the interface for which the parameter is configured
+ u8_l inst_nbr;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_FILTER_REQ message
+struct mm_set_filter_req
+{
+ /// RX filter to be put into rxCntrlReg HW register
+ u32_l filter;
+};
+
+/// Structure containing the parameters of the @ref MM_ADD_IF_REQ message.
+struct mm_add_if_req
+{
+ /// Type of the interface (AP, STA, ADHOC, ...)
+ u8_l type;
+ /// MAC ADDR of the interface to start
+ struct mac_addr addr;
+ /// P2P Interface
+ bool_l p2p;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_EDCA_REQ message
+struct mm_set_edca_req
+{
+ /// EDCA parameters of the queue (as expected by edcaACxReg HW register)
+ u32_l ac_param;
+ /// Flag indicating if UAPSD can be used on this queue
+ bool_l uapsd;
+ /// HW queue for which the parameters are configured
+ u8_l hw_queue;
+ /// Index of the interface for which the parameters are configured
+ u8_l inst_nbr;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_MU_EDCA_REQ message
+struct mm_set_mu_edca_req
+{
+ /// MU EDCA parameters of the different HE queues
+ u32_l param[AC_MAX];
+};
+
+/// Structure containing the parameters of the @ref MM_SET_UORA_REQ message
+struct mm_set_uora_req
+{
+ /// Minimum exponent of OFDMA Contention Window.
+ u8_l eocw_min;
+ /// Maximum exponent of OFDMA Contention Window.
+ u8_l eocw_max;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_TXOP_RTS_THRES_REQ message
+struct mm_set_txop_rts_thres_req
+{
+ /// TXOP RTS threshold
+ u16_l txop_dur_rts_thres;
+ /// Index of the interface for which the parameter is configured
+ u8_l inst_nbr;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_BSS_COLOR_REQ message
+struct mm_set_bss_color_req
+{
+ /// HE BSS color, formatted as per BSS_COLOR MAC HW register
+ u32_l bss_color;
+};
+
+struct mm_set_idle_req
+{
+ u8_l hw_idle;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_SLOTTIME_REQ message
+struct mm_set_slottime_req
+{
+ /// Slot time expressed in us
+ u8_l slottime;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_MODE_REQ message
+struct mm_set_mode_req
+{
+ /// abgnMode field of macCntrl1Reg register
+ u8_l abgnmode;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_VIF_STATE_REQ message
+struct mm_set_vif_state_req
+{
+ /// Association Id received from the AP (valid only if the VIF is of STA type)
+ u16_l aid;
+ /// Flag indicating if the VIF is active or not
+ bool_l active;
+ /// Interface index
+ u8_l inst_nbr;
+};
+
+/// Structure containing the parameters of the @ref MM_ADD_IF_CFM message.
+struct mm_add_if_cfm
+{
+ /// Status of operation (different from 0 if unsuccessful)
+ u8_l status;
+ /// Interface index assigned by the LMAC
+ u8_l inst_nbr;
+};
+
+/// Structure containing the parameters of the @ref MM_REMOVE_IF_REQ message.
+struct mm_remove_if_req
+{
+ /// Interface index assigned by the LMAC
+ u8_l inst_nbr;
+};
+
+/// Structure containing the parameters of the @ref MM_VERSION_CFM message.
+struct mm_version_cfm
+{
+ /// Version of the LMAC FW
+ u32_l version_lmac;
+ /// Version1 of the MAC HW (as encoded in version1Reg MAC HW register)
+ u32_l version_machw_1;
+ /// Version2 of the MAC HW (as encoded in version2Reg MAC HW register)
+ u32_l version_machw_2;
+ /// Version1 of the PHY (depends on actual PHY)
+ u32_l version_phy_1;
+ /// Version2 of the PHY (depends on actual PHY)
+ u32_l version_phy_2;
+ /// Supported Features
+ u32_l features;
+ /// Maximum number of supported stations
+ u16_l max_sta_nb;
+ /// Maximum number of supported virtual interfaces
+ u8_l max_vif_nb;
+};
+
+/// Structure containing the parameters of the @ref MM_STA_ADD_REQ message.
+struct mm_sta_add_req
+{
+ /// Maximum A-MPDU size, in bytes, for HE frames
+ u32_l ampdu_size_max_he;
+ /// Maximum A-MPDU size, in bytes, for VHT frames
+ u32_l ampdu_size_max_vht;
+ /// PAID/GID
+ u32_l paid_gid;
+ /// Maximum A-MPDU size, in bytes, for HT frames
+ u16_l ampdu_size_max_ht;
+ /// MAC address of the station to be added
+ struct mac_addr mac_addr;
+ /// A-MPDU spacing, in us
+ u8_l ampdu_spacing_min;
+ /// Interface index
+ u8_l inst_nbr;
+ /// TDLS station
+ bool_l tdls_sta;
+ /// Indicate if the station is TDLS link initiator station
+ bool_l tdls_sta_initiator;
+ /// Indicate if the TDLS Channel Switch is allowed
+ bool_l tdls_chsw_allowed;
+};
+
+/// Structure containing the parameters of the @ref MM_STA_ADD_CFM message.
+struct mm_sta_add_cfm
+{
+ /// Status of the operation (different from 0 if unsuccessful)
+ u8_l status;
+ /// Index assigned by the LMAC to the newly added station
+ u8_l sta_idx;
+ /// MAC HW index of the newly added station
+ u8_l hw_sta_idx;
+};
+
+/// Structure containing the parameters of the @ref MM_STA_DEL_REQ message.
+struct mm_sta_del_req
+{
+ /// Index of the station to be deleted
+ u8_l sta_idx;
+};
+
+/// Structure containing the parameters of the @ref MM_STA_DEL_CFM message.
+struct mm_sta_del_cfm
+{
+ /// Status of the operation (different from 0 if unsuccessful)
+ u8_l status;
+};
+
+/// Structure containing the parameters of the SET_POWER_MODE REQ message.
+struct mm_setpowermode_req
+{
+ u8_l mode;
+ u8_l sta_idx;
+};
+
+/// Structure containing the parameters of the SET_POWER_MODE CFM message.
+struct mm_setpowermode_cfm
+{
+ u8_l status;
+};
+
+/// Structure containing the parameters of the @ref MM_KEY_ADD REQ message.
+struct mm_key_add_req
+{
+ /// Key index (valid only for default keys)
+ u8_l key_idx;
+ /// STA index (valid only for pairwise or mesh group keys)
+ u8_l sta_idx;
+ /// Key material
+ struct mac_sec_key key;
+ /// Cipher suite (WEP64, WEP128, TKIP, CCMP)
+ u8_l cipher_suite;
+ /// Index of the interface for which the key is set (valid only for default keys or mesh group keys)
+ u8_l inst_nbr;
+ /// A-MSDU SPP parameter
+ u8_l spp;
+ /// Indicate if provided key is a pairwise key or not
+ bool_l pairwise;
+};
+
+/// Structure containing the parameters of the @ref MM_KEY_ADD_CFM message.
+struct mm_key_add_cfm
+{
+ /// Status of the operation (different from 0 if unsuccessful)
+ u8_l status;
+ /// HW index of the key just added
+ u8_l hw_key_idx;
+};
+
+/// Structure containing the parameters of the @ref MM_KEY_DEL_REQ message.
+struct mm_key_del_req
+{
+ /// HW index of the key to be deleted
+ u8_l hw_key_idx;
+};
+
+/// Structure containing the parameters of the @ref MM_BA_ADD_REQ message.
+struct mm_ba_add_req
+{
+ ///Type of agreement (0: TX, 1: RX)
+ u8_l type;
+ ///Index of peer station with which the agreement is made
+ u8_l sta_idx;
+ ///TID for which the agreement is made with peer station
+ u8_l tid;
+ ///Buffer size - number of MPDUs that can be held in its buffer per TID
+ u8_l bufsz;
+ /// Start sequence number negotiated during BA setup - the one in first aggregated MPDU counts more
+ u16_l ssn;
+};
+
+/// Structure containing the parameters of the @ref MM_BA_ADD_CFM message.
+struct mm_ba_add_cfm
+{
+ ///Index of peer station for which the agreement is being confirmed
+ u8_l sta_idx;
+ ///TID for which the agreement is being confirmed
+ u8_l tid;
+ /// Status of ba establishment
+ u8_l status;
+};
+
+/// Structure containing the parameters of the @ref MM_BA_DEL_REQ message.
+struct mm_ba_del_req
+{
+ ///Type of agreement (0: TX, 1: RX)
+ u8_l type;
+ ///Index of peer station for which the agreement is being deleted
+ u8_l sta_idx;
+ ///TID for which the agreement is being deleted
+ u8_l tid;
+};
+
+/// Structure containing the parameters of the @ref MM_BA_DEL_CFM message.
+struct mm_ba_del_cfm
+{
+ ///Index of peer station for which the agreement deletion is being confirmed
+ u8_l sta_idx;
+ ///TID for which the agreement deletion is being confirmed
+ u8_l tid;
+ /// Status of ba deletion
+ u8_l status;
+};
+
+/// Structure containing the parameters of the @ref MM_CHAN_CTXT_ADD_REQ message
+struct mm_chan_ctxt_add_req
+{
+ /// Operating channel
+ struct mac_chan_op chan;
+};
+
+/// Structure containing the parameters of the @ref MM_CHAN_CTXT_ADD_REQ message
+struct mm_chan_ctxt_add_cfm
+{
+ /// Status of the addition
+ u8_l status;
+ /// Index of the new channel context
+ u8_l index;
+};
+
+
+/// Structure containing the parameters of the @ref MM_CHAN_CTXT_DEL_REQ message
+struct mm_chan_ctxt_del_req
+{
+ /// Index of the new channel context to be deleted
+ u8_l index;
+};
+
+
+/// Structure containing the parameters of the @ref MM_CHAN_CTXT_LINK_REQ message
+struct mm_chan_ctxt_link_req
+{
+ /// VIF index
+ u8_l vif_index;
+ /// Channel context index
+ u8_l chan_index;
+ /// Indicate if this is a channel switch (unlink current ctx first if true)
+ u8_l chan_switch;
+};
+
+/// Structure containing the parameters of the @ref MM_CHAN_CTXT_UNLINK_REQ message
+struct mm_chan_ctxt_unlink_req
+{
+ /// VIF index
+ u8_l vif_index;
+};
+
+/// Structure containing the parameters of the @ref MM_CHAN_CTXT_UPDATE_REQ message
+struct mm_chan_ctxt_update_req
+{
+ /// Channel context index
+ u8_l chan_index;
+ /// New channel information
+ struct mac_chan_op chan;
+};
+
+/// Structure containing the parameters of the @ref MM_CHAN_CTXT_SCHED_REQ message
+struct mm_chan_ctxt_sched_req
+{
+ /// VIF index
+ u8_l vif_index;
+ /// Channel context index
+ u8_l chan_index;
+ /// Type of the scheduling request (0: normal scheduling, 1: derogatory
+ /// scheduling)
+ u8_l type;
+};
+
+/// Structure containing the parameters of the @ref MM_CHANNEL_SWITCH_IND message
+struct mm_channel_switch_ind
+{
+ /// Index of the channel context we will switch to
+ u8_l chan_index;
+ /// Indicate if the switch has been triggered by a Remain on channel request
+ bool_l roc;
+ /// VIF on which remain on channel operation has been started (if roc == 1)
+ u8_l vif_index;
+ /// Indicate if the switch has been triggered by a TDLS Remain on channel request
+ bool_l roc_tdls;
+};
+
+/// Structure containing the parameters of the @ref MM_CHANNEL_PRE_SWITCH_IND message
+struct mm_channel_pre_switch_ind
+{
+ /// Index of the channel context we will switch to
+ u8_l chan_index;
+};
+
+/// Structure containing the parameters of the @ref MM_CONNECTION_LOSS_IND message.
+struct mm_connection_loss_ind
+{
+ /// VIF instance number
+ u8_l inst_nbr;
+};
+
+
+/// Structure containing the parameters of the @ref MM_DBG_TRIGGER_REQ message.
+struct mm_dbg_trigger_req
+{
+ /// Error trace to be reported by the LMAC
+ char error[64];
+};
+
+/// Structure containing the parameters of the @ref MM_SET_PS_MODE_REQ message.
+struct mm_set_ps_mode_req
+{
+ /// Power Save is activated or deactivated
+ u8_l new_state;
+};
+
+/// Structure containing the parameters of the @ref MM_BCN_CHANGE_REQ message.
+#define BCN_MAX_CSA_CPT 2
+struct mm_bcn_change_req
+{
+ /// Pointer, in host memory, to the new beacon template
+ u32_l bcn_ptr;
+ /// Length of the beacon template
+ u16_l bcn_len;
+ /// Offset of the TIM IE in the beacon
+ u16_l tim_oft;
+ /// Length of the TIM IE
+ u8_l tim_len;
+ /// Index of the VIF for which the beacon is updated
+ u8_l inst_nbr;
+ /// Offset of CSA (channel switch announcement) counters (0 means no counter)
+ u8_l csa_oft[BCN_MAX_CSA_CPT];
+};
+
+
+/// Structure containing the parameters of the @ref MM_TIM_UPDATE_REQ message.
+struct mm_tim_update_req
+{
+ /// Association ID of the STA the bit of which has to be updated (0 for BC/MC traffic)
+ u16_l aid;
+ /// Flag indicating the availability of data packets for the given STA
+ u8_l tx_avail;
+ /// Index of the VIF for which the TIM is updated
+ u8_l inst_nbr;
+};
+
+/// Structure containing the parameters of the @ref MM_REMAIN_ON_CHANNEL_REQ message.
+struct mm_remain_on_channel_req
+{
+ /// Operation Code
+ u8_l op_code;
+ /// VIF Index
+ u8_l vif_index;
+ /// Band (2.4GHz or 5GHz)
+ u8_l band;
+ /// Channel type: 20,40,80,160 or 80+80 MHz
+ u8_l type;
+ /// Frequency for Primary 20MHz channel (in MHz)
+ u16_l prim20_freq;
+ /// Frequency for Center of the contiguous channel or center of Primary 80+80
+ u16_l center1_freq;
+ /// Frequency for Center of the non-contiguous secondary 80+80
+ u16_l center2_freq;
+ /// Duration (in ms)
+ u32_l duration_ms;
+ /// TX power (in dBm)
+ s8_l tx_power;
+};
+
+/// Structure containing the parameters of the @ref MM_REMAIN_ON_CHANNEL_CFM message
+struct mm_remain_on_channel_cfm
+{
+ /// Operation Code
+ u8_l op_code;
+ /// Status of the operation
+ u8_l status;
+ /// Channel Context index
+ u8_l chan_ctxt_index;
+};
+
+/// Structure containing the parameters of the @ref MM_REMAIN_ON_CHANNEL_EXP_IND message
+struct mm_remain_on_channel_exp_ind
+{
+ /// VIF Index
+ u8_l vif_index;
+ /// Channel Context index
+ u8_l chan_ctxt_index;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_UAPSD_TMR_REQ message.
+struct mm_set_uapsd_tmr_req
+{
+ /// action: Start or Stop the timer
+ u8_l action;
+ /// timeout value, in milliseconds
+ u32_l timeout;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_UAPSD_TMR_CFM message.
+struct mm_set_uapsd_tmr_cfm
+{
+ /// Status of the operation (different from 0 if unsuccessful)
+ u8_l status;
+};
+
+
+/// Structure containing the parameters of the @ref MM_PS_CHANGE_IND message
+struct mm_ps_change_ind
+{
+ /// Index of the peer device that is switching its PS state
+ u8_l sta_idx;
+ /// New PS state of the peer device (0: active, 1: sleeping)
+ u8_l ps_state;
+};
+
+/// Structure containing the parameters of the @ref MM_P2P_VIF_PS_CHANGE_IND message
+struct mm_p2p_vif_ps_change_ind
+{
+ /// Index of the P2P VIF that is switching its PS state
+ u8_l vif_index;
+ /// New PS state of the P2P VIF interface (0: active, 1: sleeping)
+ u8_l ps_state;
+};
+
+/// Structure containing the parameters of the @ref MM_TRAFFIC_REQ_IND message
+struct mm_traffic_req_ind
+{
+ /// Index of the peer device that needs traffic
+ u8_l sta_idx;
+ /// Number of packets that need to be sent (if 0, all buffered traffic shall be sent and
+ /// if set to @ref PS_SP_INTERRUPTED, it means that current service period has been interrupted)
+ u8_l pkt_cnt;
+ /// Flag indicating if the traffic request concerns U-APSD queues or not
+ bool_l uapsd;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_PS_OPTIONS_REQ message.
+struct mm_set_ps_options_req
+{
+ /// VIF Index
+ u8_l vif_index;
+ /// Listen interval (0 if wake up shall be based on DTIM period)
+ u16_l listen_interval;
+ /// Flag indicating if we shall listen the BC/MC traffic or not
+ bool_l dont_listen_bc_mc;
+};
+
+/// Structure containing the parameters of the @ref MM_CSA_COUNTER_IND message
+struct mm_csa_counter_ind
+{
+ /// Index of the VIF
+ u8_l vif_index;
+ /// Updated CSA counter value
+ u8_l csa_count;
+};
+
+/// Structure containing the parameters of the @ref MM_CHANNEL_SURVEY_IND message
+struct mm_channel_survey_ind
+{
+ /// Frequency of the channel
+ u16_l freq;
+ /// Noise in dbm
+ s8_l noise_dbm;
+ /// Amount of time spent of the channel (in ms)
+ u32_l chan_time_ms;
+ /// Amount of time the primary channel was sensed busy
+ u32_l chan_time_busy_ms;
+};
+
+/// Structure containing the parameters of the @ref MM_BFMER_ENABLE_REQ message.
+struct mm_bfmer_enable_req
+{
+ /**
+ * Address of the beamforming report space allocated in host memory
+ * (Valid only if vht_su_bfmee is true)
+ */
+ u32_l host_bfr_addr;
+ /**
+ * Size of the beamforming report space allocated in host memory. This space should
+ * be twice the maximum size of the expected beamforming reports as the FW will
+ * divide it in two in order to be able to upload a new report while another one is
+ * used in transmission
+ */
+ u16_l host_bfr_size;
+ /// AID
+ u16_l aid;
+ /// Station Index
+ u8_l sta_idx;
+ /// Maximum number of spatial streams the station can receive
+ u8_l rx_nss;
+ /**
+ * Indicate if peer STA is MU Beamformee (VHT) capable
+ * (Valid only if vht_su_bfmee is true)
+ */
+ bool_l vht_mu_bfmee;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_P2P_NOA_REQ message.
+struct mm_set_p2p_noa_req
+{
+ /// VIF Index
+ u8_l vif_index;
+ /// Allocated NOA Instance Number - Valid only if count = 0
+ u8_l noa_inst_nb;
+ /// Count
+ u8_l count;
+ /// Indicate if NoA can be paused for traffic reason
+ bool_l dyn_noa;
+ /// Duration (in us)
+ u32_l duration_us;
+ /// Interval (in us)
+ u32_l interval_us;
+ /// Start Time offset from next TBTT (in us)
+ u32_l start_offset;
+};
+
+#ifdef AICWF_ARP_OFFLOAD
+struct mm_set_arpoffload_en_req
+{
+ u32_l ipaddr;
+ u8_l enable;
+ u8_l vif_idx;
+};
+
+struct mm_set_arpoffload_en_cfm
+{
+ u8_l status;
+};
+#endif
+
+struct mm_set_agg_disable_req
+{
+ u8_l disable;
+ u8_l staidx;
+};
+
+struct mm_set_coex_req
+{
+ u8_l bt_on;
+ u8_l disable_coexnull;
+ u8_l enable_nullcts;
+ u8_l enable_periodic_timer;
+ u8_l coex_timeslot_set;
+ u32_l coex_timeslot[2];
+};
+
+#if 0
+struct mm_set_rf_config_req
+{
+ u8_l def_band;
+ u8_l config_type;
+ u16_l offset;
+ u16_l len;
+ u16_l set;
+ u32_l rx_gain_24g[48][4];
+ u32_l rx_gain_5g[32][4];
+ u32_l tx_gain[32];
+};
+#endif
+struct mm_set_rf_config_req
+{
+ u8_l table_sel;
+ u8_l table_ofst;
+ u8_l table_num;
+ u8_l deft_page;
+ u32_l data[64];
+};
+
+
+struct mm_set_rf_calib_req
+{
+ u32_l cal_cfg_24g;
+ u32_l cal_cfg_5g;
+ u32_l param_alpha;
+ u32_l bt_calib_en;
+ u32_l bt_calib_param;
+ u8_l xtal_cap;
+ u8_l xtal_cap_fine;
+
+};
+
+struct mm_set_rf_calib_cfm
+{
+ u32_l rxgain_24g_addr;
+ u32_l rxgain_5g_addr;
+ u32_l txgain_24g_addr;
+ u32_l txgain_5g_addr;
+};
+
+struct mm_get_mac_addr_req
+{
+ u32_l get;
+};
+
+struct mm_get_mac_addr_cfm
+{
+ u8_l mac_addr[6];
+};
+
+struct mm_get_sta_info_req
+{
+ u8_l sta_idx;
+};
+
+struct mm_get_sta_info_cfm
+{
+ u32_l rate_info;
+ u32_l txfailed;
+ u8 rssi;
+};
+
+typedef struct
+{
+ u8_l enable;
+ u8_l dsss;
+ u8_l ofdmlowrate_2g4;
+ u8_l ofdm64qam_2g4;
+ u8_l ofdm256qam_2g4;
+ u8_l ofdm1024qam_2g4;
+ u8_l ofdmlowrate_5g;
+ u8_l ofdm64qam_5g;
+ u8_l ofdm256qam_5g;
+ u8_l ofdm1024qam_5g;
+} txpwr_lvl_conf_t;
+
+typedef struct
+{
+ u8_l enable;
+ s8_l pwrlvl_11b_11ag_2g4[12];
+ s8_l pwrlvl_11n_11ac_2g4[10];
+ s8_l pwrlvl_11ax_2g4[12];
+} txpwr_lvl_conf_v2_t;
+
+typedef struct
+{
+ u8_l enable;
+ s8_l pwrlvl_11b_11ag_2g4[12];
+ s8_l pwrlvl_11n_11ac_2g4[10];
+ s8_l pwrlvl_11ax_2g4[12];
+ s8_l pwrlvl_11a_5g[12];
+ s8_l pwrlvl_11n_11ac_5g[10];
+ s8_l pwrlvl_11ax_5g[12];
+} txpwr_lvl_conf_v3_t;
+
+struct mm_set_txpwr_lvl_req
+{
+ union {
+ txpwr_lvl_conf_t txpwr_lvl;
+ txpwr_lvl_conf_v2_t txpwr_lvl_v2;
+ txpwr_lvl_conf_v3_t txpwr_lvl_v3;
+ };
+};
+
+typedef struct
+{
+ u8_l loss_enable;
+ u8_l loss_value;
+} txpwr_loss_conf_t;
+
+typedef struct
+{
+ int8_t enable;
+ int8_t dsss;
+ int8_t ofdmlowrate_2g4;
+ int8_t ofdm64qam_2g4;
+ int8_t ofdm256qam_2g4;
+ int8_t ofdm1024qam_2g4;
+ int8_t ofdmlowrate_5g;
+ int8_t ofdm64qam_5g;
+ int8_t ofdm256qam_5g;
+ int8_t ofdm1024qam_5g;
+
+} txpwr_idx_conf_t;
+
+struct mm_set_txpwr_idx_req
+{
+ txpwr_idx_conf_t txpwr_idx;
+};
+
+typedef struct
+{
+ int8_t enable;
+ int8_t chan_1_4;
+ int8_t chan_5_9;
+ int8_t chan_10_13;
+ int8_t chan_36_64;
+ int8_t chan_100_120;
+ int8_t chan_122_140;
+ int8_t chan_142_165;
+} txpwr_ofst_conf_t;
+
+struct mm_set_txpwr_ofst_req
+{
+ txpwr_ofst_conf_t txpwr_ofst;
+};
+
+typedef struct
+{
+ u8_l enable;
+ u8_l xtal_cap;
+ u8_l xtal_cap_fine;
+} xtal_cap_conf_t;
+
+
+struct mm_set_stack_start_req
+{
+ u8_l is_stack_start;
+ u8_l efuse_valid;
+ u8_l set_vendor_info;
+ u8_l fwtrace_redir;
+};
+
+struct mm_set_stack_start_cfm
+{
+ u8_l is_5g_support;
+ u8_l vendor_info;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_P2P_OPPPS_REQ message.
+struct mm_set_p2p_oppps_req
+{
+ /// VIF Index
+ u8_l vif_index;
+ /// CTWindow
+ u8_l ctwindow;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_P2P_NOA_CFM message.
+struct mm_set_p2p_noa_cfm
+{
+ /// Request status
+ u8_l status;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_P2P_OPPPS_CFM message.
+struct mm_set_p2p_oppps_cfm
+{
+ /// Request status
+ u8_l status;
+};
+
+/// Structure containing the parameters of the @ref MM_P2P_NOA_UPD_IND message.
+struct mm_p2p_noa_upd_ind
+{
+ /// VIF Index
+ u8_l vif_index;
+ /// NOA Instance Number
+ u8_l noa_inst_nb;
+ /// NoA Type
+ u8_l noa_type;
+ /// Count
+ u8_l count;
+ /// Duration (in us)
+ u32_l duration_us;
+ /// Interval (in us)
+ u32_l interval_us;
+ /// Start Time
+ u32_l start_time;
+};
+
+/// Structure containing the parameters of the @ref MM_CFG_RSSI_REQ message
+struct mm_cfg_rssi_req
+{
+ /// Index of the VIF
+ u8_l vif_index;
+ /// RSSI threshold
+ s8_l rssi_thold;
+ /// RSSI hysteresis
+ u8_l rssi_hyst;
+};
+
+/// Structure containing the parameters of the @ref MM_RSSI_STATUS_IND message
+struct mm_rssi_status_ind
+{
+ /// Index of the VIF
+ u8_l vif_index;
+ /// Status of the RSSI
+ bool_l rssi_status;
+ /// Current RSSI
+ s8_l rssi;
+};
+
+/// Structure containing the parameters of the @ref MM_PKTLOSS_IND message
+struct mm_pktloss_ind
+{
+ /// Index of the VIF
+ u8_l vif_index;
+ /// Address of the STA for which there is a packet loss
+ struct mac_addr mac_addr;
+ /// Number of packets lost
+ u32 num_packets;
+};
+
+/// Structure containing the parameters of the @ref MM_CSA_FINISH_IND message
+struct mm_csa_finish_ind
+{
+ /// Index of the VIF
+ u8_l vif_index;
+ /// Status of the operation
+ u8_l status;
+ /// New channel ctx index
+ u8_l chan_idx;
+};
+
+/// Structure containing the parameters of the @ref MM_CSA_TRAFFIC_IND message
+struct mm_csa_traffic_ind
+{
+ /// Index of the VIF
+ u8_l vif_index;
+ /// Is tx traffic enable or disable
+ bool_l enable;
+};
+
+/// Structure containing the parameters of the @ref MM_MU_GROUP_UPDATE_REQ message.
+/// Size allocated for the structure depends of the number of group
+struct mm_mu_group_update_req
+{
+ /// Station index
+ u8_l sta_idx;
+ /// Number of groups the STA belongs to
+ u8_l group_cnt;
+ /// Group information
+ struct
+ {
+ /// Group Id
+ u8_l group_id;
+ /// User position
+ u8_l user_pos;
+ } groups[0];
+};
+
+///////////////////////////////////////////////////////////////////////////////
+/////////// For Scan messages
+///////////////////////////////////////////////////////////////////////////////
+enum scan_msg_tag
+{
+ /// Scanning start Request.
+ SCAN_START_REQ = LMAC_FIRST_MSG(TASK_SCAN),
+ /// Scanning start Confirmation.
+ SCAN_START_CFM,
+ /// End of scanning indication.
+ SCAN_DONE_IND,
+ /// Cancel scan request
+ SCAN_CANCEL_REQ,
+ /// Cancel scan confirmation
+ SCAN_CANCEL_CFM,
+
+ /// MAX number of messages
+ SCAN_MAX,
+};
+
+/// Maximum number of SSIDs in a scan request
+#define SCAN_SSID_MAX 3
+
+/// Maximum number of channels in a scan request
+#define SCAN_CHANNEL_MAX (MAC_DOMAINCHANNEL_24G_MAX + MAC_DOMAINCHANNEL_5G_MAX)
+
+/// Maximum length of the ProbeReq IEs (SoftMAC mode)
+#define SCAN_MAX_IE_LEN 300
+
+/// Maximum number of PHY bands supported
+#define SCAN_BAND_MAX 2
+
+/// Structure containing the parameters of the @ref SCAN_START_REQ message
+struct scan_start_req
+{
+ /// List of channel to be scanned
+ struct mac_chan_def chan[SCAN_CHANNEL_MAX];
+ /// List of SSIDs to be scanned
+ struct mac_ssid ssid[SCAN_SSID_MAX];
+ /// BSSID to be scanned
+ struct mac_addr bssid;
+ /// Pointer (in host memory) to the additional IEs that need to be added to the ProbeReq
+ /// (following the SSID element)
+ u32_l add_ies;
+ /// Length of the additional IEs
+ u16_l add_ie_len;
+ /// Index of the VIF that is scanning
+ u8_l vif_idx;
+ /// Number of channels to scan
+ u8_l chan_cnt;
+ /// Number of SSIDs to scan for
+ u8_l ssid_cnt;
+ /// no CCK - For P2P frames not being sent at CCK rate in 2GHz band.
+ bool no_cck;
+};
+
+/// Structure containing the parameters of the @ref SCAN_START_CFM message
+struct scan_start_cfm
+{
+ /// Status of the request
+ u8_l status;
+};
+
+/// Structure containing the parameters of the @ref SCAN_CANCEL_REQ message
+struct scan_cancel_req
+{
+};
+
+/// Structure containing the parameters of the @ref SCAN_START_CFM message
+struct scan_cancel_cfm
+{
+ /// Status of the request
+ u8_l status;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+/////////// For Scanu messages
+///////////////////////////////////////////////////////////////////////////////
+/// Messages that are logically related to the task.
+enum
+{
+ /// Scan request from host.
+ SCANU_START_REQ = LMAC_FIRST_MSG(TASK_SCANU),
+ /// Scanning start Confirmation.
+ SCANU_START_CFM,
+ /// Join request
+ SCANU_JOIN_REQ,
+ /// Join confirmation.
+ SCANU_JOIN_CFM,
+ /// Scan result indication.
+ SCANU_RESULT_IND,
+ /// Fast scan request from any other module.
+ SCANU_FAST_REQ,
+ /// Confirmation of fast scan request.
+ SCANU_FAST_CFM,
+
+ SCANU_VENDOR_IE_REQ,
+ SCANU_VENDOR_IE_CFM,
+ SCANU_START_CFM_ADDTIONAL,
+ SCANU_CANCEL_REQ,
+ SCANU_CANCEL_CFM,
+
+ /// MAX number of messages
+ SCANU_MAX,
+};
+
+/// Maximum length of the additional ProbeReq IEs (FullMAC mode)
+#define SCANU_MAX_IE_LEN 200
+
+/// Structure containing the parameters of the @ref SCANU_START_REQ message
+struct scanu_start_req
+{
+ /// List of channel to be scanned
+ struct mac_chan_def chan[SCAN_CHANNEL_MAX];
+ /// List of SSIDs to be scanned
+ struct mac_ssid ssid[SCAN_SSID_MAX];
+ /// BSSID to be scanned (or WILDCARD BSSID if no BSSID is searched in particular)
+ struct mac_addr bssid;
+ /// Address (in host memory) of the additional IEs that need to be added to the ProbeReq
+ /// (following the SSID element)
+ u32_l add_ies;
+ /// Length of the additional IEs
+ u16_l add_ie_len;
+ /// Index of the VIF that is scanning
+ u8_l vif_idx;
+ /// Number of channels to scan
+ u8_l chan_cnt;
+ /// Number of SSIDs to scan for
+ u8_l ssid_cnt;
+ /// no CCK - For P2P frames not being sent at CCK rate in 2GHz band.
+ bool no_cck;
+ /// Scan duration, in us
+ u32_l duration;
+};
+
+struct scanu_vendor_ie_req
+{
+ u16_l add_ie_len;
+ u8_l vif_idx;
+ u8_l ie[256];
+};
+
+/// Structure containing the parameters of the @ref SCANU_START_CFM message
+struct scanu_start_cfm
+{
+ /// Index of the VIF that was scanning
+ u8_l vif_idx;
+ /// Status of the request
+ u8_l status;
+ /// Number of scan results available
+ u8_l result_cnt;
+};
+
+/// Parameters of the @SCANU_RESULT_IND message
+struct scanu_result_ind
+{
+ /// Length of the frame
+ u16_l length;
+ /// Frame control field of the frame.
+ u16_l framectrl;
+ /// Center frequency on which we received the packet
+ u16_l center_freq;
+ /// PHY band
+ u8_l band;
+ /// Index of the station that sent the frame. 0xFF if unknown.
+ u8_l sta_idx;
+ /// Index of the VIF that received the frame. 0xFF if unknown.
+ u8_l inst_nbr;
+ /// RSSI of the received frame.
+ s8_l rssi;
+ /// Frame payload.
+ u32_l payload[];
+};
+
+/// Structure containing the parameters of the message.
+struct scanu_fast_req
+{
+ /// The SSID to scan in the channel.
+ struct mac_ssid ssid;
+ /// BSSID.
+ struct mac_addr bssid;
+ /// Probe delay.
+ u16_l probe_delay;
+ /// Minimum channel time.
+ u16_l minch_time;
+ /// Maximum channel time.
+ u16_l maxch_time;
+ /// The channel number to scan.
+ u16_l ch_nbr;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+/////////// For ME messages
+///////////////////////////////////////////////////////////////////////////////
+/// Messages that are logically related to the task.
+enum
+{
+ /// Configuration request from host.
+ ME_CONFIG_REQ = LMAC_FIRST_MSG(TASK_ME),
+ /// Configuration confirmation.
+ ME_CONFIG_CFM,
+ /// Configuration request from host.
+ ME_CHAN_CONFIG_REQ,
+ /// Configuration confirmation.
+ ME_CHAN_CONFIG_CFM,
+ /// Set control port state for a station.
+ ME_SET_CONTROL_PORT_REQ,
+ /// Control port setting confirmation.
+ ME_SET_CONTROL_PORT_CFM,
+ /// TKIP MIC failure indication.
+ ME_TKIP_MIC_FAILURE_IND,
+ /// Add a station to the FW (AP mode)
+ ME_STA_ADD_REQ,
+ /// Confirmation of the STA addition
+ ME_STA_ADD_CFM,
+ /// Delete a station from the FW (AP mode)
+ ME_STA_DEL_REQ,
+ /// Confirmation of the STA deletion
+ ME_STA_DEL_CFM,
+ /// Indication of a TX RA/TID queue credit update
+ ME_TX_CREDITS_UPDATE_IND,
+ /// Request indicating to the FW that there is traffic buffered on host
+ ME_TRAFFIC_IND_REQ,
+ /// Confirmation that the @ref ME_TRAFFIC_IND_REQ has been executed
+ ME_TRAFFIC_IND_CFM,
+ /// Request of RC statistics to a station
+ ME_RC_STATS_REQ,
+ /// RC statistics confirmation
+ ME_RC_STATS_CFM,
+ /// RC fixed rate request
+ ME_RC_SET_RATE_REQ,
+ /// Configure monitor interface
+ ME_CONFIG_MONITOR_REQ,
+ /// Configure monitor interface response
+ ME_CONFIG_MONITOR_CFM,
+ /// Setting power Save mode request from host
+ ME_SET_PS_MODE_REQ,
+ /// Set power Save mode confirmation
+ ME_SET_PS_MODE_CFM,
+ /// Setting Low Power level request from host
+ ME_SET_LP_LEVEL_REQ,
+ /// Set Low Power level confirmation
+ ME_SET_LP_LEVEL_CFM,
+ /// MAX number of messages
+ ME_MAX,
+};
+
+/// Structure containing the parameters of the @ref ME_START_REQ message
+struct me_config_req
+{
+ /// HT Capabilities
+ struct mac_htcapability ht_cap;
+ /// VHT Capabilities
+ struct mac_vhtcapability vht_cap;
+ /// HE capabilities
+ struct mac_hecapability he_cap;
+ /// Lifetime of packets sent under a BlockAck agreement (expressed in TUs)
+ u16_l tx_lft;
+ /// Maximum supported BW
+ u8_l phy_bw_max;
+ /// Boolean indicating if HT is supported or not
+ bool_l ht_supp;
+ /// Boolean indicating if VHT is supported or not
+ bool_l vht_supp;
+ /// Boolean indicating if HE is supported or not
+ bool_l he_supp;
+ /// Boolean indicating if HE OFDMA UL is enabled or not
+ bool_l he_ul_on;
+ /// Boolean indicating if PS mode shall be enabled or not
+ bool_l ps_on;
+ /// Boolean indicating if Antenna Diversity shall be enabled or not
+ bool_l ant_div_on;
+ /// Boolean indicating if Dynamic PS mode shall be used or not
+ bool_l dpsm;
+};
+
+/// Structure containing the parameters of the @ref ME_CHAN_CONFIG_REQ message
+struct me_chan_config_req
+{
+ /// List of 2.4GHz supported channels
+ struct mac_chan_def chan2G4[MAC_DOMAINCHANNEL_24G_MAX];
+ /// List of 5GHz supported channels
+ struct mac_chan_def chan5G[MAC_DOMAINCHANNEL_5G_MAX];
+ /// Number of 2.4GHz channels in the list
+ u8_l chan2G4_cnt;
+ /// Number of 5GHz channels in the list
+ u8_l chan5G_cnt;
+};
+
+/// Structure containing the parameters of the @ref ME_SET_CONTROL_PORT_REQ message
+struct me_set_control_port_req
+{
+ /// Index of the station for which the control port is opened
+ u8_l sta_idx;
+ /// Control port state
+ bool_l control_port_open;
+};
+
+/// Structure containing the parameters of the @ref ME_TKIP_MIC_FAILURE_IND message
+struct me_tkip_mic_failure_ind
+{
+ /// Address of the sending STA
+ struct mac_addr addr;
+ /// TSC value
+ u64_l tsc;
+ /// Boolean indicating if the packet was a group or unicast one (true if group)
+ bool_l ga;
+ /// Key Id
+ u8_l keyid;
+ /// VIF index
+ u8_l vif_idx;
+};
+
+/// Structure containing the parameters of the @ref ME_STA_ADD_REQ message
+struct me_sta_add_req
+{
+ /// MAC address of the station to be added
+ struct mac_addr mac_addr;
+ /// Supported legacy rates
+ struct mac_rateset rate_set;
+ /// HT Capabilities
+ struct mac_htcapability ht_cap;
+ /// VHT Capabilities
+ struct mac_vhtcapability vht_cap;
+ /// HE capabilities
+ struct mac_hecapability he_cap;
+ /// Flags giving additional information about the station (@ref mac_sta_flags)
+ u32_l flags;
+ /// Association ID of the station
+ u16_l aid;
+ /// Bit field indicating which queues have U-APSD enabled
+ u8_l uapsd_queues;
+ /// Maximum size, in frames, of a APSD service period
+ u8_l max_sp_len;
+ /// Operation mode information (valid if bit @ref STA_OPMOD_NOTIF is
+ /// set in the flags)
+ u8_l opmode;
+ /// Index of the VIF the station is attached to
+ u8_l vif_idx;
+ /// Whether the the station is TDLS station
+ bool_l tdls_sta;
+ /// Indicate if the station is TDLS link initiator station
+ bool_l tdls_sta_initiator;
+ /// Indicate if the TDLS Channel Switch is allowed
+ bool_l tdls_chsw_allowed;
+};
+
+/// Structure containing the parameters of the @ref ME_STA_ADD_CFM message
+struct me_sta_add_cfm
+{
+ /// Station index
+ u8_l sta_idx;
+ /// Status of the station addition
+ u8_l status;
+ /// PM state of the station
+ u8_l pm_state;
+};
+
+/// Structure containing the parameters of the @ref ME_STA_DEL_REQ message.
+struct me_sta_del_req
+{
+ /// Index of the station to be deleted
+ u8_l sta_idx;
+ /// Whether the the station is TDLS station
+ bool_l tdls_sta;
+};
+
+/// Structure containing the parameters of the @ref ME_TX_CREDITS_UPDATE_IND message.
+struct me_tx_credits_update_ind
+{
+ /// Index of the station for which the credits are updated
+ u8_l sta_idx;
+ /// TID for which the credits are updated
+ u8_l tid;
+ /// Offset to be applied on the credit count
+ s8_l credits;
+};
+
+/// Structure containing the parameters of the @ref ME_TRAFFIC_IND_REQ message.
+struct me_traffic_ind_req
+{
+ /// Index of the station for which UAPSD traffic is available on host
+ u8_l sta_idx;
+ /// Flag indicating the availability of UAPSD packets for the given STA
+ u8_l tx_avail;
+ /// Indicate if traffic is on uapsd-enabled queues
+ bool_l uapsd;
+};
+
+struct mm_apm_staloss_ind
+{
+ u8_l sta_idx;
+ u8_l vif_idx;
+ u8_l mac_addr[6];
+};
+
+enum vendor_hwconfig_tag{
+ ACS_TXOP_REQ = 0,
+ CHANNEL_ACCESS_REQ,
+ MAC_TIMESCALE_REQ,
+ CCA_THRESHOLD_REQ,
+ BWMODE_REQ,
+};
+
+enum {
+ BWMODE20M = 0,
+ BWMODE10M,
+ BWMODE5M,
+};
+
+struct mm_set_acs_txop_req
+{
+ u32_l hwconfig_id;
+ u16_l txop_bk;
+ u16_l txop_be;
+ u16_l txop_vi;
+ u16_l txop_vo;
+};
+
+struct mm_set_channel_access_req
+{
+ u32_l hwconfig_id;
+ u32_l edca[4];
+ u8_l vif_idx;
+ u8_l retry_cnt;
+ u8_l rts_en;
+ u8_l long_nav_en;
+ u8_l cfe_en;
+ u8_l rc_retry_cnt[3];
+};
+
+struct mm_set_mac_timescale_req
+{
+ u32_l hwconfig_id;
+ u8_l sifsA_time;
+ u8_l sifsB_time;
+ u8_l slot_time;
+ u8_l rx_startdelay_ofdm;
+ u8_l rx_startdelay_long;
+ u8_l rx_startdelay_short;
+};
+
+struct mm_set_cca_threshold_req
+{
+ u32_l hwconfig_id;
+ u8_l auto_cca_en;
+ s8_l cca20p_rise_th;
+ s8_l cca20s_rise_th;
+ s8_l cca20p_fall_th;
+ s8_l cca20s_fall_th;
+
+};
+
+struct mm_set_bwmode_req
+{
+ u32_l hwconfig_id;
+ u8_l bwmode;
+};
+
+struct mm_set_txop_req
+{
+ u16_l txop_bk;
+ u16_l txop_be;
+ u16_l txop_vi;
+ u16_l txop_vo;
+ u8_l long_nav_en;
+ u8_l cfe_en;
+};
+
+struct mm_set_vendor_trx_param_req
+{
+ u32_l edca[4];
+ u8_l vif_idx;
+ u8_l retry_cnt;
+};
+
+struct mm_get_fw_version_cfm
+{
+ u8_l fw_version_len;
+ u8_l fw_version[63];
+};
+
+/// Structure containing the parameters of the @ref ME_RC_STATS_REQ message.
+struct me_rc_stats_req
+{
+ /// Index of the station for which the RC statistics are requested
+ u8_l sta_idx;
+};
+
+/// Structure containing the rate control statistics
+struct rc_rate_stats
+{
+ /// Number of attempts (per sampling interval)
+ u16_l attempts;
+ /// Number of success (per sampling interval)
+ u16_l success;
+ /// Estimated probability of success (EWMA)
+ u16_l probability;
+ /// Rate configuration of the sample
+ u16_l rate_config;
+ union
+ {
+ struct {
+ /// Number of times the sample has been skipped (per sampling interval)
+ u8_l sample_skipped;
+ /// Whether the old probability is available
+ bool_l old_prob_available;
+ /// Whether the rate can be used in the retry chain
+ bool_l rate_allowed;
+ };
+ struct {
+ /// RU size and UL length received in the latest HE trigger frame
+ u16_l ru_and_length;
+ };
+ };
+};
+
+/// Number of RC samples
+#define RC_MAX_N_SAMPLE 10
+/// Index of the HE statistics element in the table
+#define RC_HE_STATS_IDX RC_MAX_N_SAMPLE
+
+/// Structure containing the parameters of the @ref ME_RC_STATS_CFM message.
+struct me_rc_stats_cfm
+{
+ /// Index of the station for which the RC statistics are provided
+ u8_l sta_idx;
+ /// Number of samples used in the RC algorithm
+ u16_l no_samples;
+ /// Number of MPDUs transmitted (per sampling interval)
+ u16_l ampdu_len;
+ /// Number of AMPDUs transmitted (per sampling interval)
+ u16_l ampdu_packets;
+ /// Average number of MPDUs in each AMPDU frame (EWMA)
+ u32_l avg_ampdu_len;
+ // Current step 0 of the retry chain
+ u8_l sw_retry_step;
+ /// Trial transmission period
+ u8_l sample_wait;
+ /// Retry chain steps
+ u16_l retry_step_idx[4];
+ /// RC statistics - Max number of RC samples, plus one for the HE TB statistics
+ struct rc_rate_stats rate_stats[RC_MAX_N_SAMPLE + 1];
+ /// Throughput - Max number of RC samples, plus one for the HE TB statistics
+ u32_l tp[RC_MAX_N_SAMPLE + 1];
+};
+
+/// Structure containing the parameters of the @ref ME_RC_SET_RATE_REQ message.
+struct me_rc_set_rate_req
+{
+ /// Index of the station for which the fixed rate is set
+ u8_l sta_idx;
+ /// Rate configuration to be set
+ u16_l fixed_rate_cfg;
+};
+
+/// Structure containing the parameters of the @ref ME_CONFIG_MONITOR_REQ message.
+struct me_config_monitor_req
+{
+ /// Channel to configure
+ struct mac_chan_op chan;
+ /// Is channel data valid
+ bool_l chan_set;
+ /// Enable report of unsupported HT frames
+ bool_l uf;
+ /// Enable auto-reply as the mac_addr matches
+ bool_l auto_reply;
+};
+
+/// Structure containing the parameters of the @ref ME_CONFIG_MONITOR_CFM message.
+struct me_config_monitor_cfm
+{
+ /// Channel context index
+ u8_l chan_index;
+ /// Channel parameters
+ struct mac_chan_op chan;
+};
+
+/// Structure containing the parameters of the @ref ME_SET_PS_MODE_REQ message.
+struct me_set_ps_mode_req
+{
+ /// Power Save is activated or deactivated
+ u8_l ps_state;
+};
+
+/// Structure containing the parameters of the @ref ME_SET_LP_LEVEL_REQ message.
+struct me_set_lp_level_req
+{
+ /// Low Power level
+ u8_l lp_level;
+};
+
+
+///////////////////////////////////////////////////////////////////////////////
+/////////// For SM messages
+///////////////////////////////////////////////////////////////////////////////
+/// Message API of the SM task
+enum sm_msg_tag
+{
+ /// Request to connect to an AP
+ SM_CONNECT_REQ = LMAC_FIRST_MSG(TASK_SM),
+ /// Confirmation of connection
+ SM_CONNECT_CFM,
+ /// Indicates that the SM associated to the AP
+ SM_CONNECT_IND,
+ /// Request to disconnect
+ SM_DISCONNECT_REQ,
+ /// Confirmation of disconnection
+ SM_DISCONNECT_CFM,
+ /// Indicates that the SM disassociated the AP
+ SM_DISCONNECT_IND,
+ /// Request to start external authentication
+ SM_EXTERNAL_AUTH_REQUIRED_IND,
+ /// Response to external authentication request
+ SM_EXTERNAL_AUTH_REQUIRED_RSP,
+
+ /// MAX number of messages
+ SM_MAX,
+};
+
+/// Structure containing the parameters of @ref SM_CONNECT_REQ message.
+struct sm_connect_req
+{
+ /// SSID to connect to
+ struct mac_ssid ssid;
+ /// BSSID to connect to (if not specified, set this field to WILDCARD BSSID)
+ struct mac_addr bssid;
+ /// Channel on which we have to connect (if not specified, set -1 in the chan.freq field)
+ struct mac_chan_def chan;
+ /// Connection flags (see @ref mac_connection_flags)
+ u32_l flags;
+ /// Control port Ethertype (in network endianness)
+ u16_l ctrl_port_ethertype;
+ /// Length of the association request IEs
+ u16_l ie_len;
+ /// Listen interval to be used for this connection
+ u16_l listen_interval;
+ /// Flag indicating if the we have to wait for the BC/MC traffic after beacon or not
+ bool_l dont_wait_bcmc;
+ /// Authentication type
+ u8_l auth_type;
+ /// UAPSD queues (bit0: VO, bit1: VI, bit2: BE, bit3: BK)
+ u8_l uapsd_queues;
+ /// VIF index
+ u8_l vif_idx;
+ /// Buffer containing the additional information elements to be put in the
+ /// association request
+ u32_l ie_buf[64];
+};
+
+/// Structure containing the parameters of the @ref SM_CONNECT_CFM message.
+struct sm_connect_cfm
+{
+ /// Status. If 0, it means that the connection procedure will be performed and that
+ /// a subsequent @ref SM_CONNECT_IND message will be forwarded once the procedure is
+ /// completed
+ u8_l status;
+};
+
+#define SM_ASSOC_IE_LEN 800
+/// Structure containing the parameters of the @ref SM_CONNECT_IND message.
+struct sm_connect_ind
+{
+ /// Status code of the connection procedure
+ u16_l status_code;
+ /// BSSID
+ struct mac_addr bssid;
+ /// Flag indicating if the indication refers to an internal roaming or from a host request
+ bool_l roamed;
+ /// Index of the VIF for which the association process is complete
+ u8_l vif_idx;
+ /// Index of the STA entry allocated for the AP
+ u8_l ap_idx;
+ /// Index of the LMAC channel context the connection is attached to
+ u8_l ch_idx;
+ /// Flag indicating if the AP is supporting QoS
+ bool_l qos;
+ /// ACM bits set in the AP WMM parameter element
+ u8_l acm;
+ /// Length of the AssocReq IEs
+ u16_l assoc_req_ie_len;
+ /// Length of the AssocRsp IEs
+ u16_l assoc_rsp_ie_len;
+ /// IE buffer
+ u32_l assoc_ie_buf[SM_ASSOC_IE_LEN/4];
+
+ u16_l aid;
+ u8_l band;
+ u16_l center_freq;
+ u8_l width;
+ u32_l center_freq1;
+ u32_l center_freq2;
+
+ /// EDCA parameters
+ u32_l ac_param[AC_MAX];
+};
+
+/// Structure containing the parameters of the @ref SM_DISCONNECT_REQ message.
+struct sm_disconnect_req
+{
+ /// Reason of the deauthentication.
+ u16_l reason_code;
+ /// Index of the VIF.
+ u8_l vif_idx;
+};
+
+/// Structure containing the parameters of SM_ASSOCIATION_IND the message
+struct sm_association_ind
+{
+ // MAC ADDR of the STA
+ struct mac_addr me_mac_addr;
+};
+
+
+/// Structure containing the parameters of the @ref SM_DISCONNECT_IND message.
+struct sm_disconnect_ind
+{
+ /// Reason of the disconnection.
+ u16_l reason_code;
+ /// Index of the VIF.
+ u8_l vif_idx;
+ /// FT over DS is ongoing
+ bool_l ft_over_ds;
+ u8_l reassoc;
+};
+
+/// Structure containing the parameters of the @ref SM_EXTERNAL_AUTH_REQUIRED_IND
+struct sm_external_auth_required_ind
+{
+ /// Index of the VIF.
+ u8_l vif_idx;
+ /// SSID to authenticate to
+ struct mac_ssid ssid;
+ /// BSSID to authenticate to
+ struct mac_addr bssid;
+ /// AKM suite of the respective authentication
+ u32_l akm;
+};
+
+/// Structure containing the parameters of the @ref SM_EXTERNAL_AUTH_REQUIRED_RSP
+struct sm_external_auth_required_rsp
+{
+ /// Index of the VIF.
+ u8_l vif_idx;
+ /// Authentication status
+ u16_l status;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+/////////// For APM messages
+///////////////////////////////////////////////////////////////////////////////
+/// Message API of the APM task
+enum apm_msg_tag
+{
+ /// Request to start the AP.
+ APM_START_REQ = LMAC_FIRST_MSG(TASK_APM),
+ /// Confirmation of the AP start.
+ APM_START_CFM,
+ /// Request to stop the AP.
+ APM_STOP_REQ,
+ /// Confirmation of the AP stop.
+ APM_STOP_CFM,
+ /// Request to start CAC
+ APM_START_CAC_REQ,
+ /// Confirmation of the CAC start
+ APM_START_CAC_CFM,
+ /// Request to stop CAC
+ APM_STOP_CAC_REQ,
+ /// Confirmation of the CAC stop
+ APM_STOP_CAC_CFM,
+
+ APM_SET_BEACON_IE_REQ,
+ APM_SET_BEACON_IE_CFM,
+ /// MAX number of messages
+ APM_MAX,
+};
+
+/// Structure containing the parameters of the @ref APM_START_REQ message.
+struct apm_start_req
+{
+ /// Basic rate set
+ struct mac_rateset basic_rates;
+ /// Control channel on which we have to enable the AP
+ struct mac_chan_def chan;
+ /// Center frequency of the first segment
+ u32_l center_freq1;
+ /// Center frequency of the second segment (only in 80+80 configuration)
+ u32_l center_freq2;
+ /// Width of channel
+ u8_l ch_width;
+ /// Address, in host memory, to the beacon template
+ u32_l bcn_addr;
+ /// Length of the beacon template
+ u16_l bcn_len;
+ /// Offset of the TIM IE in the beacon
+ u16_l tim_oft;
+ /// Beacon interval
+ u16_l bcn_int;
+ /// Flags (@ref mac_connection_flags)
+ u32_l flags;
+ /// Control port Ethertype
+ u16_l ctrl_port_ethertype;
+ /// Length of the TIM IE
+ u8_l tim_len;
+ /// Index of the VIF for which the AP is started
+ u8_l vif_idx;
+};
+
+struct apm_set_bcn_ie_req
+{
+ u8_l vif_idx;
+ u16_l bcn_ie_len;
+ u8_l bcn_ie[512];
+};
+
+/// Structure containing the parameters of the @ref APM_START_CFM message.
+struct apm_start_cfm
+{
+ /// Status of the AP starting procedure
+ u8_l status;
+ /// Index of the VIF for which the AP is started
+ u8_l vif_idx;
+ /// Index of the channel context attached to the VIF
+ u8_l ch_idx;
+ /// Index of the STA used for BC/MC traffic
+ u8_l bcmc_idx;
+};
+
+/// Structure containing the parameters of the @ref APM_STOP_REQ message.
+struct apm_stop_req
+{
+ /// Index of the VIF for which the AP has to be stopped
+ u8_l vif_idx;
+};
+
+/// Structure containing the parameters of the @ref APM_START_CAC_REQ message.
+struct apm_start_cac_req
+{
+ /// Control channel on which we have to start the CAC
+ struct mac_chan_def chan;
+ /// Center frequency of the first segment
+ u32_l center_freq1;
+ /// Center frequency of the second segment (only in 80+80 configuration)
+ u32_l center_freq2;
+ /// Width of channel
+ u8_l ch_width;
+ /// Index of the VIF for which the CAC is started
+ u8_l vif_idx;
+};
+
+/// Structure containing the parameters of the @ref APM_START_CAC_CFM message.
+struct apm_start_cac_cfm
+{
+ /// Status of the CAC starting procedure
+ u8_l status;
+ /// Index of the channel context attached to the VIF for CAC
+ u8_l ch_idx;
+};
+
+/// Structure containing the parameters of the @ref APM_STOP_CAC_REQ message.
+struct apm_stop_cac_req
+{
+ /// Index of the VIF for which the CAC has to be stopped
+ u8_l vif_idx;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+/////////// For MESH messages
+///////////////////////////////////////////////////////////////////////////////
+
+/// Maximum length of the Mesh ID
+#define MESH_MESHID_MAX_LEN (32)
+
+/// Message API of the MESH task
+enum mesh_msg_tag
+{
+ /// Request to start the MP
+ MESH_START_REQ = LMAC_FIRST_MSG(TASK_MESH),
+ /// Confirmation of the MP start.
+ MESH_START_CFM,
+
+ /// Request to stop the MP.
+ MESH_STOP_REQ,
+ /// Confirmation of the MP stop.
+ MESH_STOP_CFM,
+
+ // Request to update the MP
+ MESH_UPDATE_REQ,
+ /// Confirmation of the MP update
+ MESH_UPDATE_CFM,
+
+ /// Request information about a given link
+ MESH_PEER_INFO_REQ,
+ /// Response to the MESH_PEER_INFO_REQ message
+ MESH_PEER_INFO_CFM,
+
+ /// Request automatic establishment of a path with a given mesh STA
+ MESH_PATH_CREATE_REQ,
+ /// Confirmation to the MESH_PATH_CREATE_REQ message
+ MESH_PATH_CREATE_CFM,
+
+ /// Request a path update (delete path, modify next hop mesh STA)
+ MESH_PATH_UPDATE_REQ,
+ /// Confirmation to the MESH_PATH_UPDATE_REQ message
+ MESH_PATH_UPDATE_CFM,
+
+ /// Indication from Host that the indicated Mesh Interface is a proxy for an external STA
+ MESH_PROXY_ADD_REQ,
+
+ /// Indicate that a connection has been established or lost
+ MESH_PEER_UPDATE_IND,
+ /// Notification that a connection has been established or lost (when MPM handled by userspace)
+ MESH_PEER_UPDATE_NTF = MESH_PEER_UPDATE_IND,
+
+ /// Indicate that a path is now active or inactive
+ MESH_PATH_UPDATE_IND,
+ /// Indicate that proxy information have been updated
+ MESH_PROXY_UPDATE_IND,
+
+ /// MAX number of messages
+ MESH_MAX,
+};
+
+/// Structure containing the parameters of the @ref MESH_START_REQ message.
+struct mesh_start_req
+{
+ /// Basic rate set
+ struct mac_rateset basic_rates;
+ /// Control channel on which we have to enable the AP
+ struct mac_chan_def chan;
+ /// Center frequency of the first segment
+ u32_l center_freq1;
+ /// Center frequency of the second segment (only in 80+80 configuration)
+ u32_l center_freq2;
+ /// Width of channel
+ u8_l ch_width;
+ /// DTIM Period
+ u8_l dtim_period;
+ /// Beacon Interval
+ u16_l bcn_int;
+ /// Index of the VIF for which the MP is started
+ u8_l vif_index;
+ /// Length of the Mesh ID
+ u8_l mesh_id_len;
+ /// Mesh ID
+ u8_l mesh_id[MESH_MESHID_MAX_LEN];
+ /// Address of the IEs to download
+ u32_l ie_addr;
+ /// Length of the provided IEs
+ u8_l ie_len;
+ /// Indicate if Mesh Peering Management (MPM) protocol is handled in userspace
+ bool_l user_mpm;
+ /// Indicate if Mesh Point is using authentication
+ bool_l is_auth;
+ /// Indicate which authentication method is used
+ u8_l auth_id;
+};
+
+/// Structure containing the parameters of the @ref MESH_START_CFM message.
+struct mesh_start_cfm
+{
+ /// Status of the MP starting procedure
+ u8_l status;
+ /// Index of the VIF for which the MP is started
+ u8_l vif_idx;
+ /// Index of the channel context attached to the VIF
+ u8_l ch_idx;
+ /// Index of the STA used for BC/MC traffic
+ u8_l bcmc_idx;
+};
+
+/// Structure containing the parameters of the @ref MESH_STOP_REQ message.
+struct mesh_stop_req
+{
+ /// Index of the VIF for which the MP has to be stopped
+ u8_l vif_idx;
+};
+
+/// Structure containing the parameters of the @ref MESH_STOP_CFM message.
+struct mesh_stop_cfm
+{
+ /// Index of the VIF for which the MP has to be stopped
+ u8_l vif_idx;
+ /// Status
+ u8_l status;
+};
+
+/// Bit fields for mesh_update_req message's flags value
+enum mesh_update_flags_bit
+{
+ /// Root Mode
+ MESH_UPDATE_FLAGS_ROOT_MODE_BIT = 0,
+ /// Gate Mode
+ MESH_UPDATE_FLAGS_GATE_MODE_BIT,
+ /// Mesh Forwarding
+ MESH_UPDATE_FLAGS_MESH_FWD_BIT,
+ /// Local Power Save Mode
+ MESH_UPDATE_FLAGS_LOCAL_PSM_BIT,
+};
+
+/// Structure containing the parameters of the @ref MESH_UPDATE_REQ message.
+struct mesh_update_req
+{
+ /// Flags, indicate fields which have been updated
+ u8_l flags;
+ /// VIF Index
+ u8_l vif_idx;
+ /// Root Mode
+ u8_l root_mode;
+ /// Gate Announcement
+ bool_l gate_announ;
+ /// Mesh Forwarding
+ bool_l mesh_forward;
+ /// Local PS Mode
+ u8_l local_ps_mode;
+};
+
+/// Structure containing the parameters of the @ref MESH_UPDATE_CFM message.
+struct mesh_update_cfm
+{
+ /// Status
+ u8_l status;
+};
+
+/// Structure containing the parameters of the @ref MESH_PEER_INFO_REQ message.
+struct mesh_peer_info_req
+{
+ ///Index of the station allocated for the peer
+ u8_l sta_idx;
+};
+
+/// Structure containing the parameters of the @ref MESH_PEER_INFO_CFM message.
+struct mesh_peer_info_cfm
+{
+ /// Response status
+ u8_l status;
+ /// Index of the station allocated for the peer
+ u8_l sta_idx;
+ /// Local Link ID
+ u16_l local_link_id;
+ /// Peer Link ID
+ u16_l peer_link_id;
+ /// Local PS Mode
+ u8_l local_ps_mode;
+ /// Peer PS Mode
+ u8_l peer_ps_mode;
+ /// Non-peer PS Mode
+ u8_l non_peer_ps_mode;
+ /// Link State
+ u8_l link_state;
+};
+
+/// Structure containing the parameters of the @ref MESH_PATH_CREATE_REQ message.
+struct mesh_path_create_req
+{
+ /// Index of the interface on which path has to be created
+ u8_l vif_idx;
+ /// Indicate if originator MAC Address is provided
+ bool_l has_orig_addr;
+ /// Path Target MAC Address
+ struct mac_addr tgt_mac_addr;
+ /// Originator MAC Address
+ struct mac_addr orig_mac_addr;
+};
+
+/// Structure containing the parameters of the @ref MESH_PATH_CREATE_CFM message.
+struct mesh_path_create_cfm
+{
+ /// Confirmation status
+ u8_l status;
+ /// VIF Index
+ u8_l vif_idx;
+};
+
+/// Structure containing the parameters of the @ref MESH_PATH_UPDATE_REQ message.
+struct mesh_path_update_req
+{
+ /// Indicate if path must be deleted
+ bool_l delete;
+ /// Index of the interface on which path has to be created
+ u8_l vif_idx;
+ /// Path Target MAC Address
+ struct mac_addr tgt_mac_addr;
+ /// Next Hop MAC Address
+ struct mac_addr nhop_mac_addr;
+};
+
+/// Structure containing the parameters of the @ref MESH_PATH_UPDATE_CFM message.
+struct mesh_path_update_cfm
+{
+ /// Confirmation status
+ u8_l status;
+ /// VIF Index
+ u8_l vif_idx;
+};
+
+/// Structure containing the parameters of the @ref MESH_PROXY_ADD_REQ message.
+struct mesh_proxy_add_req
+{
+ /// VIF Index
+ u8_l vif_idx;
+ /// MAC Address of the External STA
+ struct mac_addr ext_sta_addr;
+};
+
+/// Structure containing the parameters of the @ref MESH_PROXY_UPDATE_IND
+struct mesh_proxy_update_ind
+{
+ /// Indicate if proxy information has been added or deleted
+ bool_l delete;
+ /// Indicate if we are a proxy for the external STA
+ bool_l local;
+ /// VIF Index
+ u8_l vif_idx;
+ /// MAC Address of the External STA
+ struct mac_addr ext_sta_addr;
+ /// MAC Address of the proxy (only valid if local is false)
+ struct mac_addr proxy_mac_addr;
+};
+
+/// Structure containing the parameters of the @ref MESH_PEER_UPDATE_IND message.
+struct mesh_peer_update_ind
+{
+ /// Indicate if connection has been established or lost
+ bool_l estab;
+ /// VIF Index
+ u8_l vif_idx;
+ /// STA Index
+ u8_l sta_idx;
+ /// Peer MAC Address
+ struct mac_addr peer_addr;
+};
+
+/// Structure containing the parameters of the @ref MESH_PEER_UPDATE_NTF message.
+struct mesh_peer_update_ntf
+{
+ /// VIF Index
+ u8_l vif_idx;
+ /// STA Index
+ u8_l sta_idx;
+ /// Mesh Link State
+ u8_l state;
+};
+
+/// Structure containing the parameters of the @ref MESH_PATH_UPDATE_IND message.
+struct mesh_path_update_ind
+{
+ /// Indicate if path is deleted or not
+ bool_l delete;
+ /// Indicate if path is towards an external STA (not part of MBSS)
+ bool_l ext_sta;
+ /// VIF Index
+ u8_l vif_idx;
+ /// Path Index
+ u8_l path_idx;
+ /// Target MAC Address
+ struct mac_addr tgt_mac_addr;
+ /// External STA MAC Address (only if ext_sta is true)
+ struct mac_addr ext_sta_mac_addr;
+ /// Next Hop STA Index
+ u8_l nhop_sta_idx;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+/////////// For Debug messages
+///////////////////////////////////////////////////////////////////////////////
+
+/// Messages related to Debug Task
+enum dbg_msg_tag
+{
+ /// Memory read request
+ DBG_MEM_READ_REQ = LMAC_FIRST_MSG(TASK_DBG),
+ /// Memory read confirm
+ DBG_MEM_READ_CFM,
+ /// Memory write request
+ DBG_MEM_WRITE_REQ,
+ /// Memory write confirm
+ DBG_MEM_WRITE_CFM,
+ /// Module filter request
+ DBG_SET_MOD_FILTER_REQ,
+ /// Module filter confirm
+ DBG_SET_MOD_FILTER_CFM,
+ /// Severity filter request
+ DBG_SET_SEV_FILTER_REQ,
+ /// Severity filter confirm
+ DBG_SET_SEV_FILTER_CFM,
+ /// LMAC/MAC HW fatal error indication
+ DBG_ERROR_IND,
+ /// Request to get system statistics
+ DBG_GET_SYS_STAT_REQ,
+ /// COnfirmation of system statistics
+ DBG_GET_SYS_STAT_CFM,
+ /// Memory block write request
+ DBG_MEM_BLOCK_WRITE_REQ,
+ /// Memory block write confirm
+ DBG_MEM_BLOCK_WRITE_CFM,
+ /// Start app request
+ DBG_START_APP_REQ,
+ /// Start app confirm
+ DBG_START_APP_CFM,
+ /// Start npc request
+ DBG_START_NPC_REQ,
+ /// Start npc confirm
+ DBG_START_NPC_CFM,
+ /// Memory mask write request
+ DBG_MEM_MASK_WRITE_REQ,
+ /// Memory mask write confirm
+ DBG_MEM_MASK_WRITE_CFM,
+
+ DBG_RFTEST_CMD_REQ,
+ DBG_RFTEST_CMD_CFM,
+ DBG_BINDING_REQ,
+ DBG_BINDING_CFM,
+ DBG_BINDING_IND,
+
+ DBG_CUSTOM_MSG_REQ,
+ DBG_CUSTOM_MSG_CFM,
+ DBG_CUSTOM_MSG_IND,
+
+ DBG_GPIO_WRITE_REQ,
+ DBG_GPIO_WRITE_CFM,
+ DBG_GPIO_READ_REQ,
+ DBG_GPIO_READ_CFM,
+ DBG_GPIO_INIT_REQ,
+ DBG_GPIO_INIT_CFM,
+
+ /// Max number of Debug messages
+ DBG_MAX,
+};
+
+/// Structure containing the parameters of the @ref DBG_MEM_READ_REQ message.
+struct dbg_mem_read_req
+{
+ u32_l memaddr;
+};
+
+/// Structure containing the parameters of the @ref DBG_MEM_READ_CFM message.
+struct dbg_mem_read_cfm
+{
+ u32_l memaddr;
+ u32_l memdata;
+};
+
+/// Structure containing the parameters of the @ref DBG_MEM_WRITE_REQ message.
+struct dbg_mem_write_req
+{
+ u32_l memaddr;
+ u32_l memdata;
+};
+
+/// Structure containing the parameters of the @ref DBG_MEM_WRITE_CFM message.
+struct dbg_mem_write_cfm
+{
+ u32_l memaddr;
+ u32_l memdata;
+};
+
+/// Structure containing the parameters of the @ref DBG_MEM_MASK_WRITE_REQ message.
+struct dbg_mem_mask_write_req
+{
+ u32_l memaddr;
+ u32_l memmask;
+ u32_l memdata;
+};
+
+/// Structure containing the parameters of the @ref DBG_MEM_MASK_WRITE_CFM message.
+struct dbg_mem_mask_write_cfm
+{
+ u32_l memaddr;
+ u32_l memdata;
+};
+
+struct dbg_rftest_cmd_req
+{
+ u32_l cmd;
+ u32_l argc;
+ u8_l argv[30];
+};
+
+struct dbg_rftest_cmd_cfm
+{
+ u32_l rftest_result[18];
+};
+
+struct dbg_gpio_write_req {
+ uint8_t gpio_idx;
+ uint8_t gpio_val;
+};
+
+struct dbg_gpio_read_req {
+ uint8_t gpio_idx;
+};
+
+struct dbg_gpio_read_cfm {
+ uint8_t gpio_idx;
+ uint8_t gpio_val;
+};
+
+struct dbg_gpio_init_req {
+ uint8_t gpio_idx;
+ uint8_t gpio_dir; //1 output, 0 input;
+ uint8_t gpio_val; //for output, 1 high, 0 low;
+};
+
+/// Structure containing the parameters of the @ref DBG_SET_MOD_FILTER_REQ message.
+struct dbg_set_mod_filter_req
+{
+ /// Bit field indicating for each module if the traces are enabled or not
+ u32_l mod_filter;
+};
+
+/// Structure containing the parameters of the @ref DBG_SEV_MOD_FILTER_REQ message.
+struct dbg_set_sev_filter_req
+{
+ /// Bit field indicating the severity threshold for the traces
+ u32_l sev_filter;
+};
+
+/// Structure containing the parameters of the @ref DBG_GET_SYS_STAT_CFM message.
+struct dbg_get_sys_stat_cfm
+{
+ /// Time spent in CPU sleep since last reset of the system statistics
+ u32_l cpu_sleep_time;
+ /// Time spent in DOZE since last reset of the system statistics
+ u32_l doze_time;
+ /// Total time spent since last reset of the system statistics
+ u32_l stats_time;
+};
+
+/// Structure containing the parameters of the @ref DBG_MEM_BLOCK_WRITE_REQ message.
+struct dbg_mem_block_write_req
+{
+ u32_l memaddr;
+ u32_l memsize;
+ u32_l memdata[512 / sizeof(u32_l)];//1024 for 8801
+};
+
+/// Structure containing the parameters of the @ref DBG_MEM_BLOCK_WRITE_CFM message.
+struct dbg_mem_block_write_cfm
+{
+ u32_l wstatus;
+};
+
+/// Structure containing the parameters of the @ref DBG_START_APP_REQ message.
+struct dbg_start_app_req
+{
+ u32_l bootaddr;
+ u32_l boottype;
+};
+
+/// Structure containing the parameters of the @ref DBG_START_APP_CFM message.
+struct dbg_start_app_cfm
+{
+ u32_l bootstatus;
+};
+
+enum {
+ HOST_START_APP_AUTO = 1,
+ HOST_START_APP_CUSTOM,
+#ifdef CONFIG_USB_BT
+ HOST_START_APP_REBOOT,
+#endif // (CONFIG_USB_BT)
+ HOST_START_APP_FNCALL = 4,
+ HOST_START_APP_DUMMY = 5,
+};
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+/////////// For TDLS messages
+///////////////////////////////////////////////////////////////////////////////
+
+/// List of messages related to the task.
+enum tdls_msg_tag
+{
+ /// TDLS channel Switch Request.
+ TDLS_CHAN_SWITCH_REQ = LMAC_FIRST_MSG(TASK_TDLS),
+ /// TDLS channel switch confirmation.
+ TDLS_CHAN_SWITCH_CFM,
+ /// TDLS channel switch indication.
+ TDLS_CHAN_SWITCH_IND,
+ /// TDLS channel switch to base channel indication.
+ TDLS_CHAN_SWITCH_BASE_IND,
+ /// TDLS cancel channel switch request.
+ TDLS_CANCEL_CHAN_SWITCH_REQ,
+ /// TDLS cancel channel switch confirmation.
+ TDLS_CANCEL_CHAN_SWITCH_CFM,
+ /// TDLS peer power save indication.
+ TDLS_PEER_PS_IND,
+ /// TDLS peer traffic indication request.
+ TDLS_PEER_TRAFFIC_IND_REQ,
+ /// TDLS peer traffic indication confirmation.
+ TDLS_PEER_TRAFFIC_IND_CFM,
+ /// MAX number of messages
+ TDLS_MAX
+};
+
+/// Structure containing the parameters of the @ref TDLS_CHAN_SWITCH_REQ message
+struct tdls_chan_switch_req
+{
+ /// Index of the VIF
+ u8_l vif_index;
+ /// STA Index
+ u8_l sta_idx;
+ /// MAC address of the TDLS station
+ struct mac_addr peer_mac_addr;
+ bool_l initiator;
+ /// Band (2.4GHz or 5GHz)
+ u8_l band;
+ /// Channel type: 20,40,80,160 or 80+80 MHz
+ u8_l type;
+ /// Frequency for Primary 20MHz channel (in MHz)
+ u16_l prim20_freq;
+ /// Frequency for Center of the contiguous channel or center of Primary 80+80
+ u16_l center1_freq;
+ /// Frequency for Center of the non-contiguous secondary 80+80
+ u16_l center2_freq;
+ /// TX power (in dBm)
+ s8_l tx_power;
+ /// Operating class
+ u8_l op_class;
+};
+
+/// Structure containing the parameters of the @ref TDLS_CANCEL_CHAN_SWITCH_REQ message
+struct tdls_cancel_chan_switch_req
+{
+ /// Index of the VIF
+ u8_l vif_index;
+ /// STA Index
+ u8_l sta_idx;
+ /// MAC address of the TDLS station
+ struct mac_addr peer_mac_addr;
+};
+
+
+/// Structure containing the parameters of the @ref TDLS_CHAN_SWITCH_CFM message
+struct tdls_chan_switch_cfm
+{
+ /// Status of the operation
+ u8_l status;
+};
+
+/// Structure containing the parameters of the @ref TDLS_CANCEL_CHAN_SWITCH_CFM message
+struct tdls_cancel_chan_switch_cfm
+{
+ /// Status of the operation
+ u8_l status;
+};
+
+/// Structure containing the parameters of the @ref TDLS_CHAN_SWITCH_IND message
+struct tdls_chan_switch_ind
+{
+ /// VIF Index
+ u8_l vif_index;
+ /// Channel Context Index
+ u8_l chan_ctxt_index;
+ /// Status of the operation
+ u8_l status;
+};
+
+/// Structure containing the parameters of the @ref TDLS_CHAN_SWITCH_BASE_IND message
+struct tdls_chan_switch_base_ind
+{
+ /// VIF Index
+ u8_l vif_index;
+ /// Channel Context index
+ u8_l chan_ctxt_index;
+};
+
+/// Structure containing the parameters of the @ref TDLS_PEER_PS_IND message
+struct tdls_peer_ps_ind
+{
+ /// VIF Index
+ u8_l vif_index;
+ /// STA Index
+ u8_l sta_idx;
+ /// MAC ADDR of the TDLS STA
+ struct mac_addr peer_mac_addr;
+ /// Flag to indicate if the TDLS peer is going to sleep
+ bool ps_on;
+};
+
+/// Structure containing the parameters of the @ref TDLS_PEER_TRAFFIC_IND_REQ message
+struct tdls_peer_traffic_ind_req
+{
+ /// VIF Index
+ u8_l vif_index;
+ /// STA Index
+ u8_l sta_idx;
+ // MAC ADDR of the TDLS STA
+ struct mac_addr peer_mac_addr;
+ /// Dialog token
+ u8_l dialog_token;
+ /// TID of the latest MPDU transmitted over the TDLS direct link to the TDLS STA
+ u8_l last_tid;
+ /// Sequence number of the latest MPDU transmitted over the TDLS direct link
+ /// to the TDLS STA
+ u16_l last_sn;
+};
+
+/// Structure containing the parameters of the @ref TDLS_PEER_TRAFFIC_IND_CFM message
+struct tdls_peer_traffic_ind_cfm
+{
+ /// Status of the operation
+ u8_l status;
+};
+
+
+#endif // LMAC_MSG_H_
diff --git a/drivers/net/wireless/aic8800/aic8800_fdrv/lmac_types.h b/drivers/net/wireless/aic8800/aic8800_fdrv/lmac_types.h
new file mode 100644
index 000000000000..83b112290409
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic8800_fdrv/lmac_types.h
@@ -0,0 +1,62 @@
+/**
+ ****************************************************************************************
+ *
+ * @file co_types.h
+ *
+ * @brief This file replaces the need to include stdint or stdbool typical headers,
+ * which may not be available in all toolchains, and adds new types
+ *
+ * Copyright (C) RivieraWaves 2009-2019
+ *
+ * $Rev: $
+ *
+ ****************************************************************************************
+ */
+
+#ifndef _LMAC_INT_H_
+#define _LMAC_INT_H_
+
+
+/**
+ ****************************************************************************************
+ * @addtogroup CO_INT
+ * @ingroup COMMON
+ * @brief Common integer standard types (removes use of stdint)
+ *
+ * @{
+ ****************************************************************************************
+ */
+
+
+/*
+ * DEFINES
+ ****************************************************************************************
+ */
+
+#include <linux/version.h>
+#include <linux/types.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)
+#include <linux/bits.h>
+#else
+#include <linux/bitops.h>
+#endif
+
+#ifdef CONFIG_RWNX_TL4
+typedef uint16_t u8_l;
+typedef int16_t s8_l;
+typedef uint16_t bool_l;
+#else
+typedef uint8_t u8_l;
+typedef int8_t s8_l;
+typedef bool bool_l;
+#endif
+typedef uint16_t u16_l;
+typedef int16_t s16_l;
+typedef uint32_t u32_l;
+typedef int32_t s32_l;
+typedef uint64_t u64_l;
+
+
+
+/// @} CO_INT
+#endif // _LMAC_INT_H_
diff --git a/drivers/net/wireless/aic8800/aic8800_fdrv/md5.c b/drivers/net/wireless/aic8800/aic8800_fdrv/md5.c
new file mode 100644
index 000000000000..3d7b65375adb
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic8800_fdrv/md5.c
@@ -0,0 +1,161 @@
+#include <linux/memory.h>
+#include "md5.h"
+
+unsigned char PADDING[]={0x80,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};
+
+void MD5Init(MD5_CTX *context)
+{
+ context->count[0] = 0;
+ context->count[1] = 0;
+ context->state[0] = 0x67452301;
+ context->state[1] = 0xEFCDAB89;
+ context->state[2] = 0x98BADCFE;
+ context->state[3] = 0x10325476;
+}
+void MD5Update(MD5_CTX *context,unsigned char *input,unsigned int inputlen)
+{
+ unsigned int i = 0,index = 0,partlen = 0;
+ index = (context->count[0] >> 3) & 0x3F;
+ partlen = 64 - index;
+ context->count[0] += inputlen << 3;
+ if(context->count[0] < (inputlen << 3))
+ context->count[1]++;
+ context->count[1] += inputlen >> 29;
+
+ if(inputlen >= partlen)
+ {
+ memcpy(&context->buffer[index],input,partlen);
+ MD5Transform(context->state,context->buffer);
+ for(i = partlen;i+64 <= inputlen;i+=64)
+ MD5Transform(context->state,&input[i]);
+ index = 0;
+ }
+ else
+ {
+ i = 0;
+ }
+ memcpy(&context->buffer[index],&input[i],inputlen-i);
+}
+void MD5Final(MD5_CTX *context,unsigned char digest[16])
+{
+ unsigned int index = 0,padlen = 0;
+ unsigned char bits[8];
+ index = (context->count[0] >> 3) & 0x3F;
+ padlen = (index < 56)?(56-index):(120-index);
+ MD5Encode(bits,context->count,8);
+ MD5Update(context,PADDING,padlen);
+ MD5Update(context,bits,8);
+ MD5Encode(digest,context->state,16);
+}
+void MD5Encode(unsigned char *output,unsigned int *input,unsigned int len)
+{
+ unsigned int i = 0,j = 0;
+ while(j < len)
+ {
+ output[j] = input[i] & 0xFF;
+ output[j+1] = (input[i] >> 8) & 0xFF;
+ output[j+2] = (input[i] >> 16) & 0xFF;
+ output[j+3] = (input[i] >> 24) & 0xFF;
+ i++;
+ j+=4;
+ }
+}
+void MD5Decode(unsigned int *output,unsigned char *input,unsigned int len)
+{
+ unsigned int i = 0,j = 0;
+ while(j < len)
+ {
+ output[i] = (input[j]) |
+ (input[j+1] << 8) |
+ (input[j+2] << 16) |
+ (input[j+3] << 24);
+ i++;
+ j+=4;
+ }
+}
+void MD5Transform(unsigned int state[4],unsigned char block[64])
+{
+ unsigned int a = state[0];
+ unsigned int b = state[1];
+ unsigned int c = state[2];
+ unsigned int d = state[3];
+ unsigned int x[64];
+ MD5Decode(x,block,64);
+ FF(a, b, c, d, x[ 0], 7, 0xd76aa478); /* 1 */
+ FF(d, a, b, c, x[ 1], 12, 0xe8c7b756); /* 2 */
+ FF(c, d, a, b, x[ 2], 17, 0x242070db); /* 3 */
+ FF(b, c, d, a, x[ 3], 22, 0xc1bdceee); /* 4 */
+ FF(a, b, c, d, x[ 4], 7, 0xf57c0faf); /* 5 */
+ FF(d, a, b, c, x[ 5], 12, 0x4787c62a); /* 6 */
+ FF(c, d, a, b, x[ 6], 17, 0xa8304613); /* 7 */
+ FF(b, c, d, a, x[ 7], 22, 0xfd469501); /* 8 */
+ FF(a, b, c, d, x[ 8], 7, 0x698098d8); /* 9 */
+ FF(d, a, b, c, x[ 9], 12, 0x8b44f7af); /* 10 */
+ FF(c, d, a, b, x[10], 17, 0xffff5bb1); /* 11 */
+ FF(b, c, d, a, x[11], 22, 0x895cd7be); /* 12 */
+ FF(a, b, c, d, x[12], 7, 0x6b901122); /* 13 */
+ FF(d, a, b, c, x[13], 12, 0xfd987193); /* 14 */
+ FF(c, d, a, b, x[14], 17, 0xa679438e); /* 15 */
+ FF(b, c, d, a, x[15], 22, 0x49b40821); /* 16 */
+
+ /* Round 2 */
+ GG(a, b, c, d, x[ 1], 5, 0xf61e2562); /* 17 */
+ GG(d, a, b, c, x[ 6], 9, 0xc040b340); /* 18 */
+ GG(c, d, a, b, x[11], 14, 0x265e5a51); /* 19 */
+ GG(b, c, d, a, x[ 0], 20, 0xe9b6c7aa); /* 20 */
+ GG(a, b, c, d, x[ 5], 5, 0xd62f105d); /* 21 */
+ GG(d, a, b, c, x[10], 9, 0x2441453); /* 22 */
+ GG(c, d, a, b, x[15], 14, 0xd8a1e681); /* 23 */
+ GG(b, c, d, a, x[ 4], 20, 0xe7d3fbc8); /* 24 */
+ GG(a, b, c, d, x[ 9], 5, 0x21e1cde6); /* 25 */
+ GG(d, a, b, c, x[14], 9, 0xc33707d6); /* 26 */
+ GG(c, d, a, b, x[ 3], 14, 0xf4d50d87); /* 27 */
+ GG(b, c, d, a, x[ 8], 20, 0x455a14ed); /* 28 */
+ GG(a, b, c, d, x[13], 5, 0xa9e3e905); /* 29 */
+ GG(d, a, b, c, x[ 2], 9, 0xfcefa3f8); /* 30 */
+ GG(c, d, a, b, x[ 7], 14, 0x676f02d9); /* 31 */
+ GG(b, c, d, a, x[12], 20, 0x8d2a4c8a); /* 32 */
+
+ /* Round 3 */
+ HH(a, b, c, d, x[ 5], 4, 0xfffa3942); /* 33 */
+ HH(d, a, b, c, x[ 8], 11, 0x8771f681); /* 34 */
+ HH(c, d, a, b, x[11], 16, 0x6d9d6122); /* 35 */
+ HH(b, c, d, a, x[14], 23, 0xfde5380c); /* 36 */
+ HH(a, b, c, d, x[ 1], 4, 0xa4beea44); /* 37 */
+ HH(d, a, b, c, x[ 4], 11, 0x4bdecfa9); /* 38 */
+ HH(c, d, a, b, x[ 7], 16, 0xf6bb4b60); /* 39 */
+ HH(b, c, d, a, x[10], 23, 0xbebfbc70); /* 40 */
+ HH(a, b, c, d, x[13], 4, 0x289b7ec6); /* 41 */
+ HH(d, a, b, c, x[ 0], 11, 0xeaa127fa); /* 42 */
+ HH(c, d, a, b, x[ 3], 16, 0xd4ef3085); /* 43 */
+ HH(b, c, d, a, x[ 6], 23, 0x4881d05); /* 44 */
+ HH(a, b, c, d, x[ 9], 4, 0xd9d4d039); /* 45 */
+ HH(d, a, b, c, x[12], 11, 0xe6db99e5); /* 46 */
+ HH(c, d, a, b, x[15], 16, 0x1fa27cf8); /* 47 */
+ HH(b, c, d, a, x[ 2], 23, 0xc4ac5665); /* 48 */
+
+ /* Round 4 */
+ II(a, b, c, d, x[ 0], 6, 0xf4292244); /* 49 */
+ II(d, a, b, c, x[ 7], 10, 0x432aff97); /* 50 */
+ II(c, d, a, b, x[14], 15, 0xab9423a7); /* 51 */
+ II(b, c, d, a, x[ 5], 21, 0xfc93a039); /* 52 */
+ II(a, b, c, d, x[12], 6, 0x655b59c3); /* 53 */
+ II(d, a, b, c, x[ 3], 10, 0x8f0ccc92); /* 54 */
+ II(c, d, a, b, x[10], 15, 0xffeff47d); /* 55 */
+ II(b, c, d, a, x[ 1], 21, 0x85845dd1); /* 56 */
+ II(a, b, c, d, x[ 8], 6, 0x6fa87e4f); /* 57 */
+ II(d, a, b, c, x[15], 10, 0xfe2ce6e0); /* 58 */
+ II(c, d, a, b, x[ 6], 15, 0xa3014314); /* 59 */
+ II(b, c, d, a, x[13], 21, 0x4e0811a1); /* 60 */
+ II(a, b, c, d, x[ 4], 6, 0xf7537e82); /* 61 */
+ II(d, a, b, c, x[11], 10, 0xbd3af235); /* 62 */
+ II(c, d, a, b, x[ 2], 15, 0x2ad7d2bb); /* 63 */
+ II(b, c, d, a, x[ 9], 21, 0xeb86d391); /* 64 */
+ state[0] += a;
+ state[1] += b;
+ state[2] += c;
+ state[3] += d;
+}
diff --git a/drivers/net/wireless/aic8800/aic8800_fdrv/md5.h b/drivers/net/wireless/aic8800/aic8800_fdrv/md5.h
new file mode 100644
index 000000000000..6ed5c0f8e886
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic8800_fdrv/md5.h
@@ -0,0 +1,48 @@
+#ifndef MD5_H
+#define MD5_H
+
+typedef struct
+{
+ unsigned int count[2];
+ unsigned int state[4];
+ unsigned char buffer[64];
+}MD5_CTX;
+
+
+#define F(x,y,z) ((x & y) | (~x & z))
+#define G(x,y,z) ((x & z) | (y & ~z))
+#define H(x,y,z) (x^y^z)
+#define I(x,y,z) (y ^ (x | ~z))
+#define ROTATE_LEFT(x,n) ((x << n) | (x >> (32-n)))
+#define FF(a,b,c,d,x,s,ac) \
+ { \
+ a += F(b,c,d) + x + ac; \
+ a = ROTATE_LEFT(a,s); \
+ a += b; \
+ }
+#define GG(a,b,c,d,x,s,ac) \
+ { \
+ a += G(b,c,d) + x + ac; \
+ a = ROTATE_LEFT(a,s); \
+ a += b; \
+ }
+#define HH(a,b,c,d,x,s,ac) \
+ { \
+ a += H(b,c,d) + x + ac; \
+ a = ROTATE_LEFT(a,s); \
+ a += b; \
+ }
+#define II(a,b,c,d,x,s,ac) \
+ { \
+ a += I(b,c,d) + x + ac; \
+ a = ROTATE_LEFT(a,s); \
+ a += b; \
+ }
+void MD5Init(MD5_CTX *context);
+void MD5Update(MD5_CTX *context,unsigned char *input,unsigned int inputlen);
+void MD5Final(MD5_CTX *context,unsigned char digest[16]);
+void MD5Transform(unsigned int state[4],unsigned char block[64]);
+void MD5Encode(unsigned char *output,unsigned int *input,unsigned int len);
+void MD5Decode(unsigned int *output,unsigned char *input,unsigned int len);
+
+#endif
diff --git a/drivers/net/wireless/aic8800/aic8800_fdrv/reg_access.h b/drivers/net/wireless/aic8800/aic8800_fdrv/reg_access.h
new file mode 100644
index 000000000000..b1d9c014e013
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic8800_fdrv/reg_access.h
@@ -0,0 +1,161 @@
+/**
+ ******************************************************************************
+ *
+ * @file reg_access.h
+ *
+ * @brief Definitions and macros for MAC HW and platform register accesses
+ *
+ * Copyright (C) RivieraWaves 2011-2019
+ *
+ ******************************************************************************
+ */
+
+#ifndef REG_ACCESS_H_
+#define REG_ACCESS_H_
+
+/*****************************************************************************
+ * Addresses within RWNX_ADDR_CPU
+ *****************************************************************************/
+#define RAM_LMAC_FW_ADDR 0x00150000
+
+#define ROM_FMAC_FW_ADDR 0x00010000
+#define RAM_FMAC_FW_ADDR 0x00120000
+#define ROM_FMAC_PATCH_ADDR 0x00180000
+#ifdef CONFIG_DPD
+#define ROM_FMAC_CALIB_ADDR 0x00130000
+#endif
+
+
+/*****************************************************************************
+ * Addresses within RWNX_ADDR_SYSTEM
+ *****************************************************************************/
+/* Shard RAM */
+#define SHARED_RAM_START_ADDR 0x00000000
+
+/* IPC registers */
+#define IPC_REG_BASE_ADDR 0x00800000
+
+/* System Controller Registers */
+#define SYSCTRL_SIGNATURE_ADDR 0x00900000
+// old diag register name
+#define SYSCTRL_DIAG_CONF_ADDR 0x00900068
+#define SYSCTRL_PHYDIAG_CONF_ADDR 0x00900074
+#define SYSCTRL_RIUDIAG_CONF_ADDR 0x00900078
+// new diag register name
+#define SYSCTRL_DIAG_CONF0 0x00900064
+#define SYSCTRL_DIAG_CONF1 0x00900068
+#define SYSCTRL_DIAG_CONF2 0x00900074
+#define SYSCTRL_DIAG_CONF3 0x00900078
+#define SYSCTRL_MISC_CNTL_ADDR 0x009000E0
+#define BOOTROM_ENABLE BIT(4)
+#define FPGA_B_RESET BIT(1)
+#define SOFT_RESET BIT(0)
+
+/* MAC platform */
+#define NXMAC_VERSION_1_ADDR 0x00B00004
+#define NXMAC_MU_MIMO_TX_BIT BIT(19)
+#define NXMAC_BFMER_BIT BIT(18)
+#define NXMAC_BFMEE_BIT BIT(17)
+#define NXMAC_MAC_80211MH_FORMAT_BIT BIT(16)
+#define NXMAC_COEX_BIT BIT(14)
+#define NXMAC_WAPI_BIT BIT(13)
+#define NXMAC_TPC_BIT BIT(12)
+#define NXMAC_VHT_BIT BIT(11)
+#define NXMAC_HT_BIT BIT(10)
+#define NXMAC_RCE_BIT BIT(8)
+#define NXMAC_CCMP_BIT BIT(7)
+#define NXMAC_TKIP_BIT BIT(6)
+#define NXMAC_WEP_BIT BIT(5)
+#define NXMAC_SECURITY_BIT BIT(4)
+#define NXMAC_SME_BIT BIT(3)
+#define NXMAC_HCCA_BIT BIT(2)
+#define NXMAC_EDCA_BIT BIT(1)
+#define NXMAC_QOS_BIT BIT(0)
+
+#define NXMAC_RX_CNTRL_ADDR 0x00B00060
+#define NXMAC_EN_DUPLICATE_DETECTION_BIT BIT(31)
+#define NXMAC_ACCEPT_UNKNOWN_BIT BIT(30)
+#define NXMAC_ACCEPT_OTHER_DATA_FRAMES_BIT BIT(29)
+#define NXMAC_ACCEPT_QO_S_NULL_BIT BIT(28)
+#define NXMAC_ACCEPT_QCFWO_DATA_BIT BIT(27)
+#define NXMAC_ACCEPT_Q_DATA_BIT BIT(26)
+#define NXMAC_ACCEPT_CFWO_DATA_BIT BIT(25)
+#define NXMAC_ACCEPT_DATA_BIT BIT(24)
+#define NXMAC_ACCEPT_OTHER_CNTRL_FRAMES_BIT BIT(23)
+#define NXMAC_ACCEPT_CF_END_BIT BIT(22)
+#define NXMAC_ACCEPT_ACK_BIT BIT(21)
+#define NXMAC_ACCEPT_CTS_BIT BIT(20)
+#define NXMAC_ACCEPT_RTS_BIT BIT(19)
+#define NXMAC_ACCEPT_PS_POLL_BIT BIT(18)
+#define NXMAC_ACCEPT_BA_BIT BIT(17)
+#define NXMAC_ACCEPT_BAR_BIT BIT(16)
+#define NXMAC_ACCEPT_OTHER_MGMT_FRAMES_BIT BIT(15)
+#define NXMAC_ACCEPT_BFMEE_FRAMES_BIT BIT(14)
+#define NXMAC_ACCEPT_ALL_BEACON_BIT BIT(13)
+#define NXMAC_ACCEPT_NOT_EXPECTED_BA_BIT BIT(12)
+#define NXMAC_ACCEPT_DECRYPT_ERROR_FRAMES_BIT BIT(11)
+#define NXMAC_ACCEPT_BEACON_BIT BIT(10)
+#define NXMAC_ACCEPT_PROBE_RESP_BIT BIT(9)
+#define NXMAC_ACCEPT_PROBE_REQ_BIT BIT(8)
+#define NXMAC_ACCEPT_MY_UNICAST_BIT BIT(7)
+#define NXMAC_ACCEPT_UNICAST_BIT BIT(6)
+#define NXMAC_ACCEPT_ERROR_FRAMES_BIT BIT(5)
+#define NXMAC_ACCEPT_OTHER_BSSID_BIT BIT(4)
+#define NXMAC_ACCEPT_BROADCAST_BIT BIT(3)
+#define NXMAC_ACCEPT_MULTICAST_BIT BIT(2)
+#define NXMAC_DONT_DECRYPT_BIT BIT(1)
+#define NXMAC_EXC_UNENCRYPTED_BIT BIT(0)
+
+#define NXMAC_DEBUG_PORT_SEL_ADDR 0x00B00510
+#define NXMAC_SW_SET_PROFILING_ADDR 0x00B08564
+#define NXMAC_SW_CLEAR_PROFILING_ADDR 0x00B08568
+
+/* Modem Status */
+#define MDM_HDMCONFIG_ADDR 0x00C00000
+
+/* Clock gating configuration */
+#define MDM_MEMCLKCTRL0_ADDR 0x00C00848
+#define MDM_CLKGATEFCTRL0_ADDR 0x00C00874
+#define CRM_CLKGATEFCTRL0_ADDR 0x00940010
+
+/* AGC (trident) */
+#define AGC_RWNXAGCCNTL_ADDR 0x00C02060
+
+/* LDPC RAM*/
+#define PHY_LDPC_RAM_ADDR 0x00C09000
+
+/* FCU (elma )*/
+#define FCU_RWNXFCAGCCNTL_ADDR 0x00C09034
+
+/* AGC RAM */
+#define PHY_AGC_UCODE_ADDR 0x00C0A000
+
+/* RIU */
+#define RIU_RWNXVERSION_ADDR 0x00C0B000
+#define RIU_RWNXDYNAMICCONFIG_ADDR 0x00C0B008
+#define RIU_AGCMEMBISTSTAT_ADDR 0x00C0B238
+#define RIU_AGCMEMSIGNATURESTAT_ADDR 0x00C0B23C
+#define RIU_RWNXAGCCNTL_ADDR 0x00C0B390
+
+/* FCU RAM */
+#define PHY_FCU_UCODE_ADDR 0x00C0E000
+
+/* RF ITF */
+#define FPGAB_MPIF_SEL_ADDR 0x00C10030
+#define RF_V6_DIAGPORT_CONF1_ADDR 0x00C10010
+#define RF_v6_PHYDIAG_CONF1_ADDR 0x00C10018
+
+#define RF_V7_DIAGPORT_CONF1_ADDR 0x00F10010
+#define RF_v7_PHYDIAG_CONF1_ADDR 0x00F10018
+
+/*****************************************************************************
+ * Macros for generated register files
+ *****************************************************************************/
+/* Macros for IPC registers access (used in reg_ipc_app.h) */
+#define REG_IPC_APP_RD(env, INDEX) \
+ (*(volatile u32*)((u8*)env + IPC_REG_BASE_ADDR + 4*(INDEX)))
+
+#define REG_IPC_APP_WR(env, INDEX, value) \
+ (*(volatile u32*)((u8*)env + IPC_REG_BASE_ADDR + 4*(INDEX)) = value)
+
+#endif /* REG_ACCESS_H_ */
diff --git a/drivers/net/wireless/aic8800/aic8800_fdrv/reg_ipc_app.h b/drivers/net/wireless/aic8800/aic8800_fdrv/reg_ipc_app.h
new file mode 100644
index 000000000000..2a3b545f3add
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic8800_fdrv/reg_ipc_app.h
@@ -0,0 +1,299 @@
+/**
+ ****************************************************************************************
+ *
+ * @file rwnx_ipc_app.h
+ *
+ * @brief IPC module register definitions
+ *
+ * Copyright (C) RivieraWaves 2011-2019
+ *
+ ****************************************************************************************
+ */
+
+#ifndef _REG_IPC_APP_H_
+#define _REG_IPC_APP_H_
+
+#ifndef __KERNEL__
+#include <stdint.h>
+#include "arch.h"
+#else
+#include "ipc_compat.h"
+#endif
+#include "reg_access.h"
+
+#define REG_IPC_APP_DECODING_MASK 0x0000007F
+
+/**
+ * @brief APP2EMB_TRIGGER register definition
+ * <pre>
+ * Bits Field Name Reset Value
+ * ----- ------------------ -----------
+ * 31:00 APP2EMB_TRIGGER 0x0
+ * </pre>
+ */
+#define IPC_APP2EMB_TRIGGER_ADDR 0x12000000
+#define IPC_APP2EMB_TRIGGER_OFFSET 0x00000000
+#define IPC_APP2EMB_TRIGGER_INDEX 0x00000000
+#define IPC_APP2EMB_TRIGGER_RESET 0x00000000
+
+__INLINE u32 ipc_app2emb_trigger_get(void *env)
+{
+ return REG_IPC_APP_RD(env, IPC_APP2EMB_TRIGGER_INDEX);
+}
+
+__INLINE void ipc_app2emb_trigger_set(void *env, u32 value)
+{
+ REG_IPC_APP_WR(env, IPC_APP2EMB_TRIGGER_INDEX, value);
+}
+
+// field definitions
+#define IPC_APP2EMB_TRIGGER_MASK ((u32)0xFFFFFFFF)
+#define IPC_APP2EMB_TRIGGER_LSB 0
+#define IPC_APP2EMB_TRIGGER_WIDTH ((u32)0x00000020)
+
+#define IPC_APP2EMB_TRIGGER_RST 0x0
+
+__INLINE u32 ipc_app2emb_trigger_getf(void *env)
+{
+ u32 localVal = REG_IPC_APP_RD(env, IPC_APP2EMB_TRIGGER_INDEX);
+ ASSERT_ERR((localVal & ~((u32)0xFFFFFFFF)) == 0);
+ return (localVal >> 0);
+}
+
+__INLINE void ipc_app2emb_trigger_setf(void *env, u32 app2embtrigger)
+{
+ ASSERT_ERR((((u32)app2embtrigger << 0) & ~((u32)0xFFFFFFFF)) == 0);
+ REG_IPC_APP_WR(env, IPC_APP2EMB_TRIGGER_INDEX, (u32)app2embtrigger << 0);
+}
+
+/**
+ * @brief EMB2APP_RAWSTATUS register definition
+ * <pre>
+ * Bits Field Name Reset Value
+ * ----- ------------------ -----------
+ * 31:00 EMB2APP_RAWSTATUS 0x0
+ * </pre>
+ */
+#define IPC_EMB2APP_RAWSTATUS_ADDR 0x12000004
+#define IPC_EMB2APP_RAWSTATUS_OFFSET 0x00000004
+#define IPC_EMB2APP_RAWSTATUS_INDEX 0x00000001
+#define IPC_EMB2APP_RAWSTATUS_RESET 0x00000000
+
+__INLINE u32 ipc_emb2app_rawstatus_get(void *env)
+{
+ return REG_IPC_APP_RD(env, IPC_EMB2APP_RAWSTATUS_INDEX);
+}
+
+__INLINE void ipc_emb2app_rawstatus_set(void *env, u32 value)
+{
+ REG_IPC_APP_WR(env, IPC_EMB2APP_RAWSTATUS_INDEX, value);
+}
+
+// field definitions
+#define IPC_EMB2APP_RAWSTATUS_MASK ((u32)0xFFFFFFFF)
+#define IPC_EMB2APP_RAWSTATUS_LSB 0
+#define IPC_EMB2APP_RAWSTATUS_WIDTH ((u32)0x00000020)
+
+#define IPC_EMB2APP_RAWSTATUS_RST 0x0
+
+__INLINE u32 ipc_emb2app_rawstatus_getf(void *env)
+{
+ u32 localVal = REG_IPC_APP_RD(env, IPC_EMB2APP_RAWSTATUS_INDEX);
+ ASSERT_ERR((localVal & ~((u32)0xFFFFFFFF)) == 0);
+ return (localVal >> 0);
+}
+
+/**
+ * @brief EMB2APP_ACK register definition
+ * <pre>
+ * Bits Field Name Reset Value
+ * ----- ------------------ -----------
+ * 31:00 EMB2APP_ACK 0x0
+ * </pre>
+ */
+#define IPC_EMB2APP_ACK_ADDR 0x12000008
+#define IPC_EMB2APP_ACK_OFFSET 0x00000008
+#define IPC_EMB2APP_ACK_INDEX 0x00000002
+#define IPC_EMB2APP_ACK_RESET 0x00000000
+
+__INLINE u32 ipc_emb2app_ack_get(void *env)
+{
+ return REG_IPC_APP_RD(env, IPC_EMB2APP_ACK_INDEX);
+}
+
+__INLINE void ipc_emb2app_ack_clear(void *env, u32 value)
+{
+ REG_IPC_APP_WR(env, IPC_EMB2APP_ACK_INDEX, value);
+}
+
+// field definitions
+#define IPC_EMB2APP_ACK_MASK ((u32)0xFFFFFFFF)
+#define IPC_EMB2APP_ACK_LSB 0
+#define IPC_EMB2APP_ACK_WIDTH ((u32)0x00000020)
+
+#define IPC_EMB2APP_ACK_RST 0x0
+
+__INLINE u32 ipc_emb2app_ack_getf(void *env)
+{
+ u32 localVal = REG_IPC_APP_RD(env, IPC_EMB2APP_ACK_INDEX);
+ ASSERT_ERR((localVal & ~((u32)0xFFFFFFFF)) == 0);
+ return (localVal >> 0);
+}
+
+__INLINE void ipc_emb2app_ack_clearf(void *env, u32 emb2appack)
+{
+ ASSERT_ERR((((u32)emb2appack << 0) & ~((u32)0xFFFFFFFF)) == 0);
+ REG_IPC_APP_WR(env, IPC_EMB2APP_ACK_INDEX, (u32)emb2appack << 0);
+}
+
+/**
+ * @brief EMB2APP_UNMASK_SET register definition
+ * <pre>
+ * Bits Field Name Reset Value
+ * ----- ------------------ -----------
+ * 31:00 EMB2APP_UNMASK 0x0
+ * </pre>
+ */
+#define IPC_EMB2APP_UNMASK_SET_ADDR 0x1200000C
+#define IPC_EMB2APP_UNMASK_SET_OFFSET 0x0000000C
+#define IPC_EMB2APP_UNMASK_SET_INDEX 0x00000003
+#define IPC_EMB2APP_UNMASK_SET_RESET 0x00000000
+
+__INLINE u32 ipc_emb2app_unmask_get(void *env)
+{
+ return REG_IPC_APP_RD(env, IPC_EMB2APP_UNMASK_SET_INDEX);
+}
+
+__INLINE void ipc_emb2app_unmask_set(void *env, u32 value)
+{
+ REG_IPC_APP_WR(env, IPC_EMB2APP_UNMASK_SET_INDEX, value);
+}
+
+// field definitions
+#define IPC_EMB2APP_UNMASK_MASK ((u32)0xFFFFFFFF)
+#define IPC_EMB2APP_UNMASK_LSB 0
+#define IPC_EMB2APP_UNMASK_WIDTH ((u32)0x00000020)
+
+#define IPC_EMB2APP_UNMASK_RST 0x0
+
+__INLINE u32 ipc_emb2app_unmask_getf(void *env)
+{
+ u32 localVal = REG_IPC_APP_RD(env, IPC_EMB2APP_UNMASK_SET_INDEX);
+ ASSERT_ERR((localVal & ~((u32)0xFFFFFFFF)) == 0);
+ return (localVal >> 0);
+}
+
+__INLINE void ipc_emb2app_unmask_setf(void *env, u32 emb2appunmask)
+{
+ ASSERT_ERR((((u32)emb2appunmask << 0) & ~((u32)0xFFFFFFFF)) == 0);
+ REG_IPC_APP_WR(env, IPC_EMB2APP_UNMASK_SET_INDEX, (u32)emb2appunmask << 0);
+}
+
+/**
+ * @brief EMB2APP_UNMASK_CLEAR register definition
+ * <pre>
+ * Bits Field Name Reset Value
+ * ----- ------------------ -----------
+ * 31:00 EMB2APP_UNMASK 0x0
+ * </pre>
+ */
+#define IPC_EMB2APP_UNMASK_CLEAR_ADDR 0x12000010
+#define IPC_EMB2APP_UNMASK_CLEAR_OFFSET 0x00000010
+#define IPC_EMB2APP_UNMASK_CLEAR_INDEX 0x00000004
+#define IPC_EMB2APP_UNMASK_CLEAR_RESET 0x00000000
+
+__INLINE void ipc_emb2app_unmask_clear(void *env, u32 value)
+{
+ REG_IPC_APP_WR(env, IPC_EMB2APP_UNMASK_CLEAR_INDEX, value);
+}
+
+// fields defined in symmetrical set/clear register
+__INLINE void ipc_emb2app_unmask_clearf(void *env, u32 emb2appunmask)
+{
+ ASSERT_ERR((((u32)emb2appunmask << 0) & ~((u32)0xFFFFFFFF)) == 0);
+ REG_IPC_APP_WR(env, IPC_EMB2APP_UNMASK_CLEAR_INDEX, (u32)emb2appunmask << 0);
+}
+
+/**
+ * @brief EMB2APP_STATUS register definition
+ * <pre>
+ * Bits Field Name Reset Value
+ * ----- ------------------ -----------
+ * 31:00 EMB2APP_STATUS 0x0
+ * </pre>
+ */
+#ifdef CONFIG_RWNX_OLD_IPC
+#define IPC_EMB2APP_STATUS_ADDR 0x12000014
+#define IPC_EMB2APP_STATUS_OFFSET 0x00000014
+#define IPC_EMB2APP_STATUS_INDEX 0x00000005
+#else
+#define IPC_EMB2APP_STATUS_ADDR 0x1200001C
+#define IPC_EMB2APP_STATUS_OFFSET 0x0000001C
+#define IPC_EMB2APP_STATUS_INDEX 0x00000007
+#endif
+#define IPC_EMB2APP_STATUS_RESET 0x00000000
+
+__INLINE u32 ipc_emb2app_status_get(void *env)
+{
+ return REG_IPC_APP_RD(env, IPC_EMB2APP_STATUS_INDEX);
+}
+
+__INLINE void ipc_emb2app_status_set(void *env, u32 value)
+{
+ REG_IPC_APP_WR(env, IPC_EMB2APP_STATUS_INDEX, value);
+}
+
+// field definitions
+#define IPC_EMB2APP_STATUS_MASK ((u32)0xFFFFFFFF)
+#define IPC_EMB2APP_STATUS_LSB 0
+#define IPC_EMB2APP_STATUS_WIDTH ((u32)0x00000020)
+
+#define IPC_EMB2APP_STATUS_RST 0x0
+
+__INLINE u32 ipc_emb2app_status_getf(void *env)
+{
+ u32 localVal = REG_IPC_APP_RD(env, IPC_EMB2APP_STATUS_INDEX);
+ ASSERT_ERR((localVal & ~((u32)0xFFFFFFFF)) == 0);
+ return (localVal >> 0);
+}
+
+/**
+ * @brief APP_SIGNATURE register definition
+ * <pre>
+ * Bits Field Name Reset Value
+ * ----- ------------------ ----------
+ * 31:00 APP_SIGNATURE 0x0
+ * </pre>
+ */
+#define IPC_APP_SIGNATURE_ADDR 0x12000040
+#define IPC_APP_SIGNATURE_OFFSET 0x00000040
+#define IPC_APP_SIGNATURE_INDEX 0x00000010
+#define IPC_APP_SIGNATURE_RESET 0x00000000
+
+__INLINE u32 ipc_app_signature_get(void *env)
+{
+ return REG_IPC_APP_RD(env, IPC_APP_SIGNATURE_INDEX);
+}
+
+__INLINE void ipc_app_signature_set(void *env, u32 value)
+{
+ REG_IPC_APP_WR(env, IPC_APP_SIGNATURE_INDEX, value);
+}
+
+// field definitions
+#define IPC_APP_SIGNATURE_MASK ((u32)0xFFFFFFFF)
+#define IPC_APP_SIGNATURE_LSB 0
+#define IPC_APP_SIGNATURE_WIDTH ((u32)0x00000020)
+
+#define IPC_APP_SIGNATURE_RST 0x0
+
+__INLINE u32 ipc_app_signature_getf(void *env)
+{
+ u32 localVal = REG_IPC_APP_RD(env, IPC_APP_SIGNATURE_INDEX);
+ ASSERT_ERR((localVal & ~((u32)0xFFFFFFFF)) == 0);
+ return (localVal >> 0);
+}
+
+
+#endif // _REG_IPC_APP_H_
+
diff --git a/drivers/net/wireless/aic8800/aic8800_fdrv/regdb.c b/drivers/net/wireless/aic8800/aic8800_fdrv/regdb.c
new file mode 100644
index 000000000000..ebc77a3e59a5
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic8800_fdrv/regdb.c
@@ -0,0 +1,2873 @@
+#include <linux/nl80211.h>
+#include <net/cfg80211.h>
+#include <linux/version.h>
+
+//#include "regdb.h"
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 15, 0)
+#define REG_RULE_EXT(start, end, bw, gain, eirp, dfs_cac, reg_flags) \
+{ \
+ .freq_range.start_freq_khz = MHZ_TO_KHZ(start), \
+ .freq_range.end_freq_khz = MHZ_TO_KHZ(end), \
+ .freq_range.max_bandwidth_khz = MHZ_TO_KHZ(bw), \
+ .power_rule.max_antenna_gain = DBI_TO_MBI(gain),\
+ .power_rule.max_eirp = DBM_TO_MBM(eirp), \
+ .flags = reg_flags, \
+}
+#define NL80211_RRF_AUTO_BW 0
+#endif
+
+static const struct ieee80211_regdomain regdom_00 = {
+ .n_reg_rules = 2,
+ .alpha2 = "00",
+ .reg_rules = {
+ // 1...14
+ REG_RULE(2390 - 10, 2510 + 10, 40, 0, 20, 0),
+ // 36...165
+ REG_RULE(5150 - 10, 5970 + 10, 80, 0, 20, 0),
+ }
+};
+
+static const struct ieee80211_regdomain regdom_AD = {
+ .alpha2 = "AD",
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 20, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(5490, 5710, 80, 0, 27, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0),
+ },
+ .n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_AE = {
+ .alpha2 = "AE",
+ .dfs_region = NL80211_DFS_FCC,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 17, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 24, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5730, 160, 0, 24, 0,
+ NL80211_RRF_DFS | 0),
+ //REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+ },
+ .n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_AF = {
+ .alpha2 = "AF",
+ .dfs_region = NL80211_DFS_ETSI,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 20, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 20, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5710, 160, 0, 27, 0,
+ NL80211_RRF_DFS | 0),
+ },
+ .n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_AI = {
+ .alpha2 = "AI",
+ .dfs_region = NL80211_DFS_ETSI,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 20, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 20, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5710, 160, 0, 27, 0,
+ NL80211_RRF_DFS | 0),
+ },
+ .n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_AL = {
+ .alpha2 = "AL",
+ .dfs_region = NL80211_DFS_ETSI,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 20, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 20, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5710, 160, 0, 27, 0,
+ NL80211_RRF_DFS | 0),
+ },
+ .n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_AM = {
+ .alpha2 = "AM",
+ .dfs_region = NL80211_DFS_ETSI,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 18, 0, 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 18, 0,
+ NL80211_RRF_DFS | 0),
+ },
+ .n_reg_rules = 3
+};
+
+static const struct ieee80211_regdomain regdom_AN = {
+ .alpha2 = "AN",
+ .dfs_region = NL80211_DFS_ETSI,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 20, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 20, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5710, 160, 0, 27, 0,
+ NL80211_RRF_DFS | 0),
+ },
+ .n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_AR = {
+ .alpha2 = "AR",
+ .dfs_region = NL80211_DFS_FCC,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ //REG_RULE_EXT(5170, 5250, 80, 0, 17, 0,
+ // NL80211_RRF_AUTO_BW | 0),
+ //REG_RULE_EXT(5250, 5330, 80, 0, 24, 0,
+ // NL80211_RRF_DFS |
+ // NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5270, 5330, 40, 0, 24, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ //REG_RULE_EXT(5490, 5730, 160, 0, 24, 0,
+ // NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(5735, 5815, 80, 0, 30, 0, 0),
+ },
+ .n_reg_rules = 3
+};
+
+static const struct ieee80211_regdomain regdom_AS = {
+ .alpha2 = "AS",
+ .dfs_region = NL80211_DFS_FCC,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2472, 40, 0, 30, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 24, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 24, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5730, 160, 0, 24, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+ },
+ .n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_AT = {
+ .alpha2 = "AT",
+ .dfs_region = NL80211_DFS_ETSI,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 20, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 20, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5710, 160, 0, 27, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0),
+ },
+ .n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_AU = {
+ .alpha2 = "AU",
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 17, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 24, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5710, 160, 0, 24, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+ },
+ .n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_AW = {
+ .alpha2 = "AW",
+ .dfs_region = NL80211_DFS_ETSI,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 20, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 20, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5710, 160, 0, 27, 0,
+ NL80211_RRF_DFS | 0),
+ },
+ .n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_AZ = {
+ .alpha2 = "AZ",
+ .dfs_region = NL80211_DFS_ETSI,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 18, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 18, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ },
+ .n_reg_rules = 3
+};
+
+static const struct ieee80211_regdomain regdom_BA = {
+ .alpha2 = "BA",
+ .dfs_region = NL80211_DFS_ETSI,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 20, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 20, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5710, 160, 0, 27, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0),
+ },
+ .n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_BB = {
+ .alpha2 = "BB",
+ .dfs_region = NL80211_DFS_FCC,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 23, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 23, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+ },
+ .n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_BD = {
+ .alpha2 = "BD",
+ .dfs_region = NL80211_DFS_JP,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+ },
+ .n_reg_rules = 2
+};
+
+static const struct ieee80211_regdomain regdom_BE = {
+ .alpha2 = "BE",
+ .dfs_region = NL80211_DFS_ETSI,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 20, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 20, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5710, 160, 0, 27, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0),
+ },
+ .n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_BF = {
+ .alpha2 = "BF",
+ .dfs_region = NL80211_DFS_FCC,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 17, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 24, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5730, 160, 0, 24, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+ },
+ .n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_BG = {
+ .alpha2 = "BG",
+ .dfs_region = NL80211_DFS_ETSI,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 20, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 20, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5710, 160, 0, 27, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0),
+ },
+ .n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_BH = {
+ .alpha2 = "BH",
+ .dfs_region = NL80211_DFS_JP,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 20, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(5735, 5835, 80, 0, 20, 0, 0),
+ },
+ .n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_BL = {
+ .alpha2 = "BL",
+ .dfs_region = NL80211_DFS_ETSI,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 20, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 20, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5710, 160, 0, 27, 0,
+ NL80211_RRF_DFS | 0),
+ },
+ .n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_BM = {
+ .alpha2 = "BM",
+ .dfs_region = NL80211_DFS_FCC,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2472, 40, 0, 30, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 24, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 24, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5730, 160, 0, 24, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+ },
+ .n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_BN = {
+ .alpha2 = "BN",
+ .dfs_region = NL80211_DFS_JP,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 20, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 20, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5735, 5835, 80, 0, 20, 0, 0),
+ },
+ .n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_BO = {
+ .alpha2 = "BO",
+ .dfs_region = NL80211_DFS_JP,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 30, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+ },
+ .n_reg_rules = 3
+};
+
+static const struct ieee80211_regdomain regdom_BR = {
+ .alpha2 = "BR",
+ .dfs_region = NL80211_DFS_FCC,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 17, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 24, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5730, 160, 0, 24, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+ },
+ .n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_BS = {
+ .alpha2 = "BS",
+ .dfs_region = NL80211_DFS_FCC,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 24, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 24, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5730, 160, 0, 24, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+ },
+ .n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_BT = {
+ .alpha2 = "BT",
+ .dfs_region = NL80211_DFS_ETSI,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 20, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 20, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5710, 160, 0, 27, 0,
+ NL80211_RRF_DFS | 0),
+ },
+ .n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_BY = {
+ .alpha2 = "BY",
+ .dfs_region = NL80211_DFS_ETSI,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 20, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 20, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5710, 160, 0, 27, 0,
+ NL80211_RRF_DFS | 0),
+ },
+ .n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_BZ = {
+ .alpha2 = "BZ",
+ .dfs_region = NL80211_DFS_JP,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 30, 0, 0),
+ REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+ },
+ .n_reg_rules = 2
+};
+
+static const struct ieee80211_regdomain regdom_CA = {
+ .alpha2 = "CA",
+ .dfs_region = NL80211_DFS_FCC,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2472, 40, 0, 30, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 17, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 24, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5730, 160, 0, 24, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+ },
+ .n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_CF = {
+ .alpha2 = "CF",
+ .dfs_region = NL80211_DFS_FCC,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 40, 0, 17, 0, 0),
+ REG_RULE_EXT(5250, 5330, 40, 0, 24, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(5490, 5730, 40, 0, 24, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(5735, 5835, 40, 0, 30, 0, 0),
+ },
+ .n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_CH = {
+ .alpha2 = "CH",
+ .dfs_region = NL80211_DFS_ETSI,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 20, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 20, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5710, 160, 0, 27, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0),
+ },
+ .n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_CI = {
+ .alpha2 = "CI",
+ .dfs_region = NL80211_DFS_FCC,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 17, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 24, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5730, 160, 0, 24, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+ },
+ .n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_CL = {
+ .alpha2 = "CL",
+ .dfs_region = NL80211_DFS_JP,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 20, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 20, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5735, 5835, 80, 0, 20, 0, 0),
+ },
+ .n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_CN = {
+ .alpha2 = "CN",
+ .dfs_region = NL80211_DFS_FCC,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 23, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 23, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+ REG_RULE_EXT(57240, 59400, 2160, 0, 28, 0, 0),
+ REG_RULE_EXT(59400, 63720, 2160, 0, 44, 0, 0),
+ REG_RULE_EXT(63720, 65880, 2160, 0, 28, 0, 0),
+ },
+ .n_reg_rules = 7
+};
+
+static const struct ieee80211_regdomain regdom_CO = {
+ .alpha2 = "CO",
+ .dfs_region = NL80211_DFS_FCC,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 17, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 24, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5730, 160, 0, 24, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+ },
+ .n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_CR = {
+ .alpha2 = "CR",
+ .dfs_region = NL80211_DFS_FCC,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 17, 0, 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 24, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(5490, 5730, 80, 0, 24, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+ },
+ .n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_CX = {
+ .alpha2 = "CX",
+ .dfs_region = NL80211_DFS_FCC,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 24, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 24, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5730, 160, 0, 24, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+ },
+ .n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_CY = {
+ .alpha2 = "CY",
+ .dfs_region = NL80211_DFS_ETSI,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 20, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 20, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5710, 160, 0, 27, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0),
+ },
+ .n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_CZ = {
+ .alpha2 = "CZ",
+ .dfs_region = NL80211_DFS_ETSI,
+ .reg_rules = {
+ REG_RULE_EXT(2400, 2483, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5150, 5250, 80, 0, 23, 0,
+ NL80211_RRF_NO_OUTDOOR |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5350, 80, 0, 20, 0,
+ NL80211_RRF_NO_OUTDOOR |
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5470, 5725, 160, 0, 27, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0),
+ },
+ .n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_DE = {
+ .alpha2 = "DE",
+ .dfs_region = NL80211_DFS_ETSI,
+ .reg_rules = {
+ REG_RULE_EXT(2400, 2483, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5150, 5250, 80, 0, 20, 0,
+ NL80211_RRF_NO_OUTDOOR |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5350, 80, 0, 20, 0,
+ NL80211_RRF_NO_OUTDOOR |
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5470, 5695, 160, 0, 27, 0,
+ NL80211_RRF_DFS | 0),
+ /*REG_RULE_EXT(5470, 5725, 160, 0, 27, 0,
+ NL80211_RRF_DFS | 0),*/
+ REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0),
+ },
+ .n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_DK = {
+ .alpha2 = "DK",
+ .dfs_region = NL80211_DFS_ETSI,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 20, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 20, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5710, 160, 0, 27, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0),
+ },
+ .n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_DM = {
+ .alpha2 = "DM",
+ .dfs_region = NL80211_DFS_FCC,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2472, 40, 0, 30, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 17, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 23, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+ },
+ .n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_DO = {
+ .alpha2 = "DO",
+ .dfs_region = NL80211_DFS_FCC,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2472, 40, 0, 30, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 17, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 23, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+ },
+ .n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_DZ = {
+ .alpha2 = "DZ",
+ .dfs_region = NL80211_DFS_JP,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 23, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 23, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5670, 160, 0, 23, 0,
+ NL80211_RRF_DFS | 0),
+ },
+ .n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_EC = {
+ .alpha2 = "EC",
+ .dfs_region = NL80211_DFS_FCC,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 17, 0, 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 24, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(5490, 5730, 80, 0, 24, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+ },
+ .n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_EE = {
+ .alpha2 = "EE",
+ .dfs_region = NL80211_DFS_ETSI,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 20, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 20, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5710, 160, 0, 27, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0),
+ },
+ .n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_EG = {
+ .alpha2 = "EG",
+ .dfs_region = NL80211_DFS_ETSI,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 20, 0,
+ NL80211_RRF_DFS | 0),
+ },
+ .n_reg_rules = 3
+};
+
+static const struct ieee80211_regdomain regdom_ES = {
+ .alpha2 = "ES",
+ .dfs_region = NL80211_DFS_ETSI,
+ .reg_rules = {
+ REG_RULE_EXT(2400, 2483, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5150, 5250, 80, 0, 23, 0,
+ NL80211_RRF_NO_OUTDOOR |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5350, 80, 0, 20, 0,
+ NL80211_RRF_NO_OUTDOOR |
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5470, 5725, 160, 0, 27, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0),
+ },
+ .n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_ET = {
+ .alpha2 = "ET",
+ .dfs_region = NL80211_DFS_ETSI,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 20, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 20, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5710, 160, 0, 27, 0,
+ NL80211_RRF_DFS | 0),
+ },
+ .n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_FI = {
+ .alpha2 = "FI",
+ .dfs_region = NL80211_DFS_ETSI,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 20, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 20, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5710, 160, 0, 27, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0),
+ },
+ .n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_FM = {
+ .alpha2 = "FM",
+ .dfs_region = NL80211_DFS_FCC,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2472, 40, 0, 30, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 24, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 24, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5730, 160, 0, 24, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+ },
+ .n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_FR = {
+ .alpha2 = "FR",
+ .dfs_region = NL80211_DFS_ETSI,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 20, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 20, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5695, 160, 0, 27, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0),
+ },
+ .n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_GB = {
+ .alpha2 = "GB",
+ .dfs_region = NL80211_DFS_ETSI,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 20, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 20, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5710, 160, 0, 27, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0),
+ },
+ .n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_GD = {
+ .alpha2 = "GD",
+ .dfs_region = NL80211_DFS_FCC,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2472, 40, 0, 30, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 17, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 24, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5730, 160, 0, 24, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+ },
+ .n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_GE = {
+ .alpha2 = "GE",
+ .dfs_region = NL80211_DFS_ETSI,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 18, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 18, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0),
+ },
+ .n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_GF = {
+ .alpha2 = "GF",
+ .dfs_region = NL80211_DFS_ETSI,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 20, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 20, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5710, 160, 0, 27, 0,
+ NL80211_RRF_DFS | 0),
+ },
+ .n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_GH = {
+ .alpha2 = "GH",
+ .dfs_region = NL80211_DFS_FCC,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 17, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 24, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5730, 160, 0, 24, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+ },
+ .n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_GL = {
+ .alpha2 = "GL",
+ .dfs_region = NL80211_DFS_ETSI,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 20, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(5490, 5710, 80, 0, 27, 0,
+ NL80211_RRF_DFS | 0),
+ },
+ .n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_GP = {
+ .alpha2 = "GP",
+ .dfs_region = NL80211_DFS_ETSI,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 20, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 20, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5710, 160, 0, 27, 0,
+ NL80211_RRF_DFS | 0),
+ },
+ .n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_GR = {
+ .alpha2 = "GR",
+ .dfs_region = NL80211_DFS_ETSI,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 20, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 20, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5710, 160, 0, 27, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0),
+ },
+ .n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_GT = {
+ .alpha2 = "GT",
+ .dfs_region = NL80211_DFS_FCC,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2472, 40, 0, 30, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 17, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 23, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+ },
+ .n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_GU = {
+ .alpha2 = "GU",
+ .dfs_region = NL80211_DFS_FCC,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2472, 40, 0, 30, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 17, 0, 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 24, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(5490, 5730, 80, 0, 24, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+ },
+ .n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_GY = {
+ .alpha2 = "GY",
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 30, 0, 0),
+ REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+ },
+ .n_reg_rules = 2
+};
+
+static const struct ieee80211_regdomain regdom_HK = {
+ .alpha2 = "HK",
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 17, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 24, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5710, 160, 0, 24, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+ },
+ .n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_HN = {
+ .alpha2 = "HN",
+ .dfs_region = NL80211_DFS_FCC,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 17, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 24, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5730, 160, 0, 24, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+ },
+ .n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_HR = {
+ .alpha2 = "HR",
+ .dfs_region = NL80211_DFS_ETSI,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 20, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 20, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5710, 160, 0, 27, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0),
+ },
+ .n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_HT = {
+ .alpha2 = "HT",
+ .dfs_region = NL80211_DFS_FCC,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2472, 40, 0, 30, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 24, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 24, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5730, 160, 0, 24, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+ },
+ .n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_HU = {
+ .alpha2 = "HU",
+ .dfs_region = NL80211_DFS_ETSI,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 20, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 20, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5710, 160, 0, 27, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0),
+ },
+ .n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_ID = {
+ .alpha2 = "ID",
+ .dfs_region = NL80211_DFS_JP,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5735, 5815, 80, 0, 23, 0, 0),
+ },
+ .n_reg_rules = 2
+};
+
+static const struct ieee80211_regdomain regdom_IE = {
+ .alpha2 = "IE",
+ .dfs_region = NL80211_DFS_ETSI,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 20, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 20, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5710, 160, 0, 27, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0),
+ },
+ .n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_IL = {
+ .alpha2 = "IL",
+ .dfs_region = NL80211_DFS_ETSI,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5150, 5250, 80, 0, 23, 0,
+ NL80211_RRF_NO_OUTDOOR |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5350, 80, 0, 23, 0,
+ NL80211_RRF_NO_OUTDOOR |
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ },
+ .n_reg_rules = 3
+};
+
+static const struct ieee80211_regdomain regdom_IN = {
+ .alpha2 = "IN",
+ .dfs_region = NL80211_DFS_JP,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 20, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 20, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5735, 5835, 80, 0, 20, 0, 0),
+ },
+ .n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_IR = {
+ .alpha2 = "IR",
+ .dfs_region = NL80211_DFS_JP,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+ },
+ .n_reg_rules = 2
+};
+
+static const struct ieee80211_regdomain regdom_IS = {
+ .alpha2 = "IS",
+ .dfs_region = NL80211_DFS_ETSI,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 20, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 20, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5710, 160, 0, 27, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0),
+ },
+ .n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_IT = {
+ .alpha2 = "IT",
+ .dfs_region = NL80211_DFS_ETSI,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 20, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 20, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5710, 160, 0, 27, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0),
+ },
+ .n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_JM = {
+ .alpha2 = "JM",
+ .dfs_region = NL80211_DFS_FCC,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 17, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 24, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5730, 160, 0, 24, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+ },
+ .n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_JO = {
+ .alpha2 = "JO",
+ .dfs_region = NL80211_DFS_JP,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 23, 0, 0),
+ REG_RULE_EXT(5735, 5835, 80, 0, 23, 0, 0),
+ },
+ .n_reg_rules = 3
+};
+
+static const struct ieee80211_regdomain regdom_JP = {
+ .alpha2 = "JP",
+ .dfs_region = NL80211_DFS_JP,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(2474, 2494, 20, 0, 20, 0,
+ NL80211_RRF_NO_OFDM | 0),
+ REG_RULE_EXT(4910, 4990, 40, 0, 23, 0, 0),
+ REG_RULE_EXT(5030, 5090, 40, 0, 23, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 20, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 20, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5710, 160, 0, 23, 0,
+ NL80211_RRF_DFS | 0),
+ },
+ .n_reg_rules = 7
+};
+
+static const struct ieee80211_regdomain regdom_KE = {
+ .alpha2 = "KE",
+ .dfs_region = NL80211_DFS_JP,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 23, 0, 0),
+ REG_RULE_EXT(5490, 5570, 80, 0, 30, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(5735, 5775, 40, 0, 23, 0, 0),
+ },
+ .n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_KH = {
+ .alpha2 = "KH",
+ .dfs_region = NL80211_DFS_ETSI,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 20, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 20, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5710, 160, 0, 27, 0,
+ NL80211_RRF_DFS | 0),
+ },
+ .n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_KN = {
+ .alpha2 = "KN",
+ .dfs_region = NL80211_DFS_ETSI,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 20, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 20, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5710, 160, 0, 30, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(5735, 5815, 80, 0, 30, 0, 0),
+ },
+ .n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_KP = {
+ .alpha2 = "KP",
+ .dfs_region = NL80211_DFS_JP,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 20, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 20, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5630, 80, 0, 30, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(5735, 5815, 80, 0, 30, 0, 0),
+ },
+ .n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_KR = {
+ .alpha2 = "KR",
+ .dfs_region = NL80211_DFS_JP,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 20, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 20, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5710, 160, 0, 30, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+ },
+ .n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_KW = {
+ .alpha2 = "KW",
+ .dfs_region = NL80211_DFS_ETSI,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 20, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 20, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ },
+ .n_reg_rules = 3
+};
+
+static const struct ieee80211_regdomain regdom_KY = {
+ .alpha2 = "KY",
+ .dfs_region = NL80211_DFS_FCC,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 24, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 24, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5730, 160, 0, 24, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+ },
+ .n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_KZ = {
+ .alpha2 = "KZ",
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ },
+ .n_reg_rules = 1
+};
+
+static const struct ieee80211_regdomain regdom_LB = {
+ .alpha2 = "LB",
+ .dfs_region = NL80211_DFS_FCC,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 17, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 24, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5730, 160, 0, 24, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+ },
+ .n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_LC = {
+ .alpha2 = "LC",
+ .dfs_region = NL80211_DFS_ETSI,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 20, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 20, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5710, 160, 0, 30, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(5735, 5815, 80, 0, 30, 0, 0),
+ },
+ .n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_LI = {
+ .alpha2 = "LI",
+ .dfs_region = NL80211_DFS_ETSI,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 20, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 20, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5710, 160, 0, 27, 0,
+ NL80211_RRF_DFS | 0),
+ },
+ .n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_LK = {
+ .alpha2 = "LK",
+ .dfs_region = NL80211_DFS_FCC,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 17, 0, 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 24, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(5490, 5730, 80, 0, 24, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+ },
+ .n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_LS = {
+ .alpha2 = "LS",
+ .dfs_region = NL80211_DFS_ETSI,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 20, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 20, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5710, 160, 0, 27, 0,
+ NL80211_RRF_DFS | 0),
+ },
+ .n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_LT = {
+ .alpha2 = "LT",
+ .dfs_region = NL80211_DFS_ETSI,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 20, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 20, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5710, 160, 0, 27, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0),
+ },
+ .n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_LU = {
+ .alpha2 = "LU",
+ .dfs_region = NL80211_DFS_ETSI,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 20, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 20, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5710, 160, 0, 27, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0),
+ },
+ .n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_LV = {
+ .alpha2 = "LV",
+ .dfs_region = NL80211_DFS_ETSI,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 20, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 20, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5710, 160, 0, 27, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0),
+ },
+ .n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_MA = {
+ .alpha2 = "MA",
+ .dfs_region = NL80211_DFS_ETSI,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 20, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 20, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ },
+ .n_reg_rules = 3
+};
+
+static const struct ieee80211_regdomain regdom_MC = {
+ .alpha2 = "MC",
+ .dfs_region = NL80211_DFS_ETSI,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 20, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 20, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5710, 160, 0, 27, 0,
+ NL80211_RRF_DFS | 0),
+ },
+ .n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_MD = {
+ .alpha2 = "MD",
+ .dfs_region = NL80211_DFS_ETSI,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 20, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 20, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5710, 160, 0, 27, 0,
+ NL80211_RRF_DFS | 0),
+ },
+ .n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_ME = {
+ .alpha2 = "ME",
+ .dfs_region = NL80211_DFS_ETSI,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 20, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 20, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5710, 160, 0, 27, 0,
+ NL80211_RRF_DFS | 0),
+ },
+ .n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_MF = {
+ .alpha2 = "MF",
+ .dfs_region = NL80211_DFS_ETSI,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 20, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 20, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5710, 160, 0, 27, 0,
+ NL80211_RRF_DFS | 0),
+ },
+ .n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_MH = {
+ .alpha2 = "MH",
+ .dfs_region = NL80211_DFS_FCC,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2472, 40, 0, 30, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 24, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 24, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5730, 160, 0, 24, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+ },
+ .n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_MK = {
+ .alpha2 = "MK",
+ .dfs_region = NL80211_DFS_ETSI,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 20, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 20, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5710, 160, 0, 27, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0),
+ },
+ .n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_MN = {
+ .alpha2 = "MN",
+ .dfs_region = NL80211_DFS_FCC,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 24, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 24, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5730, 160, 0, 24, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+ },
+ .n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_MO = {
+ .alpha2 = "MO",
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 40, 0, 23, 0, 0),
+ REG_RULE_EXT(5250, 5330, 40, 0, 23, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(5735, 5835, 40, 0, 30, 0, 0),
+ },
+ .n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_MP = {
+ .alpha2 = "MP",
+ .dfs_region = NL80211_DFS_FCC,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2472, 40, 0, 30, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 24, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 24, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5730, 160, 0, 24, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+ },
+ .n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_MQ = {
+ .alpha2 = "MQ",
+ .dfs_region = NL80211_DFS_ETSI,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 20, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 20, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5710, 160, 0, 27, 0,
+ NL80211_RRF_DFS | 0),
+ },
+ .n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_MR = {
+ .alpha2 = "MR",
+ .dfs_region = NL80211_DFS_ETSI,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 20, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 20, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5710, 160, 0, 27, 0,
+ NL80211_RRF_DFS | 0),
+ },
+ .n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_MT = {
+ .alpha2 = "MT",
+ .dfs_region = NL80211_DFS_ETSI,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 20, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 20, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5710, 160, 0, 27, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0),
+ },
+ .n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_MU = {
+ .alpha2 = "MU",
+ .dfs_region = NL80211_DFS_FCC,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 24, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 24, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5730, 160, 0, 24, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+ },
+ .n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_MW = {
+ .alpha2 = "MW",
+ .dfs_region = NL80211_DFS_ETSI,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 20, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 20, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5710, 160, 0, 27, 0,
+ NL80211_RRF_DFS | 0),
+ },
+ .n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_MX = {
+ .alpha2 = "MX",
+ .dfs_region = NL80211_DFS_FCC,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 17, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 24, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5730, 160, 0, 24, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+ },
+ .n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_MY = {
+ .alpha2 = "MY",
+ .dfs_region = NL80211_DFS_FCC,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 17, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 23, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+ },
+ .n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_NI = {
+ .alpha2 = "NI",
+ .dfs_region = NL80211_DFS_FCC,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2472, 40, 0, 30, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 24, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 24, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5730, 160, 0, 24, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+ },
+ .n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_NL = {
+ .alpha2 = "NL",
+ .dfs_region = NL80211_DFS_ETSI,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 20, 0,
+ NL80211_RRF_NO_OUTDOOR |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 20, 0,
+ NL80211_RRF_NO_OUTDOOR |
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5710, 160, 0, 27, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0),
+ },
+ .n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_NO = {
+ .alpha2 = "NO",
+ .dfs_region = NL80211_DFS_ETSI,
+ .reg_rules = {
+ REG_RULE_EXT(2400, 2483, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5150, 5250, 80, 0, 23, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5350, 80, 0, 20, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5470, 5795, 160, 0, 27, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(5815, 5850, 35, 0, 33, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(17100, 17300, 200, 0, 20, 0, 0),
+ REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0),
+ },
+ .n_reg_rules = 7
+};
+
+static const struct ieee80211_regdomain regdom_NP = {
+ .alpha2 = "NP",
+ .dfs_region = NL80211_DFS_JP,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 20, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 20, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5735, 5835, 80, 0, 20, 0, 0),
+ },
+ .n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_NZ = {
+ .alpha2 = "NZ",
+ .dfs_region = NL80211_DFS_FCC,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 30, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 17, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 24, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5730, 160, 0, 24, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+ },
+ .n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_OM = {
+ .alpha2 = "OM",
+ .dfs_region = NL80211_DFS_ETSI,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 20, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 20, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5710, 160, 0, 27, 0,
+ NL80211_RRF_DFS | 0),
+ },
+ .n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_PA = {
+ .alpha2 = "PA",
+ .dfs_region = NL80211_DFS_FCC,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2472, 40, 0, 30, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 17, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 23, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+ },
+ .n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_PE = {
+ .alpha2 = "PE",
+ .dfs_region = NL80211_DFS_FCC,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 17, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 24, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5730, 160, 0, 24, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+ },
+ .n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_PF = {
+ .alpha2 = "PF",
+ .dfs_region = NL80211_DFS_ETSI,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 20, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 20, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5710, 160, 0, 27, 0,
+ NL80211_RRF_DFS | 0),
+ },
+ .n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_PG = {
+ .alpha2 = "PG",
+ .dfs_region = NL80211_DFS_FCC,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 17, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 24, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5730, 160, 0, 24, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+ },
+ .n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_PH = {
+ .alpha2 = "PH",
+ .dfs_region = NL80211_DFS_FCC,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 17, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 24, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5730, 160, 0, 24, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+ },
+ .n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_PK = {
+ .alpha2 = "PK",
+ .dfs_region = NL80211_DFS_JP,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+ },
+ .n_reg_rules = 2
+};
+
+static const struct ieee80211_regdomain regdom_PL = {
+ .alpha2 = "PL",
+ .dfs_region = NL80211_DFS_ETSI,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 20, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 20, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5710, 160, 0, 27, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0),
+ },
+ .n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_PM = {
+ .alpha2 = "PM",
+ .dfs_region = NL80211_DFS_ETSI,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 20, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 20, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5710, 160, 0, 27, 0,
+ NL80211_RRF_DFS | 0),
+ },
+ .n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_PR = {
+ .alpha2 = "PR",
+ .dfs_region = NL80211_DFS_FCC,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2472, 40, 0, 30, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 17, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 24, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5730, 160, 0, 24, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+ },
+ .n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_PT = {
+ .alpha2 = "PT",
+ .dfs_region = NL80211_DFS_ETSI,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 20, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 20, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5710, 160, 0, 27, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0),
+ },
+ .n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_PW = {
+ .alpha2 = "PW",
+ .dfs_region = NL80211_DFS_FCC,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2472, 40, 0, 30, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 24, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 24, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5730, 160, 0, 24, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+ },
+ .n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_PY = {
+ .alpha2 = "PY",
+ .dfs_region = NL80211_DFS_FCC,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 24, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 24, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5730, 160, 0, 24, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+ },
+ .n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_QA = {
+ .alpha2 = "QA",
+ .dfs_region = NL80211_DFS_JP,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+ },
+ .n_reg_rules = 2
+};
+
+static const struct ieee80211_regdomain regdom_RE = {
+ .alpha2 = "RE",
+ .dfs_region = NL80211_DFS_ETSI,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 20, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 20, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5710, 160, 0, 27, 0,
+ NL80211_RRF_DFS | 0),
+ },
+ .n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_RO = {
+ .alpha2 = "RO",
+ .dfs_region = NL80211_DFS_ETSI,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 20, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 20, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5710, 160, 0, 27, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0),
+ },
+ .n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_RS = {
+ .alpha2 = "RS",
+ .dfs_region = NL80211_DFS_ETSI,
+ .reg_rules = {
+ REG_RULE_EXT(2400, 2483, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5150, 5350, 40, 0, 23, 0,
+ NL80211_RRF_NO_OUTDOOR | 0),
+ REG_RULE_EXT(5470, 5725, 20, 0, 30, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0),
+ },
+ .n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_RU = {
+ .alpha2 = "RU",
+ .dfs_region = NL80211_DFS_ETSI,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 20, 0, 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 20, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(5650, 5730, 80, 0, 30, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+ },
+ .n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_RW = {
+ .alpha2 = "RW",
+ .dfs_region = NL80211_DFS_FCC,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 17, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 24, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5730, 160, 0, 24, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+ },
+ .n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_SA = {
+ .alpha2 = "SA",
+ .dfs_region = NL80211_DFS_ETSI,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 20, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 20, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5710, 160, 0, 27, 0,
+ NL80211_RRF_DFS | 0),
+ },
+ .n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_SE = {
+ .alpha2 = "SE",
+ .dfs_region = NL80211_DFS_ETSI,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 20, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 20, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5710, 160, 0, 27, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0),
+ },
+ .n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_SG = {
+ .alpha2 = "SG",
+ .dfs_region = NL80211_DFS_FCC,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 17, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 24, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5730, 160, 0, 24, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+ },
+ .n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_SI = {
+ .alpha2 = "SI",
+ .dfs_region = NL80211_DFS_ETSI,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 20, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 20, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5710, 160, 0, 27, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0),
+ },
+ .n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_SK = {
+ .alpha2 = "SK",
+ .dfs_region = NL80211_DFS_ETSI,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 20, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 20, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5710, 160, 0, 27, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0),
+ },
+ .n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_SN = {
+ .alpha2 = "SN",
+ .dfs_region = NL80211_DFS_FCC,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 17, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 24, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5730, 160, 0, 24, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+ },
+ .n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_SR = {
+ .alpha2 = "SR",
+ .dfs_region = NL80211_DFS_ETSI,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 20, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 20, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5710, 160, 0, 27, 0,
+ NL80211_RRF_DFS | 0),
+ },
+ .n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_SV = {
+ .alpha2 = "SV",
+ .dfs_region = NL80211_DFS_FCC,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 17, 0, 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 23, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+ },
+ .n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_SY = {
+ .alpha2 = "SY",
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ },
+ .n_reg_rules = 1
+};
+
+static const struct ieee80211_regdomain regdom_TC = {
+ .alpha2 = "TC",
+ .dfs_region = NL80211_DFS_FCC,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 24, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 24, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5730, 160, 0, 24, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+ },
+ .n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_TD = {
+ .alpha2 = "TD",
+ .dfs_region = NL80211_DFS_ETSI,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 20, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 20, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5710, 160, 0, 27, 0,
+ NL80211_RRF_DFS | 0),
+ },
+ .n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_TG = {
+ .alpha2 = "TG",
+ .dfs_region = NL80211_DFS_ETSI,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5250, 5330, 40, 0, 20, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(5490, 5710, 40, 0, 27, 0,
+ NL80211_RRF_DFS | 0),
+ },
+ .n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_TH = {
+ .alpha2 = "TH",
+ .dfs_region = NL80211_DFS_FCC,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 17, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 24, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5730, 160, 0, 24, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+ },
+ .n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_TN = {
+ .alpha2 = "TN",
+ .dfs_region = NL80211_DFS_ETSI,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 20, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 20, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ },
+ .n_reg_rules = 3
+};
+
+static const struct ieee80211_regdomain regdom_TR = {
+ .alpha2 = "TR",
+ .dfs_region = NL80211_DFS_ETSI,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 20, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 20, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5710, 160, 0, 27, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0),
+ },
+ .n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_TT = {
+ .alpha2 = "TT",
+ .dfs_region = NL80211_DFS_FCC,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 17, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 24, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5730, 160, 0, 24, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+ },
+ .n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_TW = {
+ .alpha2 = "TW",
+ .dfs_region = NL80211_DFS_JP,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2472, 40, 0, 30, 0, 0),
+ REG_RULE_EXT(5270, 5330, 40, 0, 17, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(5490, 5590, 80, 0, 30, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(5650, 5710, 40, 0, 30, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+ },
+ .n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_UA = {
+ .alpha2 = "UA",
+ .dfs_region = NL80211_DFS_ETSI,
+ .reg_rules = {
+ REG_RULE_EXT(2400, 2483, 40, 0, 20, 0,
+ NL80211_RRF_NO_OUTDOOR | 0),
+ REG_RULE_EXT(5150, 5350, 40, 0, 20, 0,
+ NL80211_RRF_NO_OUTDOOR | 0),
+ REG_RULE_EXT(5490, 5670, 80, 0, 20, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(5735, 5835, 80, 0, 20, 0, 0),
+ REG_RULE_EXT(57000, 66000, 2160, 0, 40, 0, 0),
+ },
+ .n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_UG = {
+ .alpha2 = "UG",
+ .dfs_region = NL80211_DFS_FCC,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 24, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 24, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5730, 160, 0, 24, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+ },
+ .n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_US = {
+ .alpha2 = "US",
+ .dfs_region = NL80211_DFS_FCC,
+ .reg_rules = {
+ // 1...13
+ REG_RULE_EXT(2402, 2472, 40, 0, 30, 0, 0),
+ // 36 40 44 48
+ REG_RULE_EXT(5170, 5250, 80, 0, 17, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ // 52 56 60 64
+ REG_RULE_EXT(5250, 5330, 80, 0, 23, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ // 100 104 108 112 116 120 124
+ REG_RULE_EXT(5490, 5650, 80, 0, 24, 0,
+ NL80211_RRF_DFS | 0),
+ // 128 132 136 140
+ REG_RULE_EXT(5650, 5710, 40, 0, 24, 0,
+ NL80211_RRF_DFS | 0),
+ // 149 153 157 161 165
+ REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+ REG_RULE_EXT(57240, 63720, 2160, 0, 40, 0, 0),
+ },
+ .n_reg_rules = 7
+};
+
+static const struct ieee80211_regdomain regdom_UY = {
+ .alpha2 = "UY",
+ .dfs_region = NL80211_DFS_FCC,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 17, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 24, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5730, 160, 0, 24, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+ },
+ .n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_UZ = {
+ .alpha2 = "UZ",
+ .dfs_region = NL80211_DFS_ETSI,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 20, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 20, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ },
+ .n_reg_rules = 3
+};
+
+static const struct ieee80211_regdomain regdom_VC = {
+ .alpha2 = "VC",
+ .dfs_region = NL80211_DFS_ETSI,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 20, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 20, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5710, 160, 0, 27, 0,
+ NL80211_RRF_DFS | 0),
+ },
+ .n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_VE = {
+ .alpha2 = "VE",
+ .dfs_region = NL80211_DFS_FCC,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 30, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 23, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 23, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+ },
+ .n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_VI = {
+ .alpha2 = "VI",
+ .dfs_region = NL80211_DFS_FCC,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2472, 40, 0, 30, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 24, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 24, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5730, 160, 0, 24, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+ },
+ .n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_VN = {
+ .alpha2 = "VN",
+ .dfs_region = NL80211_DFS_FCC,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 17, 0, 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 24, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(5490, 5730, 80, 0, 24, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+ },
+ .n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_VU = {
+ .alpha2 = "VU",
+ .dfs_region = NL80211_DFS_FCC,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 17, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 24, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5730, 160, 0, 24, 0,
+ NL80211_RRF_DFS | 0),
+ REG_RULE_EXT(5735, 5835, 80, 0, 30, 0, 0),
+ },
+ .n_reg_rules = 5
+};
+
+static const struct ieee80211_regdomain regdom_WF = {
+ .alpha2 = "WF",
+ .dfs_region = NL80211_DFS_ETSI,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 20, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 20, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5710, 160, 0, 27, 0,
+ NL80211_RRF_DFS | 0),
+ },
+ .n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_YE = {
+ .alpha2 = "YE",
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ },
+ .n_reg_rules = 1
+};
+
+static const struct ieee80211_regdomain regdom_YT = {
+ .alpha2 = "YT",
+ .dfs_region = NL80211_DFS_ETSI,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 20, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 20, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5710, 160, 0, 27, 0,
+ NL80211_RRF_DFS | 0),
+ },
+ .n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_ZA = {
+ .alpha2 = "ZA",
+ .dfs_region = NL80211_DFS_ETSI,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 20, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 20, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5695, 160, 0, 27, 0,
+ NL80211_RRF_DFS | 0),
+ /*REG_RULE_EXT(5490, 5710, 160, 0, 27, 0,
+ NL80211_RRF_DFS | 0),*/
+ },
+ .n_reg_rules = 4
+};
+
+static const struct ieee80211_regdomain regdom_ZW = {
+ .alpha2 = "ZW",
+ .dfs_region = NL80211_DFS_ETSI,
+ .reg_rules = {
+ REG_RULE_EXT(2402, 2482, 40, 0, 20, 0, 0),
+ REG_RULE_EXT(5170, 5250, 80, 0, 20, 0,
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5250, 5330, 80, 0, 20, 0,
+ NL80211_RRF_DFS |
+ NL80211_RRF_AUTO_BW | 0),
+ REG_RULE_EXT(5490, 5710, 160, 0, 27, 0,
+ NL80211_RRF_DFS | 0),
+ },
+ .n_reg_rules = 4
+};
+
+const struct ieee80211_regdomain *reg_regdb[] = {
+ &regdom_00,
+ &regdom_AD,
+ &regdom_AE,
+ &regdom_AF,
+ &regdom_AI,
+ &regdom_AL,
+ &regdom_AM,
+ &regdom_AN,
+ &regdom_AR,
+ &regdom_AS,
+ &regdom_AT,
+ &regdom_AU,
+ &regdom_AW,
+ &regdom_AZ,
+ &regdom_BA,
+ &regdom_BB,
+ &regdom_BD,
+ &regdom_BE,
+ &regdom_BF,
+ &regdom_BG,
+ &regdom_BH,
+ &regdom_BL,
+ &regdom_BM,
+ &regdom_BN,
+ &regdom_BO,
+ &regdom_BR,
+ &regdom_BS,
+ &regdom_BT,
+ &regdom_BY,
+ &regdom_BZ,
+ &regdom_CA,
+ &regdom_CF,
+ &regdom_CH,
+ &regdom_CI,
+ &regdom_CL,
+ &regdom_CN,
+ &regdom_CO,
+ &regdom_CR,
+ &regdom_CX,
+ &regdom_CY,
+ &regdom_CZ,
+ &regdom_DE,
+ &regdom_DK,
+ &regdom_DM,
+ &regdom_DO,
+ &regdom_DZ,
+ &regdom_EC,
+ &regdom_EE,
+ &regdom_EG,
+ &regdom_ES,
+ &regdom_ET,
+ &regdom_FI,
+ &regdom_FM,
+ &regdom_FR,
+ &regdom_GB,
+ &regdom_GD,
+ &regdom_GE,
+ &regdom_GF,
+ &regdom_GH,
+ &regdom_GL,
+ &regdom_GP,
+ &regdom_GR,
+ &regdom_GT,
+ &regdom_GU,
+ &regdom_GY,
+ &regdom_HK,
+ &regdom_HN,
+ &regdom_HR,
+ &regdom_HT,
+ &regdom_HU,
+ &regdom_ID,
+ &regdom_IE,
+ &regdom_IL,
+ &regdom_IN,
+ &regdom_IR,
+ &regdom_IS,
+ &regdom_IT,
+ &regdom_JM,
+ &regdom_JO,
+ &regdom_JP,
+ &regdom_KE,
+ &regdom_KH,
+ &regdom_KN,
+ &regdom_KP,
+ &regdom_KR,
+ &regdom_KW,
+ &regdom_KY,
+ &regdom_KZ,
+ &regdom_LB,
+ &regdom_LC,
+ &regdom_LI,
+ &regdom_LK,
+ &regdom_LS,
+ &regdom_LT,
+ &regdom_LU,
+ &regdom_LV,
+ &regdom_MA,
+ &regdom_MC,
+ &regdom_MD,
+ &regdom_ME,
+ &regdom_MF,
+ &regdom_MH,
+ &regdom_MK,
+ &regdom_MN,
+ &regdom_MO,
+ &regdom_MP,
+ &regdom_MQ,
+ &regdom_MR,
+ &regdom_MT,
+ &regdom_MU,
+ &regdom_MW,
+ &regdom_MX,
+ &regdom_MY,
+ &regdom_NI,
+ &regdom_NL,
+ &regdom_NO,
+ &regdom_NP,
+ &regdom_NZ,
+ &regdom_OM,
+ &regdom_PA,
+ &regdom_PE,
+ &regdom_PF,
+ &regdom_PG,
+ &regdom_PH,
+ &regdom_PK,
+ &regdom_PL,
+ &regdom_PM,
+ &regdom_PR,
+ &regdom_PT,
+ &regdom_PW,
+ &regdom_PY,
+ &regdom_QA,
+ &regdom_RE,
+ &regdom_RO,
+ &regdom_RS,
+ &regdom_RU,
+ &regdom_RW,
+ &regdom_SA,
+ &regdom_SE,
+ &regdom_SG,
+ &regdom_SI,
+ &regdom_SK,
+ &regdom_SN,
+ &regdom_SR,
+ &regdom_SV,
+ &regdom_SY,
+ &regdom_TC,
+ &regdom_TD,
+ &regdom_TG,
+ &regdom_TH,
+ &regdom_TN,
+ &regdom_TR,
+ &regdom_TT,
+ &regdom_TW,
+ &regdom_UA,
+ &regdom_UG,
+ &regdom_US,
+ &regdom_UY,
+ &regdom_UZ,
+ &regdom_VC,
+ &regdom_VE,
+ &regdom_VI,
+ &regdom_VN,
+ &regdom_VU,
+ &regdom_WF,
+ &regdom_YE,
+ &regdom_YT,
+ &regdom_ZA,
+ &regdom_ZW,
+};
+
+int reg_regdb_size = ARRAY_SIZE(reg_regdb);
+
+
diff --git a/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_bfmer.c b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_bfmer.c
new file mode 100644
index 000000000000..4be5ec0c356f
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_bfmer.c
@@ -0,0 +1,105 @@
+/**
+ ******************************************************************************
+ *
+ * @file rwnx_bfmer.c
+ *
+ * @brief VHT Beamformer function definitions
+ *
+ * Copyright (C) RivieraWaves 2016-2019
+ *
+ ******************************************************************************
+ */
+
+/**
+ * INCLUDE FILES
+ ******************************************************************************
+ */
+
+#include <linux/slab.h>
+#include "rwnx_bfmer.h"
+
+/**
+ * FUNCTION DEFINITIONS
+ ******************************************************************************
+ */
+
+int rwnx_bfmer_report_add(struct rwnx_hw *rwnx_hw, struct rwnx_sta *rwnx_sta,
+ unsigned int length)
+{
+ gfp_t flags;
+ struct rwnx_bfmer_report *bfm_report ;
+
+ if (in_softirq())
+ flags = GFP_ATOMIC;
+ else
+ flags = GFP_KERNEL;
+
+ /* Allocate a structure that will contain the beamforming report */
+ bfm_report = kmalloc(sizeof(*bfm_report) + length, flags);
+
+
+ /* Check report allocation */
+ if (!bfm_report) {
+ /* Do not use beamforming */
+ return -1;
+ }
+
+ /* Store report length */
+ bfm_report->length = length;
+
+ /*
+ * Need to provide a Virtual Address to the MAC so that it can
+ * upload the received Beamforming Report in driver memory
+ */
+ bfm_report->dma_addr = dma_map_single(rwnx_hw->dev, &bfm_report->report[0],
+ length, DMA_FROM_DEVICE);
+
+ /* Check DMA mapping result */
+ if (dma_mapping_error(rwnx_hw->dev, bfm_report->dma_addr)) {
+ /* Free allocated report */
+ kfree(bfm_report);
+ /* And leave */
+ return -1;
+ }
+
+ /* Store report structure */
+ rwnx_sta->bfm_report = bfm_report;
+
+ return 0;
+}
+
+void rwnx_bfmer_report_del(struct rwnx_hw *rwnx_hw, struct rwnx_sta *rwnx_sta)
+{
+ /* Verify if a report has been allocated */
+ if (rwnx_sta->bfm_report) {
+ struct rwnx_bfmer_report *bfm_report = rwnx_sta->bfm_report;
+
+ /* Unmap DMA region */
+ dma_unmap_single(rwnx_hw->dev, bfm_report->dma_addr,
+ bfm_report->length, DMA_BIDIRECTIONAL);
+
+ /* Free allocated report structure and clean the pointer */
+ kfree(bfm_report);
+ rwnx_sta->bfm_report = NULL;
+ }
+}
+
+#ifdef CONFIG_RWNX_FULLMAC
+u8 rwnx_bfmer_get_rx_nss(const struct ieee80211_vht_cap *vht_capa)
+{
+ int i;
+ u8 rx_nss = 0;
+ u16 rx_mcs_map = le16_to_cpu(vht_capa->supp_mcs.rx_mcs_map);
+
+ for (i = 7; i >= 0; i--) {
+ u8 mcs = (rx_mcs_map >> (2 * i)) & 3;
+
+ if (mcs != IEEE80211_VHT_MCS_NOT_SUPPORTED) {
+ rx_nss = i + 1;
+ break;
+ }
+ }
+
+ return rx_nss;
+}
+#endif /* CONFIG_RWNX_FULLMAC */
diff --git a/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_bfmer.h b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_bfmer.h
new file mode 100644
index 000000000000..9a313bfb1eb3
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_bfmer.h
@@ -0,0 +1,100 @@
+/**
+ ******************************************************************************
+ *
+ * @file rwnx_bfmer.h
+ *
+ * @brief VHT Beamformer function declarations
+ *
+ * Copyright (C) RivieraWaves 2016-2019
+ *
+ ******************************************************************************
+ */
+
+#ifndef _RWNX_BFMER_H_
+#define _RWNX_BFMER_H_
+
+/**
+ * INCLUDE FILES
+ ******************************************************************************
+ */
+
+#include "rwnx_defs.h"
+
+/**
+ * DEFINES
+ ******************************************************************************
+ */
+
+/// Maximal supported report length (in bytes)
+#define RWNX_BFMER_REPORT_MAX_LEN 2048
+
+/// Size of the allocated report space (twice the maximum report length)
+#define RWNX_BFMER_REPORT_SPACE_SIZE (RWNX_BFMER_REPORT_MAX_LEN * 2)
+
+/**
+ * TYPE DEFINITIONS
+ ******************************************************************************
+ */
+
+/*
+ * Structure used to store a beamforming report.
+ */
+struct rwnx_bfmer_report {
+ dma_addr_t dma_addr; /* Virtual address provided to MAC for
+ DMA transfer of the Beamforming Report */
+ unsigned int length; /* Report Length */
+ u8 report[1]; /* Report to be used for VHT TX Beamforming */
+};
+
+/**
+ * FUNCTION DECLARATIONS
+ ******************************************************************************
+ */
+
+/**
+ ******************************************************************************
+ * @brief Allocate memory aiming to contains the Beamforming Report received
+ * from a Beamformee capable capable.
+ * The providing length shall be large enough to contain the VHT Compressed
+ * Beaforming Report and the MU Exclusive part.
+ * It also perform a DMA Mapping providing an address to be provided to the HW
+ * responsible for the DMA transfer of the report.
+ * If successful a struct rwnx_bfmer_report object is allocated, it's address
+ * is stored in rwnx_sta->bfm_report.
+ *
+ * @param[in] rwnx_hw PHY Information
+ * @param[in] rwnx_sta Peer STA Information
+ * @param[in] length Memory size to be allocated
+ *
+ * @return 0 if operation is successful, else -1.
+ ******************************************************************************
+ */
+int rwnx_bfmer_report_add(struct rwnx_hw *rwnx_hw, struct rwnx_sta *rwnx_sta,
+ unsigned int length);
+
+/**
+ ******************************************************************************
+ * @brief Free a previously allocated memory intended to be used for
+ * Beamforming Reports.
+ *
+ * @param[in] rwnx_hw PHY Information
+ * @param[in] rwnx_sta Peer STA Information
+ *
+ ******************************************************************************
+ */
+void rwnx_bfmer_report_del(struct rwnx_hw *rwnx_hw, struct rwnx_sta *rwnx_sta);
+
+#ifdef CONFIG_RWNX_FULLMAC
+/**
+ ******************************************************************************
+ * @brief Parse a Rx VHT-MCS map in order to deduce the maximum number of
+ * Spatial Streams supported by a beamformee.
+ *
+ * @param[in] vht_capa Received VHT Capability field.
+ *
+ ******************************************************************************
+ */
+u8 rwnx_bfmer_get_rx_nss(const struct ieee80211_vht_cap *vht_capa);
+#endif /* CONFIG_RWNX_FULLMAC */
+
+#endif /* _RWNX_BFMER_H_ */
diff --git a/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_cfgfile.c b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_cfgfile.c
new file mode 100644
index 000000000000..2af5c3071c35
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_cfgfile.c
@@ -0,0 +1,237 @@
+/**
+ ****************************************************************************************
+ *
+ * @file rwnx_configparse.c
+ *
+ * Copyright (C) RivieraWaves 2012-2019
+ *
+ ****************************************************************************************
+ */
+#include <linux/firmware.h>
+#include <linux/if_ether.h>
+
+#include "rwnx_defs.h"
+#include "rwnx_cfgfile.h"
+
+/**
+ *
+ */
+static const char *rwnx_find_tag(const u8 *file_data, unsigned int file_size,
+ const char *tag_name, unsigned int tag_len)
+{
+ unsigned int curr, line_start = 0, line_size;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ /* Walk through all the lines of the configuration file */
+ while (line_start < file_size) {
+ /* Search the end of the current line (or the end of the file) */
+ for (curr = line_start; curr < file_size; curr++)
+ if (file_data[curr] == '\n')
+ break;
+
+ /* Compute the line size */
+ line_size = curr - line_start;
+
+ /* Check if this line contains the expected tag */
+ if ((line_size == (strlen(tag_name) + tag_len)) &&
+ (!strncmp(&file_data[line_start], tag_name, strlen(tag_name))))
+ return (&file_data[line_start + strlen(tag_name)]);
+
+ /* Move to next line */
+ line_start = curr + 1;
+ }
+
+ /* Tag not found */
+ return NULL;
+}
+
+/**
+ * Parse the Config file used at init time
+ */
+int rwnx_parse_configfile(struct rwnx_hw *rwnx_hw, const char *filename,
+ struct rwnx_conf_file *config)
+{
+ const struct firmware *config_fw;
+ u8 dflt_mac[ETH_ALEN] = { 0, 111, 111, 111, 111, 0 };
+ int ret;
+ const u8 *tag_ptr;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ if ((ret = request_firmware(&config_fw, filename, rwnx_hw->dev))) {
+ printk(KERN_CRIT "%s: Failed to get %s (%d)\n", __func__, filename, ret);
+ return ret;
+ }
+
+ /* Get MAC Address */
+ tag_ptr = rwnx_find_tag(config_fw->data, config_fw->size,
+ "MAC_ADDR=", strlen("00:00:00:00:00:00"));
+ if (tag_ptr != NULL) {
+ u8 *addr = config->mac_addr;
+ if (sscanf(tag_ptr,
+ "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
+ addr + 0, addr + 1, addr + 2,
+ addr + 3, addr + 4, addr + 5) != ETH_ALEN)
+ memcpy(config->mac_addr, dflt_mac, ETH_ALEN);
+ } else
+ memcpy(config->mac_addr, dflt_mac, ETH_ALEN);
+
+ RWNX_DBG("MAC Address is:\n%pM\n", config->mac_addr);
+
+ /* Release the configuration file */
+ release_firmware(config_fw);
+
+ return 0;
+}
+
+/**
+ * Parse the Config file used at init time
+ */
+int rwnx_parse_phy_configfile(struct rwnx_hw *rwnx_hw, const char *filename,
+ struct rwnx_phy_conf_file *config, int path)
+{
+ const struct firmware *config_fw;
+ int ret;
+ const u8 *tag_ptr;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ if ((ret = request_firmware(&config_fw, filename, rwnx_hw->dev))) {
+ printk(KERN_CRIT "%s: Failed to get %s (%d)\n", __func__, filename, ret);
+ return ret;
+ }
+
+ /* Get Trident path mapping */
+ tag_ptr = rwnx_find_tag(config_fw->data, config_fw->size,
+ "TRD_PATH_MAPPING=", strlen("00"));
+ if (tag_ptr != NULL) {
+ u8 val;
+ if (sscanf(tag_ptr, "%hhx", &val) == 1)
+ config->trd.path_mapping = val;
+ else
+ config->trd.path_mapping = path;
+ } else
+ config->trd.path_mapping = path;
+
+ RWNX_DBG("Trident path mapping is: %d\n", config->trd.path_mapping);
+
+ /* Get DC offset compensation */
+ tag_ptr = rwnx_find_tag(config_fw->data, config_fw->size,
+ "TX_DC_OFF_COMP=", strlen("00000000"));
+ if (tag_ptr != NULL) {
+ if (sscanf(tag_ptr, "%08x", &config->trd.tx_dc_off_comp) != 1)
+ config->trd.tx_dc_off_comp = 0;
+ } else
+ config->trd.tx_dc_off_comp = 0;
+
+ RWNX_DBG("TX DC offset compensation is: %08X\n", config->trd.tx_dc_off_comp);
+
+ /* Get Karst TX IQ compensation value for path0 on 2.4GHz */
+ tag_ptr = rwnx_find_tag(config_fw->data, config_fw->size,
+ "KARST_TX_IQ_COMP_2_4G_PATH_0=", strlen("00000000"));
+ if (tag_ptr != NULL) {
+ if (sscanf(tag_ptr, "%08x", &config->karst.tx_iq_comp_2_4G[0]) != 1)
+ config->karst.tx_iq_comp_2_4G[0] = 0x01000000;
+ } else
+ config->karst.tx_iq_comp_2_4G[0] = 0x01000000;
+
+ RWNX_DBG("Karst TX IQ compensation for path 0 on 2.4GHz is: %08X\n", config->karst.tx_iq_comp_2_4G[0]);
+
+ /* Get Karst TX IQ compensation value for path1 on 2.4GHz */
+ tag_ptr = rwnx_find_tag(config_fw->data, config_fw->size,
+ "KARST_TX_IQ_COMP_2_4G_PATH_1=", strlen("00000000"));
+ if (tag_ptr != NULL) {
+ if (sscanf(tag_ptr, "%08x", &config->karst.tx_iq_comp_2_4G[1]) != 1)
+ config->karst.tx_iq_comp_2_4G[1] = 0x01000000;
+ } else
+ config->karst.tx_iq_comp_2_4G[1] = 0x01000000;
+
+ RWNX_DBG("Karst TX IQ compensation for path 1 on 2.4GHz is: %08X\n", config->karst.tx_iq_comp_2_4G[1]);
+
+ /* Get Karst RX IQ compensation value for path0 on 2.4GHz */
+ tag_ptr = rwnx_find_tag(config_fw->data, config_fw->size,
+ "KARST_RX_IQ_COMP_2_4G_PATH_0=", strlen("00000000"));
+ if (tag_ptr != NULL) {
+ if (sscanf(tag_ptr, "%08x", &config->karst.rx_iq_comp_2_4G[0]) != 1)
+ config->karst.rx_iq_comp_2_4G[0] = 0x01000000;
+ } else
+ config->karst.rx_iq_comp_2_4G[0] = 0x01000000;
+
+ RWNX_DBG("Karst RX IQ compensation for path 0 on 2.4GHz is: %08X\n", config->karst.rx_iq_comp_2_4G[0]);
+
+ /* Get Karst RX IQ compensation value for path1 on 2.4GHz */
+ tag_ptr = rwnx_find_tag(config_fw->data, config_fw->size,
+ "KARST_RX_IQ_COMP_2_4G_PATH_1=", strlen("00000000"));
+ if (tag_ptr != NULL) {
+ if (sscanf(tag_ptr, "%08x", &config->karst.rx_iq_comp_2_4G[1]) != 1)
+ config->karst.rx_iq_comp_2_4G[1] = 0x01000000;
+ } else
+ config->karst.rx_iq_comp_2_4G[1] = 0x01000000;
+
+ RWNX_DBG("Karst RX IQ compensation for path 1 on 2.4GHz is: %08X\n", config->karst.rx_iq_comp_2_4G[1]);
+
+ /* Get Karst TX IQ compensation value for path0 on 5GHz */
+ tag_ptr = rwnx_find_tag(config_fw->data, config_fw->size,
+ "KARST_TX_IQ_COMP_5G_PATH_0=", strlen("00000000"));
+ if (tag_ptr != NULL) {
+ if (sscanf(tag_ptr, "%08x", &config->karst.tx_iq_comp_5G[0]) != 1)
+ config->karst.tx_iq_comp_5G[0] = 0x01000000;
+ } else
+ config->karst.tx_iq_comp_5G[0] = 0x01000000;
+
+ RWNX_DBG("Karst TX IQ compensation for path 0 on 5GHz is: %08X\n", config->karst.tx_iq_comp_5G[0]);
+
+ /* Get Karst TX IQ compensation value for path1 on 5GHz */
+ tag_ptr = rwnx_find_tag(config_fw->data, config_fw->size,
+ "KARST_TX_IQ_COMP_5G_PATH_1=", strlen("00000000"));
+ if (tag_ptr != NULL) {
+ if (sscanf(tag_ptr, "%08x", &config->karst.tx_iq_comp_5G[1]) != 1)
+ config->karst.tx_iq_comp_5G[1] = 0x01000000;
+ } else
+ config->karst.tx_iq_comp_5G[1] = 0x01000000;
+
+ RWNX_DBG("Karst TX IQ compensation for path 1 on 5GHz is: %08X\n", config->karst.tx_iq_comp_5G[1]);
+
+ /* Get Karst RX IQ compensation value for path0 on 5GHz */
+ tag_ptr = rwnx_find_tag(config_fw->data, config_fw->size,
+ "KARST_RX_IQ_COMP_5G_PATH_0=", strlen("00000000"));
+ if (tag_ptr != NULL) {
+ if (sscanf(tag_ptr, "%08x", &config->karst.rx_iq_comp_5G[0]) != 1)
+ config->karst.rx_iq_comp_5G[0] = 0x01000000;
+ } else
+ config->karst.rx_iq_comp_5G[0] = 0x01000000;
+
+ RWNX_DBG("Karst RX IQ compensation for path 0 on 5GHz is: %08X\n", config->karst.rx_iq_comp_5G[0]);
+
+ /* Get Karst RX IQ compensation value for path1 on 5GHz */
+ tag_ptr = rwnx_find_tag(config_fw->data, config_fw->size,
+ "KARST_RX_IQ_COMP_5G_PATH_1=", strlen("00000000"));
+ if (tag_ptr != NULL) {
+ if (sscanf(tag_ptr, "%08x", &config->karst.rx_iq_comp_5G[1]) != 1)
+ config->karst.rx_iq_comp_5G[1] = 0x01000000;
+ } else
+ config->karst.rx_iq_comp_5G[1] = 0x01000000;
+
+ RWNX_DBG("Karst RX IQ compensation for path 1 on 5GHz is: %08X\n", config->karst.rx_iq_comp_5G[1]);
+
+ /* Get Karst default path */
+ tag_ptr = rwnx_find_tag(config_fw->data, config_fw->size,
+ "KARST_DEFAULT_PATH=", strlen("00"));
+ if (tag_ptr != NULL) {
+ u8 val;
+ if (sscanf(tag_ptr, "%hhx", &val) == 1)
+ config->karst.path_used = val;
+ else
+ config->karst.path_used = path;
+ } else
+ config->karst.path_used = path;
+
+ RWNX_DBG("Karst default path is: %d\n", config->karst.path_used);
+
+ /* Release the configuration file */
+ release_firmware(config_fw);
+
+ return 0;
+}
+
diff --git a/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_cfgfile.h b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_cfgfile.h
new file mode 100644
index 000000000000..2e91f7fb399a
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_cfgfile.h
@@ -0,0 +1,35 @@
+/**
+ ****************************************************************************************
+ *
+ * @file rwnx_cfgfile.h
+ *
+ * Copyright (C) RivieraWaves 2012-2019
+ *
+ ****************************************************************************************
+ */
+
+#ifndef _RWNX_CFGFILE_H_
+#define _RWNX_CFGFILE_H_
+
+/*
+ * Structure used to retrieve information from the Config file used at Initialization time
+ */
+struct rwnx_conf_file {
+ u8 mac_addr[ETH_ALEN];
+};
+
+/*
+ * Structure used to retrieve information from the PHY Config file used at Initialization time
+ */
+struct rwnx_phy_conf_file {
+ struct phy_trd_cfg_tag trd;
+ struct phy_karst_cfg_tag karst;
+};
+
+int rwnx_parse_configfile(struct rwnx_hw *rwnx_hw, const char *filename,
+ struct rwnx_conf_file *config);
+
+int rwnx_parse_phy_configfile(struct rwnx_hw *rwnx_hw, const char *filename,
+ struct rwnx_phy_conf_file *config, int path);
+
+#endif /* _RWNX_CFGFILE_H_ */
diff --git a/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_cmds.c b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_cmds.c
new file mode 100644
index 000000000000..562d5701683d
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_cmds.c
@@ -0,0 +1,541 @@
+/**
+ ******************************************************************************
+ *
+ * rwnx_cmds.c
+ *
+ * Handles queueing (push to IPC, ack/cfm from IPC) of commands issued to
+ * LMAC FW
+ *
+ * Copyright (C) RivieraWaves 2014-2019
+ *
+ ******************************************************************************
+ */
+
+//#define CREATE_TRACE_POINTS
+#include <linux/list.h>
+
+#include "rwnx_cmds.h"
+#include "rwnx_defs.h"
+#include "rwnx_strs.h"
+#include "rwnx_events.h"
+#include "aicwf_txrxif.h"
+#ifdef AICWF_SDIO_SUPPORT
+#include "aicwf_sdio.h"
+#else
+#include "aicwf_usb.h"
+#endif
+/**
+ *
+ */
+extern int aicwf_sdio_writeb(struct aic_sdio_dev *sdiodev, uint regaddr, u8 val);
+
+void rwnx_cmd_free(struct rwnx_cmd *cmd);
+
+static void cmd_dump(const struct rwnx_cmd *cmd)
+{
+ printk(KERN_CRIT "tkn[%d] flags:%04x result:%3d cmd:%4d-%-24s - reqcfm(%4d-%-s)\n",
+ cmd->tkn, cmd->flags, cmd->result, cmd->id, RWNX_ID2STR(cmd->id),
+ cmd->reqid, cmd->reqid != (lmac_msg_id_t)-1 ? RWNX_ID2STR(cmd->reqid) : "none");
+}
+
+/**
+ *
+ */
+static void cmd_complete(struct rwnx_cmd_mgr *cmd_mgr, struct rwnx_cmd *cmd)
+{
+ //RWNX_DBG(RWNX_FN_ENTRY_STR);
+ lockdep_assert_held(&cmd_mgr->lock);
+
+ list_del(&cmd->list);
+ cmd_mgr->queue_sz--;
+
+ cmd->flags |= RWNX_CMD_FLAG_DONE;
+ if (cmd->flags & RWNX_CMD_FLAG_NONBLOCK) {
+ rwnx_cmd_free(cmd);//kfree(cmd);AIDEN
+ } else {
+ if (RWNX_CMD_WAIT_COMPLETE(cmd->flags)) {
+ cmd->result = 0;
+ complete(&cmd->complete);
+ }
+ }
+}
+
+int cmd_mgr_queue_force_defer(struct rwnx_cmd_mgr *cmd_mgr, struct rwnx_cmd *cmd)
+{
+ bool defer_push = false;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+#ifdef CREATE_TRACE_POINTS
+ trace_msg_send(cmd->id);
+#endif
+ spin_lock_bh(&cmd_mgr->lock);
+
+ if (cmd_mgr->state == RWNX_CMD_MGR_STATE_CRASHED) {
+ printk(KERN_CRIT"cmd queue crashed\n");
+ cmd->result = -EPIPE;
+ spin_unlock_bh(&cmd_mgr->lock);
+ return -EPIPE;
+ }
+
+ #ifndef CONFIG_RWNX_FHOST
+ if (!list_empty(&cmd_mgr->cmds)) {
+ if (cmd_mgr->queue_sz == cmd_mgr->max_queue_sz) {
+ printk(KERN_CRIT"Too many cmds (%d) already queued\n",
+ cmd_mgr->max_queue_sz);
+ cmd->result = -ENOMEM;
+ spin_unlock_bh(&cmd_mgr->lock);
+ return -ENOMEM;
+ }
+ }
+ #endif
+
+ cmd->flags |= RWNX_CMD_FLAG_WAIT_PUSH;
+ defer_push = true;
+
+ if (cmd->flags & RWNX_CMD_FLAG_REQ_CFM)
+ cmd->flags |= RWNX_CMD_FLAG_WAIT_CFM;
+
+ cmd->tkn = cmd_mgr->next_tkn++;
+ cmd->result = -EINTR;
+
+ if (!(cmd->flags & RWNX_CMD_FLAG_NONBLOCK))
+ init_completion(&cmd->complete);
+
+ list_add_tail(&cmd->list, &cmd_mgr->cmds);
+ cmd_mgr->queue_sz++;
+ spin_unlock_bh(&cmd_mgr->lock);
+
+ WAKE_CMD_WORK(cmd_mgr);
+ return 0;
+}
+
+void rwnx_msg_free_(struct lmac_msg *msg);
+
+
+static int cmd_mgr_queue(struct rwnx_cmd_mgr *cmd_mgr, struct rwnx_cmd *cmd)
+{
+ int ret = 0;
+#ifdef AICWF_SDIO_SUPPORT
+ struct aic_sdio_dev *sdiodev = container_of(cmd_mgr, struct aic_sdio_dev, cmd_mgr);
+#endif
+#ifdef AICWF_USB_SUPPORT
+
+ struct aic_usb_dev *usbdev = container_of(cmd_mgr, struct aic_usb_dev, cmd_mgr);
+#endif
+ bool defer_push = false;
+
+ //RWNX_DBG(RWNX_FN_ENTRY_STR);
+#ifdef CREATE_TRACE_POINTS
+ trace_msg_send(cmd->id);
+#endif
+ spin_lock_bh(&cmd_mgr->lock);
+
+ if (cmd_mgr->state == RWNX_CMD_MGR_STATE_CRASHED) {
+ printk(KERN_CRIT"cmd queue crashed\n");
+ cmd->result = -EPIPE;
+ spin_unlock_bh(&cmd_mgr->lock);
+ return -EPIPE;
+ }
+
+ #ifndef CONFIG_RWNX_FHOST
+ if (!list_empty(&cmd_mgr->cmds)) {
+ struct rwnx_cmd *last;
+
+ if (cmd_mgr->queue_sz == cmd_mgr->max_queue_sz) {
+ printk(KERN_CRIT"Too many cmds (%d) already queued\n",
+ cmd_mgr->max_queue_sz);
+ cmd->result = -ENOMEM;
+ spin_unlock_bh(&cmd_mgr->lock);
+ return -ENOMEM;
+ }
+ last = list_entry(cmd_mgr->cmds.prev, struct rwnx_cmd, list);
+ if (last->flags & (RWNX_CMD_FLAG_WAIT_ACK | RWNX_CMD_FLAG_WAIT_PUSH | RWNX_CMD_FLAG_WAIT_CFM)) {
+#if 0 // queue even NONBLOCK command.
+ if (cmd->flags & RWNX_CMD_FLAG_NONBLOCK) {
+ printk(KERN_CRIT"cmd queue busy\n");
+ cmd->result = -EBUSY;
+ spin_unlock_bh(&cmd_mgr->lock);
+ return -EBUSY;
+ }
+#endif
+ cmd->flags |= RWNX_CMD_FLAG_WAIT_PUSH;
+ defer_push = true;
+ }
+ }
+ #endif
+
+#if 0
+ cmd->flags |= RWNX_CMD_FLAG_WAIT_ACK;
+#endif
+ if (cmd->flags & RWNX_CMD_FLAG_REQ_CFM)
+ cmd->flags |= RWNX_CMD_FLAG_WAIT_CFM;
+
+ cmd->tkn = cmd_mgr->next_tkn++;
+ cmd->result = -EINTR;
+
+ if (!(cmd->flags & RWNX_CMD_FLAG_NONBLOCK))
+ init_completion(&cmd->complete);
+
+ list_add_tail(&cmd->list, &cmd_mgr->cmds);
+ cmd_mgr->queue_sz++;
+
+ if(cmd->a2e_msg->id == ME_TRAFFIC_IND_REQ
+ #ifdef AICWF_ARP_OFFLOAD
+ || cmd->a2e_msg->id == MM_SET_ARPOFFLOAD_REQ
+ #endif
+ ) {
+ defer_push = true;
+ cmd->flags |= RWNX_CMD_FLAG_WAIT_PUSH;
+ //printk("defer push: tkn=%d\r\n", cmd->tkn);
+ }
+
+ spin_unlock_bh(&cmd_mgr->lock);
+ if (!defer_push) {
+ AICWFDBG(LOGTRACE, "queue:id=%x, param_len=%u\n",cmd->a2e_msg->id, cmd->a2e_msg->param_len);
+
+ #ifdef AICWF_SDIO_SUPPORT
+ aicwf_set_cmd_tx((void *)(sdiodev), cmd->a2e_msg, sizeof(struct lmac_msg) + cmd->a2e_msg->param_len);
+ #else
+ aicwf_set_cmd_tx((void *)(usbdev), cmd->a2e_msg, sizeof(struct lmac_msg) + cmd->a2e_msg->param_len);
+ #endif
+ //rwnx_ipc_msg_push(rwnx_hw, cmd, RWNX_CMD_A2EMSG_LEN(cmd->a2e_msg));
+
+ kfree(cmd->a2e_msg);
+ } else {
+ WAKE_CMD_WORK(cmd_mgr);
+ return 0;
+ }
+
+ if (!(cmd->flags & RWNX_CMD_FLAG_NONBLOCK)) {
+ #ifdef CONFIG_RWNX_FHOST
+ if (wait_for_completion_killable(&cmd->complete)) {
+ cmd->result = -EINTR;
+ spin_lock_bh(&cmd_mgr->lock);
+ cmd_complete(cmd_mgr, cmd);
+ spin_unlock_bh(&cmd_mgr->lock);
+ /* TODO: kill the cmd at fw level */
+ }
+ #else
+ unsigned long tout = msecs_to_jiffies(RWNX_80211_CMD_TIMEOUT_MS/*AIDEN workaround* cmd_mgr->queue_sz*/);
+ if (!wait_for_completion_killable_timeout(&cmd->complete, tout)) {
+ printk(KERN_CRIT"%s cmd timed-out cmd_mgr->queue_sz:%d\n", __func__,cmd_mgr->queue_sz);
+ #ifdef AICWF_SDIO_SUPPORT
+ ret = aicwf_sdio_writeb(sdiodev, SDIOWIFI_WAKEUP_REG, 2);
+ if (ret < 0) {
+ sdio_err("reg:%d write failed!\n", SDIOWIFI_WAKEUP_REG);
+ }
+ #endif
+
+ cmd_dump(cmd);
+ spin_lock_bh(&cmd_mgr->lock);
+
+ cmd_mgr->state = RWNX_CMD_MGR_STATE_CRASHED;
+ if (!(cmd->flags & RWNX_CMD_FLAG_DONE)) {
+ cmd->result = -ETIMEDOUT;
+ cmd_complete(cmd_mgr, cmd);
+ }
+ ret = -ETIMEDOUT;
+ spin_unlock_bh(&cmd_mgr->lock);
+ }
+ else{
+ rwnx_cmd_free(cmd);//kfree(cmd);AIDEN
+ if(!list_empty(&cmd_mgr->cmds) && usbdev->state == USB_UP_ST)
+ WAKE_CMD_WORK(cmd_mgr);
+ }
+ #endif
+ } else {
+ cmd->result = 0;
+ }
+ return ret;
+}
+
+/**
+ *
+ */
+static int cmd_mgr_llind(struct rwnx_cmd_mgr *cmd_mgr, struct rwnx_cmd *cmd)
+{
+ struct rwnx_cmd *cur, *acked = NULL, *next = NULL;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ spin_lock_bh(&cmd_mgr->lock);
+ list_for_each_entry(cur, &cmd_mgr->cmds, list) {
+ if (!acked) {
+ if (cur->tkn == cmd->tkn) {
+ if (WARN_ON_ONCE(cur != cmd)) {
+ cmd_dump(cmd);
+ }
+ acked = cur;
+ continue;
+ }
+ }
+ if (cur->flags & RWNX_CMD_FLAG_WAIT_PUSH) {
+ next = cur;
+ break;
+ }
+ }
+ if (!acked) {
+ printk(KERN_CRIT "Error: acked cmd not found\n");
+ } else {
+ cmd->flags &= ~RWNX_CMD_FLAG_WAIT_ACK;
+ if (RWNX_CMD_WAIT_COMPLETE(cmd->flags))
+ cmd_complete(cmd_mgr, cmd);
+ }
+
+ if (next) {
+ #if 0 //there is no ack
+ struct rwnx_hw *rwnx_hw = container_of(cmd_mgr, struct rwnx_hw, cmd_mgr);
+ next->flags &= ~RWNX_CMD_FLAG_WAIT_PUSH;
+ rwnx_ipc_msg_push(rwnx_hw, next, RWNX_CMD_A2EMSG_LEN(next->a2e_msg));
+ kfree(next->a2e_msg);
+ #endif
+ }
+ spin_unlock(&cmd_mgr->lock);
+
+ return 0;
+}
+
+void cmd_mgr_task_process(struct work_struct *work)
+{
+ struct rwnx_cmd_mgr *cmd_mgr = container_of(work, struct rwnx_cmd_mgr, cmdWork);
+ struct rwnx_cmd *cur, *next = NULL;
+ unsigned long tout;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ while(1) {
+ next = NULL;
+ spin_lock_bh(&cmd_mgr->lock);
+
+ list_for_each_entry(cur, &cmd_mgr->cmds, list) {
+ if (cur->flags & RWNX_CMD_FLAG_WAIT_PUSH) { //just judge the first
+ next = cur;
+ }
+ break;
+ }
+ spin_unlock_bh(&cmd_mgr->lock);
+
+ if(next == NULL)
+ break;
+
+ if (next) {
+ #ifdef AICWF_SDIO_SUPPORT
+ struct aic_sdio_dev *sdiodev = container_of(cmd_mgr, struct aic_sdio_dev, cmd_mgr);
+ #endif
+ #ifdef AICWF_USB_SUPPORT
+ struct aic_usb_dev *usbdev = container_of(cmd_mgr, struct aic_usb_dev, cmd_mgr);
+ #endif
+ next->flags &= ~RWNX_CMD_FLAG_WAIT_PUSH;
+
+ //printk("cmd_process, cmd->id=%d, tkn=%d\r\n",next->reqid, next->tkn);
+ //rwnx_ipc_msg_push(rwnx_hw, next, RWNX_CMD_A2EMSG_LEN(next->a2e_msg));
+#ifdef AICWF_SDIO_SUPPORT
+ aicwf_set_cmd_tx((void *)(sdiodev), next->a2e_msg, sizeof(struct lmac_msg) + next->a2e_msg->param_len);
+#else
+ aicwf_set_cmd_tx((void *)(usbdev), next->a2e_msg, sizeof(struct lmac_msg) + next->a2e_msg->param_len);
+#endif
+ kfree(next->a2e_msg);
+
+ tout = msecs_to_jiffies(RWNX_80211_CMD_TIMEOUT_MS * cmd_mgr->queue_sz);
+ if (!wait_for_completion_killable_timeout(&next->complete, tout)) {
+ printk(KERN_CRIT"%s cmd timed-out cmd_mgr->queue_sz:%d\n", __func__, cmd_mgr->queue_sz);
+ cmd_dump(next);
+ spin_lock_bh(&cmd_mgr->lock);
+ //AIDEN workaround
+ cmd_mgr->state = RWNX_CMD_MGR_STATE_CRASHED;
+ if (!(next->flags & RWNX_CMD_FLAG_DONE)) {
+ next->result = -ETIMEDOUT;
+ cmd_complete(cmd_mgr, next);
+ }
+ spin_unlock_bh(&cmd_mgr->lock);
+ } else
+ rwnx_cmd_free(next);//kfree(next);AIDEN
+ }
+ }
+
+}
+
+
+static int cmd_mgr_run_callback(struct rwnx_hw *rwnx_hw, struct rwnx_cmd *cmd,
+ struct rwnx_cmd_e2amsg *msg, msg_cb_fct cb)
+{
+ int res;
+
+ if (! cb){
+ return 0;
+ }
+ //RWNX_DBG(RWNX_FN_ENTRY_STR);
+#ifndef CONFIG_DEBUG_ATOMIC_SLEEP
+ //spin_lock_bh(&rwnx_hw->cb_lock);
+#endif
+ res = cb(rwnx_hw, cmd, msg);
+#ifndef CONFIG_DEBUG_ATOMIC_SLEEP
+ //spin_unlock_bh(&rwnx_hw->cb_lock);
+#endif
+
+ return res;
+}
+
+/**
+ *
+
+ */
+static int cmd_mgr_msgind(struct rwnx_cmd_mgr *cmd_mgr, struct rwnx_cmd_e2amsg *msg,
+ msg_cb_fct cb)
+{
+#ifdef AICWF_SDIO_SUPPORT
+ struct aic_sdio_dev *sdiodev = container_of(cmd_mgr, struct aic_sdio_dev, cmd_mgr);
+ struct rwnx_hw *rwnx_hw = sdiodev->rwnx_hw;
+#endif
+#ifdef AICWF_USB_SUPPORT
+ struct aic_usb_dev *usbdev = container_of(cmd_mgr, struct aic_usb_dev, cmd_mgr);
+ struct rwnx_hw *rwnx_hw = usbdev->rwnx_hw;
+#endif
+ struct rwnx_cmd *cmd, *pos;
+ bool found = false;
+
+ // RWNX_DBG(RWNX_FN_ENTRY_STR);
+#ifdef CREATE_TRACE_POINTS
+ trace_msg_recv(msg->id);
+#endif
+ AICWFDBG(LOGTRACE, "%s cmd->id=%d\n", __func__, msg->id);
+ spin_lock_bh(&cmd_mgr->lock);
+ list_for_each_entry_safe(cmd, pos, &cmd_mgr->cmds, list) {
+ if (cmd->reqid == msg->id &&
+ (cmd->flags & RWNX_CMD_FLAG_WAIT_CFM)) {
+
+ if (!cmd_mgr_run_callback(rwnx_hw, cmd, msg, cb)) {
+ found = true;
+ cmd->flags &= ~RWNX_CMD_FLAG_WAIT_CFM;
+
+ if (WARN((msg->param_len > RWNX_CMD_E2AMSG_LEN_MAX),
+ "Unexpect E2A msg len %d > %d\n", msg->param_len,
+ RWNX_CMD_E2AMSG_LEN_MAX)) {
+ msg->param_len = RWNX_CMD_E2AMSG_LEN_MAX;
+ }
+
+ if (cmd->e2a_msg && msg->param_len)
+ memcpy(cmd->e2a_msg, &msg->param, msg->param_len);
+
+ if (RWNX_CMD_WAIT_COMPLETE(cmd->flags))
+ cmd_complete(cmd_mgr, cmd);
+
+ break;
+ }
+ }
+ }
+ spin_unlock_bh(&cmd_mgr->lock);
+
+ if (!found)
+ cmd_mgr_run_callback(rwnx_hw, NULL, msg, cb);
+
+ return 0;
+}
+
+/**
+ *
+ */
+static void cmd_mgr_print(struct rwnx_cmd_mgr *cmd_mgr)
+{
+ struct rwnx_cmd *cur;
+
+ spin_lock_bh(&cmd_mgr->lock);
+ RWNX_DBG("q_sz/max: %2d / %2d - next tkn: %d\n",
+ cmd_mgr->queue_sz, cmd_mgr->max_queue_sz,
+ cmd_mgr->next_tkn);
+ list_for_each_entry(cur, &cmd_mgr->cmds, list) {
+ cmd_dump(cur);
+ }
+ spin_unlock_bh(&cmd_mgr->lock);
+}
+
+static void cmd_mgr_drain(struct rwnx_cmd_mgr *cmd_mgr)
+{
+ struct rwnx_cmd *cur, *nxt;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ spin_lock_bh(&cmd_mgr->lock);
+ list_for_each_entry_safe(cur, nxt, &cmd_mgr->cmds, list) {
+ list_del(&cur->list);
+ cmd_mgr->queue_sz--;
+ if (!(cur->flags & RWNX_CMD_FLAG_NONBLOCK))
+ complete(&cur->complete);
+ }
+ spin_unlock_bh(&cmd_mgr->lock);
+}
+
+void rwnx_cmd_mgr_init(struct rwnx_cmd_mgr *cmd_mgr)
+{
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ INIT_LIST_HEAD(&cmd_mgr->cmds);
+ cmd_mgr->state = RWNX_CMD_MGR_STATE_INITED;
+ spin_lock_init(&cmd_mgr->lock);
+ cmd_mgr->max_queue_sz = RWNX_CMD_MAX_QUEUED;
+ cmd_mgr->queue = &cmd_mgr_queue;
+ cmd_mgr->print = &cmd_mgr_print;
+ cmd_mgr->drain = &cmd_mgr_drain;
+ cmd_mgr->llind = &cmd_mgr_llind;
+ cmd_mgr->msgind = &cmd_mgr_msgind;
+
+ INIT_WORK(&cmd_mgr->cmdWork, cmd_mgr_task_process);
+ cmd_mgr->cmd_wq = create_singlethread_workqueue("cmd_wq");
+ if (!cmd_mgr->cmd_wq) {
+ txrx_err("insufficient memory to create cmd workqueue.\n");
+ return;
+ }
+}
+
+void rwnx_cmd_mgr_deinit(struct rwnx_cmd_mgr *cmd_mgr)
+{
+ cmd_mgr->print(cmd_mgr);
+ cmd_mgr->drain(cmd_mgr);
+ cmd_mgr->print(cmd_mgr);
+ flush_workqueue(cmd_mgr->cmd_wq);
+ destroy_workqueue(cmd_mgr->cmd_wq);
+ memset(cmd_mgr, 0, sizeof(*cmd_mgr));
+}
+
+
+void aicwf_set_cmd_tx(void *dev, struct lmac_msg *msg, uint len)
+{
+ u8 *buffer = NULL;
+ u16 index = 0;
+#ifdef AICWF_SDIO_SUPPORT
+ struct aic_sdio_dev *sdiodev = (struct aic_sdio_dev *)dev;
+ struct aicwf_bus *bus = sdiodev->bus_if;
+#else
+ struct aic_usb_dev *usbdev = (struct aic_usb_dev *)dev;
+ struct aicwf_bus *bus = NULL;
+ if (!usbdev->state) {
+ printk("down msg \n");
+ return;
+ }
+ bus = usbdev->bus_if;
+#endif
+ buffer = bus->cmd_buf;
+
+ memset(buffer, 0, CMD_BUF_MAX);
+ buffer[0] = (len+4) & 0x00ff;
+ buffer[1] = ((len+4) >> 8) &0x0f;
+ buffer[2] = 0x11;
+ buffer[3] = 0x0;
+ index += 4;
+ //there is a dummy word
+ index += 4;
+
+ //make sure little endian
+ put_u16(&buffer[index], msg->id);
+ index += 2;
+ put_u16(&buffer[index], msg->dest_id);
+ index += 2;
+ put_u16(&buffer[index], msg->src_id);
+ index += 2;
+ put_u16(&buffer[index], msg->param_len);
+ index += 2;
+ memcpy(&buffer[index], (u8 *)msg->param, msg->param_len);
+
+ aicwf_bus_txmsg(bus, buffer, len + 8);
+}
+
diff --git a/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_cmds.h b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_cmds.h
new file mode 100644
index 000000000000..8862a86e3920
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_cmds.h
@@ -0,0 +1,120 @@
+/**
+ ******************************************************************************
+ *
+ * rwnx_cmds.h
+ *
+ * Copyright (C) RivieraWaves 2014-2019
+ *
+ ******************************************************************************
+ */
+
+#ifndef _RWNX_CMDS_H_
+#define _RWNX_CMDS_H_
+
+#include <linux/spinlock.h>
+#include <linux/completion.h>
+#include <linux/module.h>
+#include "lmac_msg.h"
+
+#ifdef CONFIG_RWNX_SDM
+#define RWNX_80211_CMD_TIMEOUT_MS (20 * 300)
+#elif defined(CONFIG_RWNX_FHOST)
+#define RWNX_80211_CMD_TIMEOUT_MS (10000)
+#else
+#define RWNX_80211_CMD_TIMEOUT_MS 4000//500//300
+#endif
+
+#define RWNX_CMD_FLAG_NONBLOCK BIT(0)
+#define RWNX_CMD_FLAG_REQ_CFM BIT(1)
+#define RWNX_CMD_FLAG_WAIT_PUSH BIT(2)
+#define RWNX_CMD_FLAG_WAIT_ACK BIT(3)
+#define RWNX_CMD_FLAG_WAIT_CFM BIT(4)
+#define RWNX_CMD_FLAG_DONE BIT(5)
+/* ATM IPC design makes it possible to get the CFM before the ACK,
+ * otherwise this could have simply been a state enum */
+#define RWNX_CMD_WAIT_COMPLETE(flags) \
+ (!(flags & (RWNX_CMD_FLAG_WAIT_ACK | RWNX_CMD_FLAG_WAIT_CFM)))
+
+#define RWNX_CMD_MAX_QUEUED 16//8 AIDEN
+
+#ifdef CONFIG_RWNX_FHOST
+#include "ipc_fhost.h"
+#define rwnx_cmd_e2amsg ipc_fhost_msg
+#define rwnx_cmd_a2emsg ipc_fhost_msg
+#define RWNX_CMD_A2EMSG_LEN(m) (m->param_len)
+#define RWNX_CMD_E2AMSG_LEN_MAX IPC_FHOST_MSG_BUF_SIZE
+struct rwnx_term_stream;
+
+#else /* !CONFIG_RWNX_FHOST*/
+#include "ipc_shared.h"
+#define rwnx_cmd_e2amsg ipc_e2a_msg
+#define rwnx_cmd_a2emsg lmac_msg
+#define RWNX_CMD_A2EMSG_LEN(m) (sizeof(struct lmac_msg) + m->param_len)
+#define RWNX_CMD_E2AMSG_LEN_MAX (IPC_E2A_MSG_PARAM_SIZE * 4)
+
+#endif /* CONFIG_RWNX_FHOST*/
+
+struct rwnx_hw;
+struct rwnx_cmd;
+typedef int (*msg_cb_fct)(struct rwnx_hw *rwnx_hw, struct rwnx_cmd *cmd,
+ struct rwnx_cmd_e2amsg *msg);
+static inline void put_u16(u8 *buf, u16 data)
+{
+ buf[0] = (u8)(data&0x00ff);
+ buf[1] = (u8)((data >> 8)&0x00ff);
+}
+
+enum rwnx_cmd_mgr_state {
+ RWNX_CMD_MGR_STATE_DEINIT,
+ RWNX_CMD_MGR_STATE_INITED,
+ RWNX_CMD_MGR_STATE_CRASHED,
+};
+
+struct rwnx_cmd {
+ struct list_head list;
+ lmac_msg_id_t id;
+ lmac_msg_id_t reqid;
+ struct rwnx_cmd_a2emsg *a2e_msg;
+ char *e2a_msg;
+ u32 tkn;
+ u16 flags;
+
+ struct completion complete;
+ u32 result;
+ u8 used;
+ int array_id;
+ #ifdef CONFIG_RWNX_FHOST
+ struct rwnx_term_stream *stream;
+ #endif
+};
+
+struct rwnx_cmd_mgr {
+ enum rwnx_cmd_mgr_state state;
+ spinlock_t lock;
+ u32 next_tkn;
+ u32 queue_sz;
+ u32 max_queue_sz;
+
+ struct list_head cmds;
+
+ int (*queue)(struct rwnx_cmd_mgr *, struct rwnx_cmd *);
+ int (*llind)(struct rwnx_cmd_mgr *, struct rwnx_cmd *);
+ int (*msgind)(struct rwnx_cmd_mgr *, struct rwnx_cmd_e2amsg *, msg_cb_fct);
+ void (*print)(struct rwnx_cmd_mgr *);
+ void (*drain)(struct rwnx_cmd_mgr *);
+
+ struct work_struct cmdWork;
+ struct workqueue_struct *cmd_wq;
+};
+
+#define WAKE_CMD_WORK(cmd_mgr) \
+ do { \
+ queue_work((cmd_mgr)->cmd_wq, &cmd_mgr->cmdWork); \
+ } while (0)
+
+void rwnx_cmd_mgr_init(struct rwnx_cmd_mgr *cmd_mgr);
+void rwnx_cmd_mgr_deinit(struct rwnx_cmd_mgr *cmd_mgr);
+int cmd_mgr_queue_force_defer(struct rwnx_cmd_mgr *cmd_mgr, struct rwnx_cmd *cmd);
+void aicwf_set_cmd_tx(void *dev, struct lmac_msg *msg, uint len);
+
+#endif /* _RWNX_CMDS_H_ */
diff --git a/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_compat.h b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_compat.h
new file mode 100644
index 000000000000..b85983532a83
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_compat.h
@@ -0,0 +1,428 @@
+/**
+ ******************************************************************************
+ *
+ * @file rwnx_compat.h
+ *
+ * Ensure driver compilation for linux 3.16 to 3.19
+ *
+ * To avoid too many #if LINUX_VERSION_CODE if the code, when prototype change
+ * between different kernel version:
+ * - For external function, define a macro whose name is the function name with
+ * _compat suffix and prototype (actually the number of parameter) of the
+ * latest version. Then latest version this macro simply call the function
+ * and for older kernel version it call the function adapting the api.
+ * - For internal function (e.g. cfg80211_ops) do the same but the macro name
+ * doesn't need to have the _compat suffix when the function is not used
+ * directly by the driver
+ *
+ * Copyright (C) RivieraWaves 2018
+ *
+ ******************************************************************************
+ */
+#ifndef _RWNX_COMPAT_H_
+#define _RWNX_COMPAT_H_
+#include <linux/version.h>
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 10, 0)
+#error "Minimum kernel version supported is 3.10"
+#endif
+
+/* Generic */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 9, 0)
+#define __bf_shf(x) (__builtin_ffsll(x) - 1)
+#define FIELD_PREP(_mask, _val) \
+ (((typeof(_mask))(_val) << __bf_shf(_mask)) & (_mask))
+#else
+#include <linux/bitfield.h>
+#endif
+
+/* CFG80211 */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 20, 0)
+#define IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_MASK IEEE80211_HE_MAC_CAP3_MAX_A_AMPDU_LEN_EXP_MASK
+#endif
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(5, 15, 60)
+#define IEEE80211_MAX_AMPDU_BUF IEEE80211_MAX_AMPDU_BUF_HE
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 19, 0)
+#define IEEE80211_RADIOTAP_HE 23
+#define IEEE80211_RADIOTAP_HE_MU 24
+
+struct ieee80211_radiotap_he {
+ __le16 data1, data2, data3, data4, data5, data6;
+};
+
+enum ieee80211_radiotap_he_bits {
+ IEEE80211_RADIOTAP_HE_DATA1_FORMAT_MASK = 3,
+ IEEE80211_RADIOTAP_HE_DATA1_FORMAT_SU = 0,
+ IEEE80211_RADIOTAP_HE_DATA1_FORMAT_EXT_SU = 1,
+ IEEE80211_RADIOTAP_HE_DATA1_FORMAT_MU = 2,
+ IEEE80211_RADIOTAP_HE_DATA1_FORMAT_TRIG = 3,
+
+ IEEE80211_RADIOTAP_HE_DATA1_BSS_COLOR_KNOWN = 0x0004,
+ IEEE80211_RADIOTAP_HE_DATA1_BEAM_CHANGE_KNOWN = 0x0008,
+ IEEE80211_RADIOTAP_HE_DATA1_UL_DL_KNOWN = 0x0010,
+ IEEE80211_RADIOTAP_HE_DATA1_DATA_MCS_KNOWN = 0x0020,
+ IEEE80211_RADIOTAP_HE_DATA1_DATA_DCM_KNOWN = 0x0040,
+ IEEE80211_RADIOTAP_HE_DATA1_CODING_KNOWN = 0x0080,
+ IEEE80211_RADIOTAP_HE_DATA1_LDPC_XSYMSEG_KNOWN = 0x0100,
+ IEEE80211_RADIOTAP_HE_DATA1_STBC_KNOWN = 0x0200,
+ IEEE80211_RADIOTAP_HE_DATA1_SPTL_REUSE_KNOWN = 0x0400,
+ IEEE80211_RADIOTAP_HE_DATA1_SPTL_REUSE2_KNOWN = 0x0800,
+ IEEE80211_RADIOTAP_HE_DATA1_SPTL_REUSE3_KNOWN = 0x1000,
+ IEEE80211_RADIOTAP_HE_DATA1_SPTL_REUSE4_KNOWN = 0x2000,
+ IEEE80211_RADIOTAP_HE_DATA1_BW_RU_ALLOC_KNOWN = 0x4000,
+ IEEE80211_RADIOTAP_HE_DATA1_DOPPLER_KNOWN = 0x8000,
+
+ IEEE80211_RADIOTAP_HE_DATA2_PRISEC_80_KNOWN = 0x0001,
+ IEEE80211_RADIOTAP_HE_DATA2_GI_KNOWN = 0x0002,
+ IEEE80211_RADIOTAP_HE_DATA2_NUM_LTF_SYMS_KNOWN = 0x0004,
+ IEEE80211_RADIOTAP_HE_DATA2_PRE_FEC_PAD_KNOWN = 0x0008,
+ IEEE80211_RADIOTAP_HE_DATA2_TXBF_KNOWN = 0x0010,
+ IEEE80211_RADIOTAP_HE_DATA2_PE_DISAMBIG_KNOWN = 0x0020,
+ IEEE80211_RADIOTAP_HE_DATA2_TXOP_KNOWN = 0x0040,
+ IEEE80211_RADIOTAP_HE_DATA2_MIDAMBLE_KNOWN = 0x0080,
+ IEEE80211_RADIOTAP_HE_DATA2_RU_OFFSET = 0x3f00,
+ IEEE80211_RADIOTAP_HE_DATA2_RU_OFFSET_KNOWN = 0x4000,
+ IEEE80211_RADIOTAP_HE_DATA2_PRISEC_80_SEC = 0x8000,
+
+ IEEE80211_RADIOTAP_HE_DATA3_BSS_COLOR = 0x003f,
+ IEEE80211_RADIOTAP_HE_DATA3_BEAM_CHANGE = 0x0040,
+ IEEE80211_RADIOTAP_HE_DATA3_UL_DL = 0x0080,
+ IEEE80211_RADIOTAP_HE_DATA3_DATA_MCS = 0x0f00,
+ IEEE80211_RADIOTAP_HE_DATA3_DATA_DCM = 0x1000,
+ IEEE80211_RADIOTAP_HE_DATA3_CODING = 0x2000,
+ IEEE80211_RADIOTAP_HE_DATA3_LDPC_XSYMSEG = 0x4000,
+ IEEE80211_RADIOTAP_HE_DATA3_STBC = 0x8000,
+
+ IEEE80211_RADIOTAP_HE_DATA4_SU_MU_SPTL_REUSE = 0x000f,
+ IEEE80211_RADIOTAP_HE_DATA4_MU_STA_ID = 0x7ff0,
+ IEEE80211_RADIOTAP_HE_DATA4_TB_SPTL_REUSE1 = 0x000f,
+ IEEE80211_RADIOTAP_HE_DATA4_TB_SPTL_REUSE2 = 0x00f0,
+ IEEE80211_RADIOTAP_HE_DATA4_TB_SPTL_REUSE3 = 0x0f00,
+ IEEE80211_RADIOTAP_HE_DATA4_TB_SPTL_REUSE4 = 0xf000,
+
+ IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC = 0x000f,
+ IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_20MHZ = 0,
+ IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_40MHZ = 1,
+ IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_80MHZ = 2,
+ IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_160MHZ = 3,
+ IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_26T = 4,
+ IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_52T = 5,
+ IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_106T = 6,
+ IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_242T = 7,
+ IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_484T = 8,
+ IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_996T = 9,
+ IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_2x996T = 10,
+
+ IEEE80211_RADIOTAP_HE_DATA5_GI = 0x0030,
+ IEEE80211_RADIOTAP_HE_DATA5_GI_0_8 = 0,
+ IEEE80211_RADIOTAP_HE_DATA5_GI_1_6 = 1,
+ IEEE80211_RADIOTAP_HE_DATA5_GI_3_2 = 2,
+
+ IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE = 0x00c0,
+ IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE_UNKNOWN = 0,
+ IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE_1X = 1,
+ IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE_2X = 2,
+ IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE_4X = 3,
+ IEEE80211_RADIOTAP_HE_DATA5_NUM_LTF_SYMS = 0x0700,
+ IEEE80211_RADIOTAP_HE_DATA5_PRE_FEC_PAD = 0x3000,
+ IEEE80211_RADIOTAP_HE_DATA5_TXBF = 0x4000,
+ IEEE80211_RADIOTAP_HE_DATA5_PE_DISAMBIG = 0x8000,
+
+ IEEE80211_RADIOTAP_HE_DATA6_NSTS = 0x000f,
+ IEEE80211_RADIOTAP_HE_DATA6_DOPPLER = 0x0010,
+ IEEE80211_RADIOTAP_HE_DATA6_TXOP = 0x7f00,
+ IEEE80211_RADIOTAP_HE_DATA6_MIDAMBLE_PDCTY = 0x8000,
+};
+
+struct ieee80211_radiotap_he_mu {
+ __le16 flags1, flags2;
+ u8 ru_ch1[4];
+ u8 ru_ch2[4];
+};
+
+enum ieee80211_radiotap_he_mu_bits {
+ IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_MCS = 0x000f,
+ IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_MCS_KNOWN = 0x0010,
+ IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_DCM = 0x0020,
+ IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_DCM_KNOWN = 0x0040,
+ IEEE80211_RADIOTAP_HE_MU_FLAGS1_CH2_CTR_26T_RU_KNOWN = 0x0080,
+ IEEE80211_RADIOTAP_HE_MU_FLAGS1_CH1_RU_KNOWN = 0x0100,
+ IEEE80211_RADIOTAP_HE_MU_FLAGS1_CH2_RU_KNOWN = 0x0200,
+ IEEE80211_RADIOTAP_HE_MU_FLAGS1_CH1_CTR_26T_RU_KNOWN = 0x1000,
+ IEEE80211_RADIOTAP_HE_MU_FLAGS1_CH1_CTR_26T_RU = 0x2000,
+ IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_COMP_KNOWN = 0x4000,
+ IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_SYMS_USERS_KNOWN = 0x8000,
+
+ IEEE80211_RADIOTAP_HE_MU_FLAGS2_BW_FROM_SIG_A_BW = 0x0003,
+ IEEE80211_RADIOTAP_HE_MU_FLAGS2_BW_FROM_SIG_A_BW_20MHZ = 0x0000,
+ IEEE80211_RADIOTAP_HE_MU_FLAGS2_BW_FROM_SIG_A_BW_40MHZ = 0x0001,
+ IEEE80211_RADIOTAP_HE_MU_FLAGS2_BW_FROM_SIG_A_BW_80MHZ = 0x0002,
+ IEEE80211_RADIOTAP_HE_MU_FLAGS2_BW_FROM_SIG_A_BW_160MHZ = 0x0003,
+ IEEE80211_RADIOTAP_HE_MU_FLAGS2_BW_FROM_SIG_A_BW_KNOWN = 0x0004,
+ IEEE80211_RADIOTAP_HE_MU_FLAGS2_SIG_B_COMP = 0x0008,
+ IEEE80211_RADIOTAP_HE_MU_FLAGS2_SIG_B_SYMS_USERS = 0x00f0,
+ IEEE80211_RADIOTAP_HE_MU_FLAGS2_PUNC_FROM_SIG_A_BW = 0x0300,
+ IEEE80211_RADIOTAP_HE_MU_FLAGS2_PUNC_FROM_SIG_A_BW_KNOWN= 0x0400,
+ IEEE80211_RADIOTAP_HE_MU_FLAGS2_CH2_CTR_26T_RU = 0x0800,
+};
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0)
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0)
+#define rwnx_cfg80211_add_iface(wiphy, name, name_assign_type, type, params) \
+ rwnx_cfg80211_add_iface(wiphy, name, type, u32 *flags, params)
+#else
+#define rwnx_cfg80211_add_iface(wiphy, name, name_assign_type, type, params) \
+ rwnx_cfg80211_add_iface(wiphy, name, name_assign_type, type, u32 *flags, params)
+#endif
+
+#define rwnx_cfg80211_change_iface(wiphy, dev, type, params) \
+ rwnx_cfg80211_change_iface(wiphy, dev, type, u32 *flags, params)
+
+#define CCFS0(vht) vht->center_freq_seg1_idx
+#define CCFS1(vht) vht->center_freq_seg2_idx
+
+#else
+#define CCFS0(vht) vht->center_freq_seg0_idx
+#define CCFS1(vht) vht->center_freq_seg1_idx
+
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0)
+#define cfg80211_cqm_rssi_notify(dev, event, level, gfp) \
+ cfg80211_cqm_rssi_notify(dev, event, gfp)
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 9, 0)
+#define ieee80211_amsdu_to_8023s(skb, list, addr, iftype, extra_headroom, check_da, check_sa) \
+ ieee80211_amsdu_to_8023s(skb, list, addr, iftype, extra_headroom, false)
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 7, 0)
+#define NUM_NL80211_BANDS IEEE80211_NUM_BANDS
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 2, 0)
+#define cfg80211_disconnected(dev, reason, ie, len, local, gfp) \
+ cfg80211_disconnected(dev, reason, ie, len, gfp)
+#endif
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0)) && !(defined CONFIG_VENDOR_RWNX)
+#define ieee80211_chandef_to_operating_class(chan_def, op_class) 0
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0)
+#define SURVEY_INFO_TIME SURVEY_INFO_CHANNEL_TIME
+#define SURVEY_INFO_TIME_BUSY SURVEY_INFO_CHANNEL_TIME_BUSY
+#define SURVEY_INFO_TIME_EXT_BUSY SURVEY_INFO_CHANNEL_TIME_EXT_BUSY
+#define SURVEY_INFO_TIME_RX SURVEY_INFO_CHANNEL_TIME_RX
+#define SURVEY_INFO_TIME_TX SURVEY_INFO_CHANNEL_TIME_TX
+
+#define SURVEY_TIME(s) s->channel_time
+#define SURVEY_TIME_BUSY(s) s->channel_time_busy
+#else
+#define SURVEY_TIME(s) s->time
+#define SURVEY_TIME_BUSY(s) s->time_busy
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 19, 0)
+#define cfg80211_ch_switch_started_notify(dev, chandef, count)
+
+#define WLAN_BSS_COEX_INFORMATION_REQUEST BIT(0)
+#define WLAN_EXT_CAPA1_EXT_CHANNEL_SWITCHING BIT(2)
+#define WLAN_EXT_CAPA4_TDLS_BUFFER_STA BIT(4)
+#define WLAN_EXT_CAPA4_TDLS_PEER_PSM BIT(5)
+#define WLAN_EXT_CAPA4_TDLS_CHAN_SWITCH BIT(6)
+#define WLAN_EXT_CAPA5_TDLS_CH_SW_PROHIBITED BIT(7)
+#define NL80211_FEATURE_TDLS_CHANNEL_SWITCH 0
+
+#define STA_TDLS_INITIATOR(sta) 0
+
+#define REGULATORY_IGNORE_STALE_KICKOFF 0
+#else
+#define STA_TDLS_INITIATOR(sta) sta->tdls_initiator
+#endif
+
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 18, 0)) && (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0))
+#define cfg80211_rx_mgmt(wdev, freq, rssi, buf, len, flags) \
+ cfg80211_rx_mgmt(wdev, freq, rssi, buf, len, flags, GFP_ATOMIC)
+#elif LINUX_VERSION_CODE < KERNEL_VERSION(3, 11, 0)
+#define cfg80211_rx_mgmt(wdev, freq, rssi, buf, len, flags) \
+ cfg80211_rx_mgmt(wdev, freq, rssi, buf, len, GFP_ATOMIC)
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0)
+
+#if 0
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 15, 0)
+#define rwnx_cfg80211_tdls_mgmt(wiphy, dev, peer, act, tok, status, peer_capability, initiator, buf, len) \
+ rwnx_cfg80211_tdls_mgmt(wiphy, dev, peer, act, tok, status, peer_capability, buf, len)
+#else
+#define rwnx_cfg80211_tdls_mgmt(wiphy, dev, peer, act, tok, status, peer_capability, initiator, buf, len) \
+ rwnx_cfg80211_tdls_mgmt(wiphy, dev, peer, act, tok, status, buf, len)
+#endif
+#endif
+
+#include <linux/types.h>
+
+struct ieee80211_wmm_ac_param {
+ u8 aci_aifsn; /* AIFSN, ACM, ACI */
+ u8 cw; /* ECWmin, ECWmax (CW = 2^ECW - 1) */
+ __le16 txop_limit;
+} __packed;
+
+struct ieee80211_wmm_param_ie {
+ u8 element_id; /* Element ID: 221 (0xdd); */
+ u8 len; /* Length: 24 */
+ /* required fields for WMM version 1 */
+ u8 oui[3]; /* 00:50:f2 */
+ u8 oui_type; /* 2 */
+ u8 oui_subtype; /* 1 */
+ u8 version; /* 1 for WMM version 1.0 */
+ u8 qos_info; /* AP/STA specific QoS info */
+ u8 reserved; /* 0 */
+ /* AC_BE, AC_BK, AC_VI, AC_VO */
+ struct ieee80211_wmm_ac_param ac[4];
+} __packed;
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 19, 0)
+enum {
+ IEEE80211_HE_MCS_SUPPORT_0_7 = 0,
+ IEEE80211_HE_MCS_SUPPORT_0_9 = 1,
+ IEEE80211_HE_MCS_SUPPORT_0_11 = 2,
+ IEEE80211_HE_MCS_NOT_SUPPORTED = 3,
+};
+#endif
+
+/* MAC80211 */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 18, 0)
+#define rwnx_ops_mgd_prepare_tx(hw, vif, duration) \
+ rwnx_ops_mgd_prepare_tx(hw, vif)
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0)
+
+#define RX_ENC_HT(s) s->flag |= RX_FLAG_HT
+#define RX_ENC_HT_GF(s) s->flag |= (RX_FLAG_HT | RX_FLAG_HT_GF)
+#define RX_ENC_VHT(s) s->flag |= RX_FLAG_HT
+#define RX_ENC_HE(s) s->flag |= RX_FLAG_HT
+#define RX_ENC_FLAG_SHORT_GI(s) s->flag |= RX_FLAG_SHORT_GI
+#define RX_ENC_FLAG_SHORT_PRE(s) s->flag |= RX_FLAG_SHORTPRE
+#define RX_ENC_FLAG_LDPC(s) s->flag |= RX_FLAG_LDPC
+#define RX_BW_40MHZ(s) s->flag |= RX_FLAG_40MHZ
+#define RX_BW_80MHZ(s) s->vht_flag |= RX_VHT_FLAG_80MHZ
+#define RX_BW_160MHZ(s) s->vht_flag |= RX_VHT_FLAG_160MHZ
+#define RX_NSS(s) s->vht_nss
+
+#else
+#define RX_ENC_HT(s) s->encoding = RX_ENC_HT
+#define RX_ENC_HT_GF(s) { s->encoding = RX_ENC_HT; \
+ s->enc_flags |= RX_ENC_FLAG_HT_GF; }
+#define RX_ENC_VHT(s) s->encoding = RX_ENC_VHT
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 19, 0)
+#define RX_ENC_HE(s) s->encoding = RX_ENC_VHT
+#else
+#define RX_ENC_HE(s) s->encoding = RX_ENC_HE
+#endif
+#define RX_ENC_FLAG_SHORT_GI(s) s->enc_flags |= RX_ENC_FLAG_SHORT_GI
+#define RX_ENC_FLAG_SHORT_PRE(s) s->enc_flags |= RX_ENC_FLAG_SHORTPRE
+#define RX_ENC_FLAG_LDPC(s) s->enc_flags |= RX_ENC_FLAG_LDPC
+#define RX_BW_40MHZ(s) s->bw = RATE_INFO_BW_40
+#define RX_BW_80MHZ(s) s->bw = RATE_INFO_BW_80
+#define RX_BW_160MHZ(s) s->bw = RATE_INFO_BW_160
+#define RX_NSS(s) s->nss
+
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0)
+#define ieee80211_cqm_rssi_notify(vif, event, level, gfp) \
+ ieee80211_cqm_rssi_notify(vif, event, gfp)
+#endif
+
+#ifndef CONFIG_VENDOR_RWNX_AMSDUS_TX
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 4, 0))
+#define rwnx_ops_ampdu_action(hw, vif, params) \
+ rwnx_ops_ampdu_action(hw, vif, enum ieee80211_ampdu_mlme_action action, \
+ struct ieee80211_sta *sta, u16 tid, u16 *ssn, u8 buf_size)
+#elif (LINUX_VERSION_CODE < KERNEL_VERSION(4, 6, 0))
+#define rwnx_ops_ampdu_action(hw, vif, params) \
+ rwnx_ops_ampdu_action(hw, vif, enum ieee80211_ampdu_mlme_action action, \
+ struct ieee80211_sta *sta, u16 tid, u16 *ssn, u8 buf_size, \
+ bool amsdu)
+#endif
+#endif /* CONFIG_VENDOR_RWNX_AMSDUS_TX */
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 2, 0)
+#define IEEE80211_HW_SUPPORT_FAST_XMIT 0
+#define ieee80211_hw_check(hw, feat) (hw->flags & IEEE80211_HW_##feat)
+#define ieee80211_hw_set(hw, feat) {hw->flags |= IEEE80211_HW_##feat;}
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 19, 0)
+#define rwnx_ops_sw_scan_start(hw, vif, mac_addr) \
+ rwnx_ops_sw_scan_start(hw)
+#define rwnx_ops_sw_scan_complete(hw, vif) \
+ rwnx_ops_sw_scan_complete(hw)
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0)
+#define rwnx_ops_hw_scan(hw, vif, hw_req) \
+ rwnx_ops_hw_scan(hw, vif, struct cfg80211_scan_request *req)
+#endif
+
+/* NET */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 13, 0)
+#define rwnx_select_queue(dev, skb, sb_dev) \
+ rwnx_select_queue(dev, skb)
+#elif LINUX_VERSION_CODE < KERNEL_VERSION(4, 19, 0)
+#define rwnx_select_queue(dev, skb, sb_dev) \
+ rwnx_select_queue(dev, skb, void *accel_priv, select_queue_fallback_t fallback)
+#elif LINUX_VERSION_CODE < KERNEL_VERSION(5, 2, 0)
+#define rwnx_select_queue(dev, skb, sb_dev) \
+ rwnx_select_queue(dev, skb, sb_dev, select_queue_fallback_t fallback)
+#else
+#define rwnx_select_queue(dev, skb, sb_dev) \
+ rwnx_select_queue(dev, skb, sb_dev)
+#endif
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 16, 0)) && !(defined CONFIG_VENDOR_RWNX)
+#define sk_pacing_shift_update(sk, shift)
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0)
+#define alloc_netdev_mqs(size, name, assign, setup, txqs, rxqs) \
+ alloc_netdev_mqs(size, name, setup, txqs, rxqs)
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0)
+#define NET_NAME_UNKNOWN 0
+#endif
+
+/* TRACE */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 2, 0)
+#define trace_print_symbols_seq ftrace_print_symbols_seq
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0)
+#define trace_seq_buffer_ptr(p) p->buffer + p->len
+#endif
+
+/* TIME */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0)
+#define time64_to_tm(t, o, tm) time_to_tm((time_t)t, o, tm)
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 19, 0)
+#define ktime_get_real_seconds get_seconds
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0)
+typedef __s64 time64_t;
+#endif
+
+#endif /* _RWNX_COMPAT_H_ */
diff --git a/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_debugfs.c b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_debugfs.c
new file mode 100644
index 000000000000..c070aa947e25
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_debugfs.c
@@ -0,0 +1,2251 @@
+/**
+ ******************************************************************************
+ *
+ * @file rwnx_debugfs.c
+ *
+ * @brief Definition of debugfs entries
+ *
+ * Copyright (C) RivieraWaves 2012-2019
+ *
+ ******************************************************************************
+ */
+
+
+#include <linux/kernel.h>
+#include <linux/kmod.h>
+#include <linux/debugfs.h>
+#include <linux/string.h>
+#include <linux/sort.h>
+
+#include "rwnx_debugfs.h"
+#include "rwnx_msg_tx.h"
+#include "rwnx_radar.h"
+#include "rwnx_tx.h"
+
+#ifdef CONFIG_DEBUG_FS
+#ifdef CONFIG_RWNX_FULLMAC
+static ssize_t rwnx_dbgfs_stats_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct rwnx_hw *priv = file->private_data;
+ char *buf;
+ int ret;
+ int i, skipped;
+ ssize_t read;
+ int bufsz = (NX_TXQ_CNT) * 20 + (ARRAY_SIZE(priv->stats.amsdus_rx) + 1) * 40
+ + (ARRAY_SIZE(priv->stats.ampdus_tx) * 30);
+
+ if (*ppos)
+ return 0;
+
+ buf = kmalloc(bufsz, GFP_ATOMIC);
+ if (buf == NULL)
+ return 0;
+
+ ret = scnprintf(buf, bufsz, "TXQs CFM balances ");
+ for (i = 0; i < NX_TXQ_CNT; i++)
+ ret += scnprintf(&buf[ret], bufsz - ret,
+ " [%1d]:%3d", i,
+ priv->stats.cfm_balance[i]);
+
+ ret += scnprintf(&buf[ret], bufsz - ret, "\n");
+
+#ifdef CONFIG_RWNX_SPLIT_TX_BUF
+ ret += scnprintf(&buf[ret], bufsz - ret,
+ "\nAMSDU[len] done failed received\n");
+ for (i = skipped = 0; i < NX_TX_PAYLOAD_MAX; i++) {
+ if (priv->stats.amsdus[i].done) {
+ per = DIV_ROUND_UP((priv->stats.amsdus[i].failed) *
+ 100, priv->stats.amsdus[i].done);
+ } else if (priv->stats.amsdus_rx[i]) {
+ per = 0;
+ } else {
+ per = 0;
+ skipped = 1;
+ continue;
+ }
+ if (skipped) {
+ ret += scnprintf(&buf[ret], bufsz - ret, " ...\n");
+ skipped = 0;
+ }
+
+ ret += scnprintf(&buf[ret], bufsz - ret,
+ " [%2d] %10d %8d(%3d%%) %10d\n", i ? i + 1 : i,
+ priv->stats.amsdus[i].done,
+ priv->stats.amsdus[i].failed, per,
+ priv->stats.amsdus_rx[i]);
+ }
+
+ for (; i < ARRAY_SIZE(priv->stats.amsdus_rx); i++) {
+ if (!priv->stats.amsdus_rx[i]) {
+ skipped = 1;
+ continue;
+ }
+ if (skipped) {
+ ret += scnprintf(&buf[ret], bufsz - ret, " ...\n");
+ skipped = 0;
+ }
+
+ ret += scnprintf(&buf[ret], bufsz - ret,
+ " [%2d] %10d\n",
+ i + 1, priv->stats.amsdus_rx[i]);
+ }
+#else
+ ret += scnprintf(&buf[ret], bufsz - ret,
+ "\nAMSDU[len] received\n");
+ for (i = skipped = 0; i < ARRAY_SIZE(priv->stats.amsdus_rx); i++) {
+ if (!priv->stats.amsdus_rx[i]) {
+ skipped = 1;
+ continue;
+ }
+ if (skipped) {
+ ret += scnprintf(&buf[ret], bufsz - ret,
+ " ...\n");
+ skipped = 0;
+ }
+
+ ret += scnprintf(&buf[ret], bufsz - ret,
+ " [%2d] %10d\n",
+ i + 1, priv->stats.amsdus_rx[i]);
+ }
+
+#endif /* CONFIG_RWNX_SPLIT_TX_BUF */
+
+ ret += scnprintf(&buf[ret], bufsz - ret,
+ "\nAMPDU[len] done received\n");
+ for (i = skipped = 0; i < ARRAY_SIZE(priv->stats.ampdus_tx); i++) {
+ if (!priv->stats.ampdus_tx[i] && !priv->stats.ampdus_rx[i]) {
+ skipped = 1;
+ continue;
+ }
+ if (skipped) {
+ ret += scnprintf(&buf[ret], bufsz - ret,
+ " ...\n");
+ skipped = 0;
+ }
+
+ ret += scnprintf(&buf[ret], bufsz - ret,
+ " [%2d] %9d %9d\n", i ? i + 1 : i,
+ priv->stats.ampdus_tx[i], priv->stats.ampdus_rx[i]);
+ }
+
+ ret += scnprintf(&buf[ret], bufsz - ret,
+ "#mpdu missed %9d\n",
+ priv->stats.ampdus_rx_miss);
+ read = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
+
+ kfree(buf);
+
+ return read;
+}
+#endif /* CONFIG_RWNX_FULLMAC */
+
+static ssize_t rwnx_dbgfs_stats_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct rwnx_hw *priv = file->private_data;
+
+ /* Prevent from interrupt preemption as these statistics are updated under
+ * interrupt */
+ spin_lock_bh(&priv->tx_lock);
+
+ memset(&priv->stats, 0, sizeof(priv->stats));
+
+ spin_unlock_bh(&priv->tx_lock);
+
+ return count;
+}
+
+DEBUGFS_READ_WRITE_FILE_OPS(stats);
+
+#define TXQ_STA_PREF "tid|"
+#define TXQ_STA_PREF_FMT "%3d|"
+
+#ifdef CONFIG_RWNX_FULLMAC
+#define TXQ_VIF_PREF "type|"
+#define TXQ_VIF_PREF_FMT "%4s|"
+#else
+#define TXQ_VIF_PREF "AC|"
+#define TXQ_VIF_PREF_FMT "%2s|"
+#endif /* CONFIG_RWNX_FULLMAC */
+
+#define TXQ_HDR "idx| status|credit|ready|retry"
+#define TXQ_HDR_FMT "%3d|%s%s%s%s%s%s%s|%6d|%5d|%5d"
+
+#ifdef CONFIG_RWNX_AMSDUS_TX
+#ifdef CONFIG_RWNX_FULLMAC
+#define TXQ_HDR_SUFF "|amsdu"
+#define TXQ_HDR_SUFF_FMT "|%5d"
+#else
+#define TXQ_HDR_SUFF "|amsdu-ht|amdsu-vht"
+#define TXQ_HDR_SUFF_FMT "|%8d|%9d"
+#endif /* CONFIG_RWNX_FULLMAC */
+#else
+#define TXQ_HDR_SUFF ""
+#define TXQ_HDR_SUF_FMT ""
+#endif /* CONFIG_RWNX_AMSDUS_TX */
+
+#define TXQ_HDR_MAX_LEN (sizeof(TXQ_STA_PREF) + sizeof(TXQ_HDR) + sizeof(TXQ_HDR_SUFF) + 1)
+
+#ifdef CONFIG_RWNX_FULLMAC
+#define PS_HDR "Legacy PS: ready=%d, sp=%d / UAPSD: ready=%d, sp=%d"
+#define PS_HDR_LEGACY "Legacy PS: ready=%d, sp=%d"
+#define PS_HDR_UAPSD "UAPSD: ready=%d, sp=%d"
+#define PS_HDR_MAX_LEN sizeof("Legacy PS: ready=xxx, sp=xxx / UAPSD: ready=xxx, sp=xxx\n")
+#else
+#define PS_HDR ""
+#define PS_HDR_MAX_LEN 0
+#endif /* CONFIG_RWNX_FULLMAC */
+
+#define STA_HDR "** STA %d (%pM)\n"
+#define STA_HDR_MAX_LEN sizeof("- STA xx (xx:xx:xx:xx:xx:xx)\n") + PS_HDR_MAX_LEN
+
+#ifdef CONFIG_RWNX_FULLMAC
+#define VIF_HDR "* VIF [%d] %s\n"
+#define VIF_HDR_MAX_LEN sizeof(VIF_HDR) + IFNAMSIZ
+#else
+#define VIF_HDR "* VIF [%d]\n"
+#define VIF_HDR_MAX_LEN sizeof(VIF_HDR)
+#endif
+
+
+#ifdef CONFIG_RWNX_AMSDUS_TX
+
+#ifdef CONFIG_RWNX_FULLMAC
+#define VIF_SEP "---------------------------------------\n"
+#else
+#define VIF_SEP "----------------------------------------------------\n"
+#endif /* CONFIG_RWNX_FULLMAC */
+
+#else /* ! CONFIG_RWNX_AMSDUS_TX */
+#define VIF_SEP "---------------------------------\n"
+#endif /* CONFIG_RWNX_AMSDUS_TX*/
+
+#define VIF_SEP_LEN sizeof(VIF_SEP)
+
+#define CAPTION "status: L=in hwq list, F=stop full, P=stop sta PS, V=stop vif PS, C=stop channel, S=stop CSA, M=stop MU"
+#define CAPTION_LEN sizeof(CAPTION)
+
+#define STA_TXQ 0
+#define VIF_TXQ 1
+
+static int rwnx_dbgfs_txq(char *buf, size_t size, struct rwnx_txq *txq, int type, int tid, char *name)
+{
+ int res, idx = 0;
+
+ if (type == STA_TXQ) {
+ res = scnprintf(&buf[idx], size, TXQ_STA_PREF_FMT, tid);
+ idx += res;
+ size -= res;
+ } else {
+ res = scnprintf(&buf[idx], size, TXQ_VIF_PREF_FMT, name);
+ idx += res;
+ size -= res;
+ }
+
+ res = scnprintf(&buf[idx], size, TXQ_HDR_FMT, txq->idx,
+ (txq->status & RWNX_TXQ_IN_HWQ_LIST) ? "L" : " ",
+ (txq->status & RWNX_TXQ_STOP_FULL) ? "F" : " ",
+ (txq->status & RWNX_TXQ_STOP_STA_PS) ? "P" : " ",
+ (txq->status & RWNX_TXQ_STOP_VIF_PS) ? "V" : " ",
+ (txq->status & RWNX_TXQ_STOP_CHAN) ? "C" : " ",
+ (txq->status & RWNX_TXQ_STOP_CSA) ? "S" : " ",
+ (txq->status & RWNX_TXQ_STOP_MU_POS) ? "M" : " ",
+ txq->credits, skb_queue_len(&txq->sk_list),
+ txq->nb_retry);
+ idx += res;
+ size -= res;
+
+#ifdef CONFIG_RWNX_AMSDUS_TX
+ if (type == STA_TXQ) {
+ res = scnprintf(&buf[idx], size, TXQ_HDR_SUFF_FMT,
+#ifdef CONFIG_RWNX_FULLMAC
+ txq->amsdu_len
+#else
+ txq->amsdu_ht_len_cap, txq->amsdu_vht_len_cap
+#endif /* CONFIG_RWNX_FULLMAC */
+ );
+ idx += res;
+ size -= res;
+ }
+#endif
+
+ res = scnprintf(&buf[idx], size, "\n");
+ idx += res;
+ size -= res;
+
+ return idx;
+}
+
+static int rwnx_dbgfs_txq_sta(char *buf, size_t size, struct rwnx_sta *rwnx_sta,
+ struct rwnx_hw *rwnx_hw)
+{
+ int tid, res, idx = 0;
+ struct rwnx_txq *txq;
+
+ res = scnprintf(&buf[idx], size, "\n" STA_HDR,
+ rwnx_sta->sta_idx,
+#ifdef CONFIG_RWNX_FULLMAC
+ rwnx_sta->mac_addr
+#endif /* CONFIG_RWNX_FULLMAC */
+ );
+ idx += res;
+ size -= res;
+
+#ifdef CONFIG_RWNX_FULLMAC
+ if (rwnx_sta->ps.active) {
+ if (rwnx_sta->uapsd_tids &&
+ (rwnx_sta->uapsd_tids == ((1 << NX_NB_TXQ_PER_STA) - 1)))
+ res = scnprintf(&buf[idx], size, PS_HDR_UAPSD "\n",
+ rwnx_sta->ps.pkt_ready[UAPSD_ID],
+ rwnx_sta->ps.sp_cnt[UAPSD_ID]);
+ else if (rwnx_sta->uapsd_tids)
+ res = scnprintf(&buf[idx], size, PS_HDR "\n",
+ rwnx_sta->ps.pkt_ready[LEGACY_PS_ID],
+ rwnx_sta->ps.sp_cnt[LEGACY_PS_ID],
+ rwnx_sta->ps.pkt_ready[UAPSD_ID],
+ rwnx_sta->ps.sp_cnt[UAPSD_ID]);
+ else
+ res = scnprintf(&buf[idx], size, PS_HDR_LEGACY "\n",
+ rwnx_sta->ps.pkt_ready[LEGACY_PS_ID],
+ rwnx_sta->ps.sp_cnt[LEGACY_PS_ID]);
+ idx += res;
+ size -= res;
+ } else {
+ res = scnprintf(&buf[idx], size, "\n");
+ idx += res;
+ size -= res;
+ }
+#endif /* CONFIG_RWNX_FULLMAC */
+
+
+ res = scnprintf(&buf[idx], size, TXQ_STA_PREF TXQ_HDR TXQ_HDR_SUFF "\n");
+ idx += res;
+ size -= res;
+
+
+ foreach_sta_txq(rwnx_sta, txq, tid, rwnx_hw) {
+ res = rwnx_dbgfs_txq(&buf[idx], size, txq, STA_TXQ, tid, NULL);
+ idx += res;
+ size -= res;
+ }
+
+ return idx;
+}
+
+static int rwnx_dbgfs_txq_vif(char *buf, size_t size, struct rwnx_vif *rwnx_vif,
+ struct rwnx_hw *rwnx_hw)
+{
+ int res, idx = 0;
+ struct rwnx_txq *txq;
+ struct rwnx_sta *rwnx_sta;
+
+#ifdef CONFIG_RWNX_FULLMAC
+ res = scnprintf(&buf[idx], size, VIF_HDR, rwnx_vif->vif_index, rwnx_vif->ndev->name);
+ idx += res;
+ size -= res;
+ if (!rwnx_vif->up || rwnx_vif->ndev == NULL)
+ return idx;
+
+#else
+ int ac;
+ char ac_name[2] = {'0', '\0'};
+
+ res = scnprintf(&buf[idx], size, VIF_HDR, rwnx_vif->vif_index);
+ idx += res;
+ size -= res;
+#endif /* CONFIG_RWNX_FULLMAC */
+
+#ifdef CONFIG_RWNX_FULLMAC
+ if (RWNX_VIF_TYPE(rwnx_vif) == NL80211_IFTYPE_AP ||
+ RWNX_VIF_TYPE(rwnx_vif) == NL80211_IFTYPE_P2P_GO ||
+ RWNX_VIF_TYPE(rwnx_vif) == NL80211_IFTYPE_MESH_POINT) {
+ res = scnprintf(&buf[idx], size, TXQ_VIF_PREF TXQ_HDR "\n");
+ idx += res;
+ size -= res;
+ txq = rwnx_txq_vif_get(rwnx_vif, NX_UNK_TXQ_TYPE);
+ res = rwnx_dbgfs_txq(&buf[idx], size, txq, VIF_TXQ, 0, "UNK");
+ idx += res;
+ size -= res;
+ txq = rwnx_txq_vif_get(rwnx_vif, NX_BCMC_TXQ_TYPE);
+ res = rwnx_dbgfs_txq(&buf[idx], size, txq, VIF_TXQ, 0, "BCMC");
+ idx += res;
+ size -= res;
+ rwnx_sta = &rwnx_hw->sta_table[rwnx_vif->ap.bcmc_index];
+ if (rwnx_sta->ps.active) {
+ res = scnprintf(&buf[idx], size, PS_HDR_LEGACY "\n",
+ rwnx_sta->ps.sp_cnt[LEGACY_PS_ID],
+ rwnx_sta->ps.sp_cnt[LEGACY_PS_ID]);
+ idx += res;
+ size -= res;
+ } else {
+ res = scnprintf(&buf[idx], size, "\n");
+ idx += res;
+ size -= res;
+ }
+
+ list_for_each_entry(rwnx_sta, &rwnx_vif->ap.sta_list, list) {
+ res = rwnx_dbgfs_txq_sta(&buf[idx], size, rwnx_sta, rwnx_hw);
+ idx += res;
+ size -= res;
+ }
+ } else if (RWNX_VIF_TYPE(rwnx_vif) == NL80211_IFTYPE_STATION ||
+ RWNX_VIF_TYPE(rwnx_vif) == NL80211_IFTYPE_P2P_CLIENT) {
+ if (rwnx_vif->sta.ap) {
+ res = rwnx_dbgfs_txq_sta(&buf[idx], size, rwnx_vif->sta.ap, rwnx_hw);
+ idx += res;
+ size -= res;
+ }
+ }
+
+#else
+ res = scnprintf(&buf[idx], size, TXQ_VIF_PREF TXQ_HDR "\n");
+ idx += res;
+ size -= res;
+
+ foreach_vif_txq(rwnx_vif, txq, ac) {
+ ac_name[0]++;
+ res = rwnx_dbgfs_txq(&buf[idx], size, txq, VIF_TXQ, 0, ac_name);
+ idx += res;
+ size -= res;
+ }
+
+ list_for_each_entry(rwnx_sta, &rwnx_vif->stations, list) {
+ res = rwnx_dbgfs_txq_sta(&buf[idx], size, rwnx_sta, rwnx_hw);
+ idx += res;
+ size -= res;
+ }
+#endif /* CONFIG_RWNX_FULLMAC */
+ return idx;
+}
+
+static ssize_t rwnx_dbgfs_txq_read(struct file *file ,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct rwnx_hw *rwnx_hw = file->private_data;
+ struct rwnx_vif *vif;
+ char *buf;
+ int idx, res;
+ ssize_t read;
+ size_t bufsz = ((NX_VIRT_DEV_MAX * (VIF_HDR_MAX_LEN + 2 * VIF_SEP_LEN)) +
+ (NX_REMOTE_STA_MAX * STA_HDR_MAX_LEN) +
+ ((NX_REMOTE_STA_MAX + NX_VIRT_DEV_MAX + NX_NB_TXQ) *
+ TXQ_HDR_MAX_LEN) + CAPTION_LEN);
+
+ /* everything is read in one go */
+ if (*ppos)
+ return 0;
+
+ bufsz = min_t(size_t, bufsz, count);
+
+ buf = kmalloc(bufsz, GFP_ATOMIC);
+ if (buf == NULL)
+ return 0;
+
+ bufsz--;
+ idx = 0;
+
+ res = scnprintf(&buf[idx], bufsz, CAPTION);
+ idx += res;
+ bufsz -= res;
+
+ //spin_lock_bh(&rwnx_hw->tx_lock);
+ list_for_each_entry(vif, &rwnx_hw->vifs, list) {
+ res = scnprintf(&buf[idx], bufsz, "\n"VIF_SEP);
+ idx += res;
+ bufsz -= res;
+ res = rwnx_dbgfs_txq_vif(&buf[idx], bufsz, vif, rwnx_hw);
+ idx += res;
+ bufsz -= res;
+ res = scnprintf(&buf[idx], bufsz, VIF_SEP);
+ idx += res;
+ bufsz -= res;
+ }
+ //spin_unlock_bh(&rwnx_hw->tx_lock);
+
+ read = simple_read_from_buffer(user_buf, count, ppos, buf, idx);
+ kfree(buf);
+
+ return read;
+}
+DEBUGFS_READ_FILE_OPS(txq);
+
+static ssize_t rwnx_dbgfs_acsinfo_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct rwnx_hw *priv = file->private_data;
+ #ifdef CONFIG_RWNX_FULLMAC
+ struct wiphy *wiphy = priv->wiphy;
+ #endif //CONFIG_RWNX_FULLMAC
+ //char buf[(SCAN_CHANNEL_MAX + 1) * 43];
+ char *buf = NULL;
+ ssize_t size = 0;
+ int survey_cnt = 0;
+ int len = 0;
+ int band, chan_cnt;
+ int band_max = NL80211_BAND_5GHZ;
+
+ buf = (char*)kmalloc(sizeof(char) * ((SCAN_CHANNEL_MAX + 1) * 43), GFP_KERNEL);
+ memset(buf, 0, ((SCAN_CHANNEL_MAX + 1) * 43));
+
+ if (priv->band_5g_support){
+ band_max = NL80211_BAND_5GHZ + 1;
+ }
+
+ mutex_lock(&priv->dbgdump_elem.mutex);
+
+ len += scnprintf(buf, min_t(size_t, sizeof(buf) - 1, count),
+ "FREQ TIME(ms) BUSY(ms) NOISE(dBm)\n");
+
+
+ //#ifdef USE_5G
+ //for (band = NL80211_BAND_2GHZ; band <= NL80211_BAND_5GHZ; band++) {
+ //#else
+ //for (band = NL80211_BAND_2GHZ; band < NL80211_BAND_5GHZ; band++) {
+ //#endif
+ for (band = NL80211_BAND_2GHZ; band < band_max; band++) {
+ for (chan_cnt = 0; chan_cnt < wiphy->bands[band]->n_channels; chan_cnt++) {
+ struct rwnx_survey_info *p_survey_info = &priv->survey[survey_cnt];
+ struct ieee80211_channel *p_chan = &wiphy->bands[band]->channels[chan_cnt];
+
+ if (p_survey_info->filled) {
+ len += scnprintf(&buf[len], min_t(size_t, sizeof(buf) - len - 1, count),
+ "%d %03d %03d %d\n",
+ p_chan->center_freq,
+ p_survey_info->chan_time_ms,
+ p_survey_info->chan_time_busy_ms,
+ p_survey_info->noise_dbm);
+ } else {
+ len += scnprintf(&buf[len], min_t(size_t, sizeof(buf) -len -1, count),
+ "%d NOT AVAILABLE\n",
+ p_chan->center_freq);
+ }
+
+ survey_cnt++;
+ }
+ }
+
+ mutex_unlock(&priv->dbgdump_elem.mutex);
+
+ size = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+
+ kfree(buf);
+ buf = NULL;
+
+ return size;
+}
+
+DEBUGFS_READ_FILE_OPS(acsinfo);
+
+static ssize_t rwnx_dbgfs_fw_dbg_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ char help[]="usage: [MOD:<ALL|KE|DBG|IPC|DMA|MM|TX|RX|PHY>]* "
+ "[DBG:<NONE|CRT|ERR|WRN|INF|VRB>]\n";
+
+ return simple_read_from_buffer(user_buf, count, ppos, help, sizeof(help));
+}
+
+
+static ssize_t rwnx_dbgfs_fw_dbg_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct rwnx_hw *priv = file->private_data;
+ char buf[32];
+ int idx = 0;
+ u32 mod = 0;
+ size_t len = min_t(size_t, count, sizeof(buf) - 1);
+
+ if (copy_from_user(buf, user_buf, len))
+ return -EFAULT;
+ buf[len] = '\0';
+
+#define RWNX_MOD_TOKEN(str, val) \
+ if (strncmp(&buf[idx], str, sizeof(str) - 1 ) == 0) { \
+ idx += sizeof(str) - 1; \
+ mod |= val; \
+ continue; \
+ }
+
+#define RWNX_DBG_TOKEN(str, val) \
+ if (strncmp(&buf[idx], str, sizeof(str) - 1) == 0) { \
+ idx += sizeof(str) - 1; \
+ dbg = val; \
+ goto dbg_done; \
+ }
+
+ while ((idx + 4) < len) {
+ if (strncmp(&buf[idx], "MOD:", 4) == 0) {
+ idx += 4;
+ RWNX_MOD_TOKEN("ALL", 0xffffffff);
+ RWNX_MOD_TOKEN("KE", BIT(0));
+ RWNX_MOD_TOKEN("DBG", BIT(1));
+ RWNX_MOD_TOKEN("IPC", BIT(2));
+ RWNX_MOD_TOKEN("DMA", BIT(3));
+ RWNX_MOD_TOKEN("MM", BIT(4));
+ RWNX_MOD_TOKEN("TX", BIT(5));
+ RWNX_MOD_TOKEN("RX", BIT(6));
+ RWNX_MOD_TOKEN("PHY", BIT(7));
+ idx++;
+ } else if (strncmp(&buf[idx], "DBG:", 4) == 0) {
+ u32 dbg = 0;
+ idx += 4;
+ RWNX_DBG_TOKEN("NONE", 0);
+ RWNX_DBG_TOKEN("CRT", 1);
+ RWNX_DBG_TOKEN("ERR", 2);
+ RWNX_DBG_TOKEN("WRN", 3);
+ RWNX_DBG_TOKEN("INF", 4);
+ RWNX_DBG_TOKEN("VRB", 5);
+ idx++;
+ continue;
+ dbg_done:
+ rwnx_send_dbg_set_sev_filter_req(priv, dbg);
+ } else {
+ idx++;
+ }
+ }
+
+ if (mod) {
+ rwnx_send_dbg_set_mod_filter_req(priv, mod);
+ }
+
+ return count;
+}
+
+DEBUGFS_READ_WRITE_FILE_OPS(fw_dbg);
+
+static ssize_t rwnx_dbgfs_sys_stats_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct rwnx_hw *priv = file->private_data;
+ char buf[3*64];
+ int len = 0;
+ ssize_t read;
+ int error = 0;
+ struct dbg_get_sys_stat_cfm cfm;
+ u32 sleep_int, sleep_frac, doze_int, doze_frac;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ /* Get the information from the FW */
+ if ((error = rwnx_send_dbg_get_sys_stat_req(priv, &cfm)))
+ return error;
+
+ if (cfm.stats_time == 0)
+ return 0;
+
+ sleep_int = ((cfm.cpu_sleep_time * 100) / cfm.stats_time);
+ sleep_frac = (((cfm.cpu_sleep_time * 100) % cfm.stats_time) * 10) / cfm.stats_time;
+ doze_int = ((cfm.doze_time * 100) / cfm.stats_time);
+ doze_frac = (((cfm.doze_time * 100) % cfm.stats_time) * 10) / cfm.stats_time;
+
+ len += scnprintf(buf, min_t(size_t, sizeof(buf) - 1, count),
+ "\nSystem statistics:\n");
+ len += scnprintf(&buf[len], min_t(size_t, sizeof(buf) - 1, count),
+ " CPU sleep [%%]: %d.%d\n", sleep_int, sleep_frac);
+ len += scnprintf(&buf[len], min_t(size_t, sizeof(buf) - 1, count),
+ " Doze [%%]: %d.%d\n", doze_int, doze_frac);
+
+ read = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+
+ return read;
+}
+
+DEBUGFS_READ_FILE_OPS(sys_stats);
+
+#ifdef CONFIG_RWNX_MUMIMO_TX
+static ssize_t rwnx_dbgfs_mu_group_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct rwnx_hw *rwnx_hw = file->private_data;
+ struct rwnx_mu_info *mu = &rwnx_hw->mu;
+ struct rwnx_mu_group *group;
+ size_t bufsz = NX_MU_GROUP_MAX * sizeof("xx = (xx - xx - xx - xx)\n") + 50;
+ char *buf;
+ int j, res, idx = 0;
+
+ if (*ppos)
+ return 0;
+
+ buf = kmalloc(bufsz, GFP_ATOMIC);
+ if (buf == NULL)
+ return 0;
+
+ res = scnprintf(&buf[idx], bufsz, "MU Group list (%d groups, %d users max)\n",
+ NX_MU_GROUP_MAX, CONFIG_USER_MAX);
+ idx += res;
+ bufsz -= res;
+
+ list_for_each_entry(group, &mu->active_groups, list) {
+ if (group->user_cnt) {
+ res = scnprintf(&buf[idx], bufsz, "%2d = (", group->group_id);
+ idx += res;
+ bufsz -= res;
+ for (j = 0; j < (CONFIG_USER_MAX - 1) ; j++) {
+ if (group->users[j])
+ res = scnprintf(&buf[idx], bufsz, "%2d - ",
+ group->users[j]->sta_idx);
+ else
+ res = scnprintf(&buf[idx], bufsz, ".. - ");
+
+ idx += res;
+ bufsz -= res;
+ }
+
+ if (group->users[j])
+ res = scnprintf(&buf[idx], bufsz, "%2d)\n",
+ group->users[j]->sta_idx);
+ else
+ res = scnprintf(&buf[idx], bufsz, "..)\n");
+
+ idx += res;
+ bufsz -= res;
+ }
+ }
+
+ res = simple_read_from_buffer(user_buf, count, ppos, buf, idx);
+ kfree(buf);
+
+ return res;
+}
+
+DEBUGFS_READ_FILE_OPS(mu_group);
+#endif
+
+#ifdef CONFIG_RWNX_P2P_DEBUGFS
+static ssize_t rwnx_dbgfs_oppps_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct rwnx_hw *rw_hw = file->private_data;
+ struct rwnx_vif *rw_vif;
+ char buf[32];
+ size_t len = min_t(size_t, count, sizeof(buf) - 1);
+ int ctw;
+
+ if (copy_from_user(buf, user_buf, len))
+ return -EFAULT;
+ buf[len] = '\0';
+
+ /* Read the written CT Window (provided in ms) value */
+ if (sscanf(buf, "ctw=%d", &ctw) > 0) {
+ /* Check if at least one VIF is configured as P2P GO */
+ list_for_each_entry(rw_vif, &rw_hw->vifs, list) {
+#ifdef CONFIG_RWNX_FULLMAC
+ if (RWNX_VIF_TYPE(rw_vif) == NL80211_IFTYPE_P2P_GO) {
+#endif /* CONFIG_RWNX_FULLMAC */
+ struct mm_set_p2p_oppps_cfm cfm;
+
+ /* Forward request to the embedded and wait for confirmation */
+ rwnx_send_p2p_oppps_req(rw_hw, rw_vif, (u8)ctw, &cfm);
+
+ break;
+ }
+ }
+ }
+
+ return count;
+}
+
+DEBUGFS_WRITE_FILE_OPS(oppps);
+
+static ssize_t rwnx_dbgfs_noa_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct rwnx_hw *rw_hw = file->private_data;
+ struct rwnx_vif *rw_vif;
+ char buf[64];
+ size_t len = min_t(size_t, count, sizeof(buf) - 1);
+ int noa_count, interval, duration, dyn_noa;
+
+ if (copy_from_user(buf, user_buf, len))
+ return -EFAULT;
+ buf[len] = '\0';
+
+ /* Read the written NOA information */
+ if (sscanf(buf, "count=%d interval=%d duration=%d dyn=%d",
+ &noa_count, &interval, &duration, &dyn_noa) > 0) {
+ /* Check if at least one VIF is configured as P2P GO */
+ list_for_each_entry(rw_vif, &rw_hw->vifs, list) {
+#ifdef CONFIG_RWNX_FULLMAC
+ if (RWNX_VIF_TYPE(rw_vif) == NL80211_IFTYPE_P2P_GO) {
+#endif /* CONFIG_RWNX_FULLMAC */
+ struct mm_set_p2p_noa_cfm cfm;
+
+ /* Forward request to the embedded and wait for confirmation */
+ rwnx_send_p2p_noa_req(rw_hw, rw_vif, noa_count, interval,
+ duration, (dyn_noa > 0), &cfm);
+
+ break;
+ }
+ }
+ }
+
+ return count;
+}
+
+DEBUGFS_WRITE_FILE_OPS(noa);
+#endif /* CONFIG_RWNX_P2P_DEBUGFS */
+
+static char fw_log_buffer[FW_LOG_SIZE];
+
+static ssize_t rwnx_dbgfs_fw_log_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct rwnx_hw *priv = file->private_data;
+ size_t not_cpy;
+ size_t nb_cpy;
+ char *log = fw_log_buffer;
+
+ printk("%s, %d, %p, %p\n", __func__, priv->debugfs.fw_log.buf.size, priv->debugfs.fw_log.buf.start, priv->debugfs.fw_log.buf.dataend);
+ //spin_lock_bh(&priv->debugfs.fw_log.lock);
+
+ if ((priv->debugfs.fw_log.buf.start + priv->debugfs.fw_log.buf.size) >= priv->debugfs.fw_log.buf.dataend) {
+ memcpy(log, priv->debugfs.fw_log.buf.start, priv->debugfs.fw_log.buf.dataend - priv->debugfs.fw_log.buf.start);
+ not_cpy = copy_to_user(user_buf, log, priv->debugfs.fw_log.buf.dataend - priv->debugfs.fw_log.buf.start);
+ nb_cpy = priv->debugfs.fw_log.buf.dataend - priv->debugfs.fw_log.buf.start - not_cpy;
+ priv->debugfs.fw_log.buf.start = priv->debugfs.fw_log.buf.data;
+ } else {
+ memcpy(log, priv->debugfs.fw_log.buf.start, priv->debugfs.fw_log.buf.size);
+ not_cpy = copy_to_user(user_buf, log, priv->debugfs.fw_log.buf.size);
+ nb_cpy = priv->debugfs.fw_log.buf.size - not_cpy;
+ priv->debugfs.fw_log.buf.start = priv->debugfs.fw_log.buf.start + priv->debugfs.fw_log.buf.size - not_cpy;
+ }
+
+ priv->debugfs.fw_log.buf.size -= nb_cpy;
+ //spin_unlock_bh(&priv->debugfs.fw_log.lock);
+
+ printk("nb_cpy=%lu, not_cpy=%lu, start=%p, end=%p\n", (long unsigned int)nb_cpy, (long unsigned int)not_cpy, priv->debugfs.fw_log.buf.start, priv->debugfs.fw_log.buf.end);
+ return nb_cpy;
+}
+
+static ssize_t rwnx_dbgfs_fw_log_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ //struct rwnx_hw *priv = file->private_data;
+
+ printk("%s\n", __func__);
+ return count;
+}
+DEBUGFS_READ_WRITE_FILE_OPS(fw_log);
+
+#ifdef CONFIG_RWNX_RADAR
+static ssize_t rwnx_dbgfs_pulses_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos,
+ int rd_idx)
+{
+ struct rwnx_hw *priv = file->private_data;
+ char *buf;
+ int len = 0;
+ int bufsz;
+ int i;
+ int index;
+ struct rwnx_radar_pulses *p = &priv->radar.pulses[rd_idx];
+ ssize_t read;
+
+ if (*ppos != 0)
+ return 0;
+
+ /* Prevent from interrupt preemption */
+ spin_lock_bh(&priv->radar.lock);
+ bufsz = p->count * 34 + 51;
+ bufsz += rwnx_radar_dump_pattern_detector(NULL, 0, &priv->radar, rd_idx);
+ buf = kmalloc(bufsz, GFP_ATOMIC);
+ if (buf == NULL) {
+ spin_unlock_bh(&priv->radar.lock);
+ return 0;
+ }
+
+ if (p->count) {
+ len += scnprintf(&buf[len], bufsz - len,
+ " PRI WIDTH FOM FREQ\n");
+ index = p->index;
+ for (i = 0; i < p->count; i++) {
+ struct radar_pulse *pulse;
+
+ if (index > 0)
+ index--;
+ else
+ index = RWNX_RADAR_PULSE_MAX - 1;
+
+ pulse = (struct radar_pulse *) &p->buffer[index];
+
+ len += scnprintf(&buf[len], bufsz - len,
+ "%05dus %03dus %2d%% %+3dMHz\n", pulse->rep,
+ 2 * pulse->len, 6 * pulse->fom, 2*pulse->freq);
+ }
+ }
+
+ len += rwnx_radar_dump_pattern_detector(&buf[len], bufsz - len,
+ &priv->radar, rd_idx);
+
+ spin_unlock_bh(&priv->radar.lock);
+
+ read = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+
+ kfree(buf);
+
+ return read;
+}
+
+static ssize_t rwnx_dbgfs_pulses_prim_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ return rwnx_dbgfs_pulses_read(file, user_buf, count, ppos, 0);
+}
+
+DEBUGFS_READ_FILE_OPS(pulses_prim);
+
+static ssize_t rwnx_dbgfs_pulses_sec_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ return rwnx_dbgfs_pulses_read(file, user_buf, count, ppos, 1);
+}
+
+DEBUGFS_READ_FILE_OPS(pulses_sec);
+
+static ssize_t rwnx_dbgfs_detected_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct rwnx_hw *priv = file->private_data;
+ char *buf;
+ int bufsz,len = 0;
+ ssize_t read;
+
+ if (*ppos != 0)
+ return 0;
+
+ bufsz = 5; // RIU:\n
+ bufsz += rwnx_radar_dump_radar_detected(NULL, 0, &priv->radar,
+ RWNX_RADAR_RIU);
+
+ if (priv->phy.cnt > 1) {
+ bufsz += 5; // FCU:\n
+ bufsz += rwnx_radar_dump_radar_detected(NULL, 0, &priv->radar,
+ RWNX_RADAR_FCU);
+ }
+
+ buf = kmalloc(bufsz, GFP_KERNEL);
+ if (buf == NULL) {
+ return 0;
+ }
+
+ len = scnprintf(&buf[len], bufsz, "RIU:\n");
+ len += rwnx_radar_dump_radar_detected(&buf[len], bufsz - len, &priv->radar,
+ RWNX_RADAR_RIU);
+
+ if (priv->phy.cnt > 1) {
+ len += scnprintf(&buf[len], bufsz - len, "FCU:\n");
+ len += rwnx_radar_dump_radar_detected(&buf[len], bufsz - len,
+ &priv->radar, RWNX_RADAR_FCU);
+ }
+
+ read = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+
+ kfree(buf);
+
+ return read;
+}
+
+DEBUGFS_READ_FILE_OPS(detected);
+
+static ssize_t rwnx_dbgfs_enable_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct rwnx_hw *priv = file->private_data;
+ char buf[32];
+ int ret;
+ ssize_t read;
+
+ ret = scnprintf(buf, min_t(size_t, sizeof(buf) - 1, count),
+ "RIU=%d FCU=%d\n", priv->radar.dpd[RWNX_RADAR_RIU]->enabled,
+ priv->radar.dpd[RWNX_RADAR_FCU]->enabled);
+
+ read = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
+
+ return read;
+}
+
+static ssize_t rwnx_dbgfs_enable_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct rwnx_hw *priv = file->private_data;
+ char buf[32];
+ int val;
+ size_t len = min_t(size_t, count, sizeof(buf) - 1);
+
+ if (copy_from_user(buf, user_buf, len))
+ return -EFAULT;
+
+ buf[len] = '\0';
+
+ if (sscanf(buf, "RIU=%d", &val) > 0)
+ rwnx_radar_detection_enable(&priv->radar, val, RWNX_RADAR_RIU);
+
+ if (sscanf(buf, "FCU=%d", &val) > 0)
+ rwnx_radar_detection_enable(&priv->radar, val, RWNX_RADAR_FCU);
+
+ return count;
+}
+
+DEBUGFS_READ_WRITE_FILE_OPS(enable);
+
+static ssize_t rwnx_dbgfs_band_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct rwnx_hw *priv = file->private_data;
+ char buf[32];
+ int ret;
+ ssize_t read;
+
+ ret = scnprintf(buf, min_t(size_t, sizeof(buf) - 1, count),
+ "BAND=%d\n", priv->phy.sec_chan.band);
+
+ read = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
+
+ return read;
+}
+
+static ssize_t rwnx_dbgfs_band_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct rwnx_hw *priv = file->private_data;
+ char buf[32];
+ int val;
+ size_t len = min_t(size_t, count, sizeof(buf) - 1);
+ int band_max = NL80211_BAND_5GHZ;
+
+ if (priv->band_5g_support){
+ band_max = NL80211_BAND_5GHZ + 1;
+ }
+
+ if (copy_from_user(buf, user_buf, len))
+ return -EFAULT;
+
+ buf[len] = '\0';
+
+// #ifdef USE_5G
+// if ((sscanf(buf, "%d", &val) > 0) && (val >= 0) && (val <= NL80211_BAND_5GHZ))
+// #else
+// if ((sscanf(buf, "%d", &val) > 0) && (val >= 0) && (val < NL80211_BAND_5GHZ))
+// #endif
+ if ((sscanf(buf, "%d", &val) > 0) && (val >= 0) && (val < band_max)){
+ priv->phy.sec_chan.band = val;
+ }
+
+ return count;
+}
+
+DEBUGFS_READ_WRITE_FILE_OPS(band);
+
+static ssize_t rwnx_dbgfs_type_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct rwnx_hw *priv = file->private_data;
+ char buf[32];
+ int ret;
+ ssize_t read;
+
+ ret = scnprintf(buf, min_t(size_t, sizeof(buf) - 1, count),
+ "TYPE=%d\n", priv->phy.sec_chan.type);
+
+ read = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
+
+ return read;
+}
+
+static ssize_t rwnx_dbgfs_type_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct rwnx_hw *priv = file->private_data;
+ char buf[32];
+ int val;
+ size_t len = min_t(size_t, count, sizeof(buf) - 1);
+
+ if (copy_from_user(buf, user_buf, len))
+ return -EFAULT;
+
+ buf[len] = '\0';
+
+ if ((sscanf(buf, "%d", &val) > 0) && (val >= PHY_CHNL_BW_20) &&
+ (val <= PHY_CHNL_BW_80P80))
+ priv->phy.sec_chan.type = val;
+
+ return count;
+}
+
+DEBUGFS_READ_WRITE_FILE_OPS(type);
+
+static ssize_t rwnx_dbgfs_prim20_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct rwnx_hw *priv = file->private_data;
+ char buf[32];
+ int ret;
+ ssize_t read;
+
+ ret = scnprintf(buf, min_t(size_t, sizeof(buf) - 1, count),
+ "PRIM20=%dMHz\n", priv->phy.sec_chan.prim20_freq);
+
+ read = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
+
+ return read;
+}
+
+static ssize_t rwnx_dbgfs_prim20_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct rwnx_hw *priv = file->private_data;
+ char buf[32];
+ int val;
+ size_t len = min_t(size_t, count, sizeof(buf) - 1);
+
+ if (copy_from_user(buf, user_buf, len))
+ return -EFAULT;
+
+ buf[len] = '\0';
+
+ if (sscanf(buf, "%d", &val) > 0)
+ priv->phy.sec_chan.prim20_freq = val;
+
+ return count;
+}
+
+DEBUGFS_READ_WRITE_FILE_OPS(prim20);
+
+static ssize_t rwnx_dbgfs_center1_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct rwnx_hw *priv = file->private_data;
+ char buf[32];
+ int ret;
+ ssize_t read;
+
+ ret = scnprintf(buf, min_t(size_t, sizeof(buf) - 1, count),
+ "CENTER1=%dMHz\n", priv->phy.sec_chan.center_freq1);
+
+ read = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
+
+ return read;
+}
+
+static ssize_t rwnx_dbgfs_center1_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct rwnx_hw *priv = file->private_data;
+ char buf[32];
+ int val;
+ size_t len = min_t(size_t, count, sizeof(buf) - 1);
+
+ if (copy_from_user(buf, user_buf, len))
+ return -EFAULT;
+
+ buf[len] = '\0';
+
+ if (sscanf(buf, "%d", &val) > 0)
+ priv->phy.sec_chan.center_freq1 = val;
+
+ return count;
+}
+
+DEBUGFS_READ_WRITE_FILE_OPS(center1);
+
+static ssize_t rwnx_dbgfs_center2_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct rwnx_hw *priv = file->private_data;
+ char buf[32];
+ int ret;
+ ssize_t read;
+
+ ret = scnprintf(buf, min_t(size_t, sizeof(buf) - 1, count),
+ "CENTER2=%dMHz\n", priv->phy.sec_chan.center_freq2);
+
+ read = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
+
+ return read;
+}
+
+static ssize_t rwnx_dbgfs_center2_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct rwnx_hw *priv = file->private_data;
+ char buf[32];
+ int val;
+ size_t len = min_t(size_t, count, sizeof(buf) - 1);
+
+ if (copy_from_user(buf, user_buf, len))
+ return -EFAULT;
+
+ buf[len] = '\0';
+
+ if (sscanf(buf, "%d", &val) > 0)
+ priv->phy.sec_chan.center_freq2 = val;
+
+ return count;
+}
+
+DEBUGFS_READ_WRITE_FILE_OPS(center2);
+
+
+static ssize_t rwnx_dbgfs_set_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ return 0;
+}
+
+static ssize_t rwnx_dbgfs_set_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct rwnx_hw *priv = file->private_data;
+
+ rwnx_send_set_channel(priv, 1, NULL);
+ rwnx_radar_detection_enable(&priv->radar, RWNX_RADAR_DETECT_ENABLE,
+ RWNX_RADAR_FCU);
+
+ return count;
+}
+
+DEBUGFS_READ_WRITE_FILE_OPS(set);
+#endif /* CONFIG_RWNX_RADAR */
+
+static ssize_t rwnx_dbgfs_regdbg_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+
+ struct rwnx_hw *priv = file->private_data;
+ char buf[32];
+ u32 addr,val, oper;
+ size_t len = min_t(size_t, count, sizeof(buf) - 1);
+ struct dbg_mem_read_cfm mem_read_cfm;
+ int ret;
+
+ if (copy_from_user(buf, user_buf, len))
+ return -EFAULT;
+
+ buf[len] = '\0';
+
+ if (sscanf(buf, "%x %x %x" , &oper, &addr, &val ) > 0)
+ printk("addr=%x, val=%x,oper=%d\n", addr, val, oper);
+
+ if(oper== 0) {
+ ret = rwnx_send_dbg_mem_read_req(priv, addr, &mem_read_cfm);
+ printk("[0x%x] = [0x%x]\n", mem_read_cfm.memaddr, mem_read_cfm.memdata);
+ }
+
+ return count;
+}
+
+DEBUGFS_WRITE_FILE_OPS(regdbg);
+
+static ssize_t rwnx_dbgfs_vendor_hwconfig_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct rwnx_hw *priv = file->private_data;
+ char buf[64];
+ int32_t addr[9];
+ u32_l hwconfig_id;
+ size_t len = min_t(size_t,count,sizeof(buf)-1);
+ int ret;
+ printk("%s\n",__func__);
+ //choose the type of write info by struct
+ //struct mm_set_vendor_trx_param_req trx_param;
+
+ if(copy_from_user(buf,user_buf,len)) {
+ return -EFAULT;
+ }
+
+ buf[len] = '\0';
+ ret = sscanf(buf, "%x %x %x %x %x %x %x %x %x %x %x %x %x",
+ &hwconfig_id, &addr[0], &addr[1], &addr[2], &addr[3], &addr[4], &addr[5], &addr[6], &addr[7], &addr[8], &addr[9], &addr[10], &addr[11]);
+ if(ret > 13) {
+ printk("param error > 13\n");
+ } else {
+ switch(hwconfig_id)
+ {
+ case 0:
+ if(ret != 5) {
+ printk("param error != 5\n");
+ break;}
+ ret = rwnx_send_vendor_hwconfig_req(priv, hwconfig_id, addr);
+ printk("ACS_TXOP_REQ bk:0x%x be:0x%x vi:0x%x vo:0x%x\n",addr[0], addr[1], addr[2], addr[3]);
+ break;
+ case 1:
+ if(ret != 13) {
+ printk("param error != 13\n");
+ break;}
+ ret = rwnx_send_vendor_hwconfig_req(priv, hwconfig_id, addr);
+ printk("CHANNEL_ACCESS_REQ edca:%x,%x,%x,%x, vif:%x, retry_cnt:%x, rts:%x, long_nav:%x, cfe:%x, rc_retry_cnt:%x:%x:%x\n",
+ addr[0], addr[1], addr[2], addr[3], addr[4], addr[5], addr[6], addr[7], addr[8], addr[9], addr[10], addr[11]);
+ break;
+ case 2:
+ if(ret != 7) {
+ printk("param error != 7\n");
+ break;}
+ ret = rwnx_send_vendor_hwconfig_req(priv, hwconfig_id, addr);
+ printk("MAC_TIMESCALE_REQ sifsA:%x,sifsB:%x,slot:%x,ofdm_delay:%x,long_delay:%x,short_delay:%x\n",
+ addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
+ break;
+ case 3:
+ if(ret != 6) {
+ printk("param error != 6\n");
+ break;}
+ addr[1] = ~addr[1] + 1;
+ addr[2] = ~addr[2] + 1;
+ addr[3] = ~addr[3] + 1;
+ addr[4] = ~addr[4] + 1;
+ ret = rwnx_send_vendor_hwconfig_req(priv, hwconfig_id, addr);
+ printk("CCA_THRESHOLD_REQ auto_cca:%d, cca20p_rise:%d cca20s_rise:%d cca20p_fail:%d cca20s_fail:%d\n",
+ addr[0], addr[1], addr[2], addr[3], addr[4]);
+ break;
+ default:
+ printk("param error\n");
+ break;
+ }
+ if(ret) {
+ printk("rwnx_send_vendor_hwconfig_req fail: %x\n", ret);
+ }
+ }
+
+ return count;
+}
+
+DEBUGFS_WRITE_FILE_OPS(vendor_hwconfig)
+
+#ifdef CONFIG_RWNX_FULLMAC
+
+#define LINE_MAX_SZ 150
+
+struct st {
+ char line[LINE_MAX_SZ + 1];
+ unsigned int r_idx;
+};
+
+static int compare_idx(const void *st1, const void *st2)
+{
+ int index1 = ((struct st *)st1)->r_idx;
+ int index2 = ((struct st *)st2)->r_idx;
+
+ if (index1 > index2) return 1;
+ if (index1 < index2) return -1;
+
+ return 0;
+}
+
+static const int ru_size[] =
+{
+ 26,
+ 52,
+ 106,
+ 242,
+ 484,
+ 996
+};
+
+static int print_rate(char *buf, int size, int format, int nss, int mcs, int bw,
+ int sgi, int pre, int *r_idx)
+{
+ int res = 0;
+ int bitrates_cck[4] = { 10, 20, 55, 110 };
+ int bitrates_ofdm[8] = { 6, 9, 12, 18, 24, 36, 48, 54};
+ char he_gi[3][4] = {"0.8", "1.6", "3.2"};
+
+ if (format < FORMATMOD_HT_MF) {
+ if (mcs < 4) {
+ if (r_idx) {
+ *r_idx = (mcs * 2) + pre;
+ res = scnprintf(buf, size - res, "%3d ", *r_idx);
+ }
+ res += scnprintf(&buf[res], size - res, "L-CCK/%cP %2u.%1uM ",
+ pre > 0 ? 'L' : 'S',
+ bitrates_cck[mcs] / 10,
+ bitrates_cck[mcs] % 10);
+ } else {
+ mcs -= 4;
+ if (r_idx) {
+ *r_idx = N_CCK + mcs;
+ res = scnprintf(buf, size - res, "%3d ", *r_idx);
+ }
+ res += scnprintf(&buf[res], size - res, "L-OFDM %2u.0M ",
+ bitrates_ofdm[mcs]);
+ }
+ } else if (format < FORMATMOD_VHT) {
+ if (r_idx) {
+ *r_idx = N_CCK + N_OFDM + nss * 32 + mcs * 4 + bw * 2 + sgi;
+ res = scnprintf(buf, size - res, "%3d ", *r_idx);
+ }
+ mcs += nss * 8;
+ res += scnprintf(&buf[res], size - res, "HT%d/%cGI MCS%-2d ",
+ 20 * (1 << bw), sgi ? 'S' : 'L', mcs);
+ } else if (format == FORMATMOD_VHT){
+ if (r_idx) {
+ *r_idx = N_CCK + N_OFDM + N_HT + nss * 80 + mcs * 8 + bw * 2 + sgi;
+ res = scnprintf(buf, size - res, "%3d ", *r_idx);
+ }
+ res += scnprintf(&buf[res], size - res, "VHT%d/%cGI%*cMCS%d/%1d ",
+ 20 * (1 << bw), sgi ? 'S' : 'L', bw > 2 ? 5 : 6, ' ',
+ mcs, nss + 1);
+ } else if (format == FORMATMOD_HE_SU){
+ if (r_idx) {
+ *r_idx = N_CCK + N_OFDM + N_HT + N_VHT + nss * 144 + mcs * 12 + bw * 3 + sgi;
+ res = scnprintf(buf, size - res, "%3d ", *r_idx);
+ }
+ res += scnprintf(&buf[res], size - res, "HE%d/GI%s%*cMCS%d/%1d%*c",
+ 20 * (1 << bw), he_gi[sgi], bw > 2 ? 4 : 5, ' ',
+ mcs, nss + 1, mcs > 9 ? 1 : 2, ' ');
+ } else {
+ if (r_idx) {
+ *r_idx = N_CCK + N_OFDM + N_HT + N_VHT + N_HE_SU + nss * 216 + mcs * 18 + bw * 3 + sgi;
+ res = scnprintf(buf, size - res, "%3d ", *r_idx);
+ }
+ res += scnprintf(&buf[res], size - res, "HEMU-%d/GI%s%*cMCS%d/%1d%*c",
+ ru_size[bw], he_gi[sgi], bw > 1 ? 1 : 2, ' ',
+ mcs, nss + 1, mcs > 9 ? 1 : 2, ' ');
+
+ }
+
+ return res;
+}
+
+static int print_rate_from_cfg(char *buf, int size, u32 rate_config, int *r_idx, int ru_size)
+{
+ union rwnx_rate_ctrl_info *r_cfg = (union rwnx_rate_ctrl_info *)&rate_config;
+ union rwnx_mcs_index *mcs_index = (union rwnx_mcs_index *)&rate_config;
+ unsigned int ft, pre, gi, bw, nss, mcs, len;
+
+ ft = r_cfg->formatModTx;
+ pre = r_cfg->giAndPreTypeTx >> 1;
+ gi = r_cfg->giAndPreTypeTx;
+ bw = r_cfg->bwTx;
+ if (ft == FORMATMOD_HE_MU) {
+ mcs = mcs_index->he.mcs;
+ nss = mcs_index->he.nss;
+ bw = ru_size;
+ } else if (ft == FORMATMOD_HE_SU) {
+ mcs = mcs_index->he.mcs;
+ nss = mcs_index->he.nss;
+ } else if (ft == FORMATMOD_VHT) {
+ mcs = mcs_index->vht.mcs;
+ nss = mcs_index->vht.nss;
+ } else if (ft >= FORMATMOD_HT_MF) {
+ mcs = mcs_index->ht.mcs;
+ nss = mcs_index->ht.nss;
+ } else {
+ mcs = mcs_index->legacy;
+ nss = 0;
+ }
+
+ len = print_rate(buf, size, ft, nss, mcs, bw, gi, pre, r_idx);
+ return len;
+}
+
+static void idx_to_rate_cfg(int idx, union rwnx_rate_ctrl_info *r_cfg, int *ru_size)
+{
+ r_cfg->value = 0;
+ if (idx < N_CCK)
+ {
+ r_cfg->formatModTx = FORMATMOD_NON_HT;
+ r_cfg->giAndPreTypeTx = (idx & 1) << 1;
+ r_cfg->mcsIndexTx = idx / 2;
+ }
+ else if (idx < (N_CCK + N_OFDM))
+ {
+ r_cfg->formatModTx = FORMATMOD_NON_HT;
+ r_cfg->mcsIndexTx = idx - N_CCK + 4;
+ }
+ else if (idx < (N_CCK + N_OFDM + N_HT))
+ {
+ union rwnx_mcs_index *r = (union rwnx_mcs_index *)r_cfg;
+
+ idx -= (N_CCK + N_OFDM);
+ r_cfg->formatModTx = FORMATMOD_HT_MF;
+ r->ht.nss = idx / (8*2*2);
+ r->ht.mcs = (idx % (8*2*2)) / (2*2);
+ r_cfg->bwTx = ((idx % (8*2*2)) % (2*2)) / 2;
+ r_cfg->giAndPreTypeTx = idx & 1;
+ }
+ else if (idx < (N_CCK + N_OFDM + N_HT + N_VHT))
+ {
+ union rwnx_mcs_index *r = (union rwnx_mcs_index *)r_cfg;
+
+ idx -= (N_CCK + N_OFDM + N_HT);
+ r_cfg->formatModTx = FORMATMOD_VHT;
+ r->vht.nss = idx / (10*4*2);
+ r->vht.mcs = (idx % (10*4*2)) / (4*2);
+ r_cfg->bwTx = ((idx % (10*4*2)) % (4*2)) / 2;
+ r_cfg->giAndPreTypeTx = idx & 1;
+ }
+ else if (idx < (N_CCK + N_OFDM + N_HT + N_VHT + N_HE_SU))
+ {
+ union rwnx_mcs_index *r = (union rwnx_mcs_index *)r_cfg;
+
+ idx -= (N_CCK + N_OFDM + N_HT + N_VHT);
+ r_cfg->formatModTx = FORMATMOD_HE_SU;
+ r->vht.nss = idx / (12*4*3);
+ r->vht.mcs = (idx % (12*4*3)) / (4*3);
+ r_cfg->bwTx = ((idx % (12*4*3)) % (4*3)) / 3;
+ r_cfg->giAndPreTypeTx = idx % 3;
+ }
+ else
+ {
+ union rwnx_mcs_index *r = (union rwnx_mcs_index *)r_cfg;
+
+ BUG_ON(ru_size == NULL);
+
+ idx -= (N_CCK + N_OFDM + N_HT + N_VHT + N_HE_SU);
+ r_cfg->formatModTx = FORMATMOD_HE_MU;
+ r->vht.nss = idx / (12*6*3);
+ r->vht.mcs = (idx % (12*6*3)) / (6*3);
+ *ru_size = ((idx % (12*6*3)) % (6*3)) / 3;
+ r_cfg->giAndPreTypeTx = idx % 3;
+ r_cfg->bwTx = 0;
+ }
+}
+
+static void idx_to_rate_cfg1(unsigned int formatmod,
+ unsigned int mcs,unsigned int nss,
+ unsigned int bwTx,unsigned int gi,
+ union rwnx_rate_ctrl_info *r_cfg, int *ru_size)
+{
+ r_cfg->value = 0;
+
+ switch(formatmod){
+ case FORMATMOD_NON_HT:
+ {
+ r_cfg->formatModTx = formatmod;
+ r_cfg->giAndPreTypeTx = 1;
+ r_cfg->mcsIndexTx = mcs;
+ break;
+ }
+ case FORMATMOD_NON_HT_DUP_OFDM:
+ {
+ r_cfg->formatModTx = formatmod;
+ r_cfg->giAndPreTypeTx = gi;
+ r_cfg->mcsIndexTx = mcs;
+ break;
+ }
+ case FORMATMOD_HT_MF:
+ {
+ union rwnx_mcs_index *r = (union rwnx_mcs_index *)r_cfg;
+
+ r_cfg->formatModTx = formatmod;
+ r->ht.nss = nss;
+ r->ht.mcs = mcs;
+ r_cfg->bwTx = bwTx;
+ r_cfg->giAndPreTypeTx = gi;
+ break;
+ }
+ case FORMATMOD_VHT:
+ case FORMATMOD_HE_SU:
+ {
+ union rwnx_mcs_index *r = (union rwnx_mcs_index *)r_cfg;
+
+ r_cfg->formatModTx = formatmod;
+ r->vht.nss = nss;
+ r->vht.mcs = mcs;
+ r_cfg->bwTx = bwTx;
+ r_cfg->giAndPreTypeTx = gi;
+ break;
+ }
+ case FORMATMOD_HE_MU:
+ {
+ union rwnx_mcs_index *r = (union rwnx_mcs_index *)r_cfg;
+
+ r_cfg->formatModTx = formatmod;
+ r->he.nss = nss;
+ r->he.mcs = mcs;
+ r_cfg->bwTx = 0;
+ r_cfg->giAndPreTypeTx = gi;
+ break;
+ }
+ default:
+ printk("Don't have the formatmod");
+ }
+}
+
+static ssize_t rwnx_dbgfs_rc_stats_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct rwnx_sta *sta = NULL;
+ struct rwnx_hw *priv = file->private_data;
+ char *buf;
+ int bufsz, len = 0;
+ ssize_t read;
+ int i = 0;
+ int error = 0;
+ struct me_rc_stats_cfm me_rc_stats_cfm;
+ unsigned int no_samples;
+ struct st *st;
+ u8 mac[6];
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ /* everything should fit in one call */
+ if (*ppos)
+ return 0;
+
+ /* Get the station index from MAC address */
+ sscanf(file->f_path.dentry->d_parent->d_iname, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
+ &mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5]);
+ //if (mac == NULL)
+ // return 0;
+ sta = rwnx_get_sta(priv, mac);
+ if (sta == NULL)
+ return 0;
+
+ /* Forward the information to the LMAC */
+ if ((error = rwnx_send_me_rc_stats(priv, sta->sta_idx, &me_rc_stats_cfm)))
+ return error;
+
+ no_samples = me_rc_stats_cfm.no_samples;
+ if (no_samples == 0)
+ return 0;
+
+ bufsz = no_samples * LINE_MAX_SZ + 500;
+
+ buf = kmalloc(bufsz + 1, GFP_ATOMIC);
+ if (buf == NULL)
+ return 0;
+
+ st = kmalloc(sizeof(struct st) * no_samples, GFP_ATOMIC);
+ if (st == NULL)
+ {
+ kfree(buf);
+ return 0;
+ }
+
+ for (i = 0; i < no_samples; i++)
+ {
+ unsigned int tp, eprob;
+ len = print_rate_from_cfg(st[i].line, LINE_MAX_SZ,
+ me_rc_stats_cfm.rate_stats[i].rate_config,
+ &st[i].r_idx, 0);
+
+ if (me_rc_stats_cfm.sw_retry_step != 0)
+ {
+ len += scnprintf(&st[i].line[len], LINE_MAX_SZ - len, "%c",
+ me_rc_stats_cfm.retry_step_idx[me_rc_stats_cfm.sw_retry_step] == i ? '*' : ' ');
+ }
+ else
+ {
+ len += scnprintf(&st[i].line[len], LINE_MAX_SZ - len, " ");
+ }
+ len += scnprintf(&st[i].line[len], LINE_MAX_SZ - len, "%c",
+ me_rc_stats_cfm.retry_step_idx[0] == i ? 'T' : ' ');
+ len += scnprintf(&st[i].line[len], LINE_MAX_SZ - len, "%c",
+ me_rc_stats_cfm.retry_step_idx[1] == i ? 't' : ' ');
+ len += scnprintf(&st[i].line[len], LINE_MAX_SZ - len, "%c ",
+ me_rc_stats_cfm.retry_step_idx[2] == i ? 'P' : ' ');
+
+ tp = me_rc_stats_cfm.tp[i] / 10;
+ len += scnprintf(&st[i].line[len], LINE_MAX_SZ - len, " %4u.%1u",
+ tp / 10, tp % 10);
+
+ eprob = ((me_rc_stats_cfm.rate_stats[i].probability * 1000) >> 16) + 1;
+ len += scnprintf(&st[i].line[len],LINE_MAX_SZ - len,
+ " %4u.%1u %5u(%6u) %6u",
+ eprob / 10, eprob % 10,
+ me_rc_stats_cfm.rate_stats[i].success,
+ me_rc_stats_cfm.rate_stats[i].attempts,
+ me_rc_stats_cfm.rate_stats[i].sample_skipped);
+ }
+ len = scnprintf(buf, bufsz ,
+ "\nTX rate info for %02X:%02X:%02X:%02X:%02X:%02X:\n",
+ mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+ len += scnprintf(&buf[len], bufsz - len,
+ " # type rate tpt eprob ok( tot) skipped\n");
+
+ // add sorted statistics to the buffer
+ sort(st, no_samples, sizeof(st[0]), compare_idx, NULL);
+ for (i = 0; i < no_samples; i++)
+ {
+ len += scnprintf(&buf[len], bufsz - len, "%s\n", st[i].line);
+ }
+
+ // display HE TB statistics if any
+ if (me_rc_stats_cfm.rate_stats[RC_HE_STATS_IDX].rate_config != 0) {
+ unsigned int tp, eprob;
+ struct rc_rate_stats *rate_stats = &me_rc_stats_cfm.rate_stats[RC_HE_STATS_IDX];
+ int ru_index = rate_stats->ru_and_length & 0x07;
+ int ul_length = rate_stats->ru_and_length >> 3;
+
+ len += scnprintf(&buf[len], bufsz - len,
+ "\nHE TB rate info:\n");
+
+ len += scnprintf(&buf[len], bufsz - len,
+ " type rate tpt eprob ok( tot) ul_length\n ");
+ len += print_rate_from_cfg(&buf[len], bufsz - len, rate_stats->rate_config,
+ NULL, ru_index);
+
+ tp = me_rc_stats_cfm.tp[RC_HE_STATS_IDX] / 10;
+ len += scnprintf(&buf[len], bufsz - len, " %4u.%1u",
+ tp / 10, tp % 10);
+
+ eprob = ((rate_stats->probability * 1000) >> 16) + 1;
+ len += scnprintf(&buf[len],bufsz - len,
+ " %4u.%1u %5u(%6u) %6u\n",
+ eprob / 10, eprob % 10,
+ rate_stats->success,
+ rate_stats->attempts,
+ ul_length);
+ }
+
+ len += scnprintf(&buf[len], bufsz - len, "\n MPDUs AMPDUs AvLen trialP");
+ len += scnprintf(&buf[len], bufsz - len, "\n%6u %6u %3d.%1d %6u\n",
+ me_rc_stats_cfm.ampdu_len,
+ me_rc_stats_cfm.ampdu_packets,
+ me_rc_stats_cfm.avg_ampdu_len >> 16,
+ ((me_rc_stats_cfm.avg_ampdu_len * 10) >> 16) % 10,
+ me_rc_stats_cfm.sample_wait);
+
+ read = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+
+ kfree(buf);
+ kfree(st);
+
+ return read;
+}
+
+DEBUGFS_READ_FILE_OPS(rc_stats);
+
+static ssize_t rwnx_dbgfs_rc_fixed_rate_idx_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct rwnx_sta *sta = NULL;
+ struct rwnx_hw *priv = file->private_data;
+ u8 mac[6];
+ char buf[10];
+ int fixed_rate_idx = 1;
+ unsigned int formatmod, mcs, nss, bwTx, gi;
+ union rwnx_rate_ctrl_info rate_config;
+ int error = 0;
+ size_t len = min_t(size_t, count, sizeof(buf) - 1);
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ /* Get the station index from MAC address */
+ sscanf(file->f_path.dentry->d_parent->d_iname, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
+ &mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5]);
+ if (&mac[0] == NULL)
+ return 0;
+ sta = rwnx_get_sta(priv, mac);
+ if (sta == NULL)
+ return 0;
+
+ /* Get the content of the file */
+ if (copy_from_user(buf, user_buf, len))
+ return -EFAULT;
+ buf[len] = '\0';
+ //sscanf(buf, "%i\n", &fixed_rate_idx);
+ sscanf(buf, "%u %u %u %u %u",&formatmod, &mcs, &nss, &bwTx, &gi);
+ //printk("%u %u %u %u %u\n",formatmod, mcs, nss, bwTx, gi);
+ /* Convert rate index into rate configuration */
+ if ((fixed_rate_idx < 0) || (fixed_rate_idx >= (N_CCK + N_OFDM + N_HT + N_VHT + N_HE_SU)))
+ {
+ // disable fixed rate
+ rate_config.value = (u32)-1;
+ }
+ else
+ {
+ //idx_to_rate_cfg(fixed_rate_idx, &rate_config, NULL);
+ idx_to_rate_cfg1(formatmod, mcs, nss, bwTx, gi, &rate_config, NULL);
+ }
+ /*union rwnx_rate_ctrl_info *r_cfg=&rate_config;
+ printk("formatModTx=%u mcsIndexTx=%u bwTx=%u giAndPreTypeTx=%u\n",r_cfg->formatModTx,r_cfg->mcsIndexTx,r_cfg->bwTx,r_cfg->giAndPreTypeTx);
+ printk("you wen ti");*/
+ // Forward the request to the LMAC
+ if ((error = rwnx_send_me_rc_set_rate(priv, sta->sta_idx,
+ (u16)rate_config.value)) != 0)
+ {
+ return error;
+ }
+
+ priv->debugfs.rc_config[sta->sta_idx] = (int)rate_config.value;
+ return len;
+
+}
+
+
+DEBUGFS_WRITE_FILE_OPS(rc_fixed_rate_idx);
+
+static ssize_t rwnx_dbgfs_last_rx_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct rwnx_sta *sta = NULL;
+ struct rwnx_hw *priv = file->private_data;
+ struct rwnx_rx_rate_stats *rate_stats;
+ char *buf;
+ int bufsz, i, len = 0;
+ ssize_t read;
+ unsigned int fmt, pre, bw, nss, mcs, gi;
+ u8 mac[6];
+ struct rx_vector_1 *last_rx;
+ char hist[] = "##################################################";
+ int hist_len = sizeof(hist) - 1;
+ u8 nrx;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ /* everything should fit in one call */
+ if (*ppos)
+ return 0;
+
+ /* Get the station index from MAC address */
+ sscanf(file->f_path.dentry->d_parent->d_iname, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
+ &mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5]);
+ //if (mac == NULL)
+ // return 0;
+ sta = rwnx_get_sta(priv, mac);
+ if (sta == NULL)
+ return 0;
+
+ rate_stats = &sta->stats.rx_rate;
+ bufsz = (rate_stats->rate_cnt * ( 50 + hist_len) + 200);
+ buf = kmalloc(bufsz + 1, GFP_ATOMIC);
+ if (buf == NULL)
+ return 0;
+
+ // Get number of RX paths
+ nrx = (priv->version_cfm.version_phy_1 & MDM_NRX_MASK) >> MDM_NRX_LSB;
+
+ len += scnprintf(buf, bufsz,
+ "\nRX rate info for %02X:%02X:%02X:%02X:%02X:%02X:\n",
+ mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+ // Display Statistics
+ for (i = 0 ; i < rate_stats->size ; i++ )
+ {
+ if (rate_stats->table[i]) {
+ union rwnx_rate_ctrl_info rate_config;
+ int percent = (rate_stats->table[i] * 1000) / rate_stats->cpt;
+ int p;
+ int ru_size;
+
+ idx_to_rate_cfg(i, &rate_config, &ru_size);
+ len += print_rate_from_cfg(&buf[len], bufsz - len,
+ rate_config.value, NULL, ru_size);
+ p = (percent * hist_len) / 1000;
+ len += scnprintf(&buf[len], bufsz - len, ": %6d(%3d.%1d%%)%.*s\n",
+ rate_stats->table[i],
+ percent / 10, percent % 10, p, hist);
+ }
+ }
+
+ // Display detailed info of the last received rate
+ last_rx = &sta->stats.last_rx.rx_vect1;
+
+ len += scnprintf(&buf[len], bufsz - len,"\nLast received rate\n"
+ " type rate LDPC STBC BEAMFM DCM DOPPLER %s\n",
+ (nrx > 1) ? "rssi1(dBm) rssi2(dBm)" : "rssi(dBm)");
+
+ fmt = last_rx->format_mod;
+ bw = last_rx->ch_bw;
+ pre = last_rx->pre_type;
+ if (fmt >= FORMATMOD_HE_SU) {
+ mcs = last_rx->he.mcs;
+ nss = last_rx->he.nss;
+ gi = last_rx->he.gi_type;
+ if (fmt == FORMATMOD_HE_MU)
+ bw = last_rx->he.ru_size;
+ } else if (fmt == FORMATMOD_VHT) {
+ mcs = last_rx->vht.mcs;
+ nss = last_rx->vht.nss;
+ gi = last_rx->vht.short_gi;
+ } else if (fmt >= FORMATMOD_HT_MF) {
+ mcs = last_rx->ht.mcs % 8;
+ nss = last_rx->ht.mcs / 8;;
+ gi = last_rx->ht.short_gi;
+ } else {
+ BUG_ON((mcs = legrates_lut[last_rx->leg_rate]) == -1);
+ nss = 0;
+ gi = 0;
+ }
+
+ len += print_rate(&buf[len], bufsz - len, fmt, nss, mcs, bw, gi, pre, NULL);
+
+ /* flags for HT/VHT/HE */
+ if (fmt >= FORMATMOD_HE_SU) {
+ len += scnprintf(&buf[len], bufsz - len, " %c %c %c %c %c",
+ last_rx->he.fec ? 'L' : ' ',
+ last_rx->he.stbc ? 'S' : ' ',
+ last_rx->he.beamformed ? 'B' : ' ',
+ last_rx->he.dcm ? 'D' : ' ',
+ last_rx->he.doppler ? 'D' : ' ');
+ } else if (fmt == FORMATMOD_VHT) {
+ len += scnprintf(&buf[len], bufsz - len, " %c %c %c ",
+ last_rx->vht.fec ? 'L' : ' ',
+ last_rx->vht.stbc ? 'S' : ' ',
+ last_rx->vht.beamformed ? 'B' : ' ');
+ } else if (fmt >= FORMATMOD_HT_MF) {
+ len += scnprintf(&buf[len], bufsz - len, " %c %c ",
+ last_rx->ht.fec ? 'L' : ' ',
+ last_rx->ht.stbc ? 'S' : ' ');
+ } else {
+ len += scnprintf(&buf[len], bufsz - len, " ");
+ }
+ if (nrx > 1) {
+ len += scnprintf(&buf[len], bufsz - len, " %-4d %d\n",
+ last_rx->rssi1, last_rx->rssi1);
+ } else {
+ len += scnprintf(&buf[len], bufsz - len, " %d\n", last_rx->rssi1);
+ }
+
+ read = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+
+ kfree(buf);
+ return read;
+}
+
+static ssize_t rwnx_dbgfs_last_rx_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct rwnx_sta *sta = NULL;
+ struct rwnx_hw *priv = file->private_data;
+ u8 mac[6];
+
+ /* Get the station index from MAC address */
+ sscanf(file->f_path.dentry->d_parent->d_iname, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
+ &mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5]);
+ //if (mac == NULL)
+ // return 0;
+ sta = rwnx_get_sta(priv, mac);
+ if (sta == NULL)
+ return 0;
+
+ /* Prevent from interrupt preemption as these statistics are updated under
+ * interrupt */
+ spin_lock_bh(&priv->tx_lock);
+ memset(sta->stats.rx_rate.table, 0,
+ sta->stats.rx_rate.size * sizeof(sta->stats.rx_rate.table[0]));
+ sta->stats.rx_rate.cpt = 0;
+ sta->stats.rx_rate.rate_cnt = 0;
+ spin_unlock_bh(&priv->tx_lock);
+
+ return count;
+}
+
+DEBUGFS_READ_WRITE_FILE_OPS(last_rx);
+
+#endif /* CONFIG_RWNX_FULLMAC */
+
+#ifdef CONFIG_RWNX_FULLMAC
+static void rwnx_rc_stat_work(struct work_struct *ws)
+{
+ struct rwnx_debugfs *rwnx_debugfs = container_of(ws, struct rwnx_debugfs,
+ rc_stat_work);
+ struct rwnx_hw *rwnx_hw = container_of(rwnx_debugfs, struct rwnx_hw,
+ debugfs);
+ struct rwnx_sta *sta;
+ uint8_t ridx, sta_idx;
+
+ ridx = rwnx_debugfs->rc_read;
+ sta_idx = rwnx_debugfs->rc_sta[ridx];
+ if (sta_idx > (NX_REMOTE_STA_MAX + NX_VIRT_DEV_MAX)) {
+ WARN(1, "Invalid sta index %d", sta_idx);
+ return;
+ }
+
+ rwnx_debugfs->rc_sta[ridx] = 0xFF;
+ ridx = (ridx + 1) % ARRAY_SIZE(rwnx_debugfs->rc_sta);
+ rwnx_debugfs->rc_read = ridx;
+ sta = &rwnx_hw->sta_table[sta_idx];
+ if (!sta) {
+ WARN(1, "Invalid sta %d", sta_idx);
+ return;
+ }
+
+ if (rwnx_debugfs->dir_sta[sta_idx] == NULL) {
+ /* register the sta */
+ struct dentry *dir_rc = rwnx_debugfs->dir_rc;
+ struct dentry *dir_sta;
+ struct dentry *file;
+ char sta_name[18];
+ struct rwnx_rx_rate_stats *rate_stats = &sta->stats.rx_rate;
+ int nb_rx_rate = N_CCK + N_OFDM;
+ struct rwnx_rc_config_save *rc_cfg, *next;
+
+ if (sta->sta_idx >= NX_REMOTE_STA_MAX) {
+ scnprintf(sta_name, sizeof(sta_name), "bc_mc");
+ } else {
+ scnprintf(sta_name, sizeof(sta_name), "%pM", sta->mac_addr);
+ }
+
+ if (!(dir_sta = debugfs_create_dir(sta_name, dir_rc)))
+ goto error;
+
+ rwnx_debugfs->dir_sta[sta->sta_idx] = dir_sta;
+
+ file = debugfs_create_file("stats", S_IRUSR, dir_sta, rwnx_hw,
+ &rwnx_dbgfs_rc_stats_ops);
+ if (IS_ERR_OR_NULL(file))
+ goto error_after_dir;
+
+ file = debugfs_create_file("fixed_rate_idx", S_IWUSR , dir_sta, rwnx_hw,
+ &rwnx_dbgfs_rc_fixed_rate_idx_ops);
+ if (IS_ERR_OR_NULL(file))
+ goto error_after_dir;
+
+ file = debugfs_create_file("rx_rate", S_IRUSR | S_IWUSR, dir_sta, rwnx_hw,
+ &rwnx_dbgfs_last_rx_ops);
+ if (IS_ERR_OR_NULL(file))
+ goto error_after_dir;
+
+ if (rwnx_hw->mod_params->ht_on)
+ nb_rx_rate += N_HT;
+
+ if (rwnx_hw->mod_params->vht_on)
+ nb_rx_rate += N_VHT;
+
+ if (rwnx_hw->mod_params->he_on)
+ nb_rx_rate += N_HE_SU + N_HE_MU;
+
+ rate_stats->table = kzalloc(nb_rx_rate * sizeof(rate_stats->table[0]),
+ GFP_KERNEL);
+ if (!rate_stats->table)
+ goto error_after_dir;
+
+ rate_stats->size = nb_rx_rate;
+ rate_stats->cpt = 0;
+ rate_stats->rate_cnt = 0;
+
+ /* By default enable rate contoller */
+ rwnx_debugfs->rc_config[sta_idx] = -1;
+
+ /* Unless we already fix the rate for this station */
+ list_for_each_entry_safe(rc_cfg, next, &rwnx_debugfs->rc_config_save, list) {
+ if (jiffies_to_msecs(jiffies - rc_cfg->timestamp) > RC_CONFIG_DUR) {
+ list_del(&rc_cfg->list);
+ kfree(rc_cfg);
+ } else if (!memcmp(rc_cfg->mac_addr, sta->mac_addr, ETH_ALEN)) {
+ rwnx_debugfs->rc_config[sta_idx] = rc_cfg->rate;
+ list_del(&rc_cfg->list);
+ kfree(rc_cfg);
+ break;
+ }
+ }
+
+ if ((rwnx_debugfs->rc_config[sta_idx] >= 0) &&
+ rwnx_send_me_rc_set_rate(rwnx_hw, sta_idx,
+ (u16)rwnx_debugfs->rc_config[sta_idx]))
+ rwnx_debugfs->rc_config[sta_idx] = -1;
+
+ } else {
+ /* unregister the sta */
+ if (sta->stats.rx_rate.table) {
+ kfree(sta->stats.rx_rate.table);
+ sta->stats.rx_rate.table = NULL;
+ }
+ sta->stats.rx_rate.size = 0;
+ sta->stats.rx_rate.cpt = 0;
+ sta->stats.rx_rate.rate_cnt = 0;
+
+ /* If fix rate was set for this station, save the configuration in case
+ we reconnect to this station within RC_CONFIG_DUR msec */
+ if (rwnx_debugfs->rc_config[sta_idx] >= 0) {
+ struct rwnx_rc_config_save *rc_cfg;
+ rc_cfg = kmalloc(sizeof(*rc_cfg), GFP_KERNEL);
+ if (rc_cfg) {
+ rc_cfg->rate = rwnx_debugfs->rc_config[sta_idx];
+ rc_cfg->timestamp = jiffies;
+ memcpy(rc_cfg->mac_addr, sta->mac_addr, ETH_ALEN);
+ list_add_tail(&rc_cfg->list, &rwnx_debugfs->rc_config_save);
+ }
+ }
+
+ debugfs_remove_recursive(rwnx_debugfs->dir_sta[sta_idx]);
+ rwnx_debugfs->dir_sta[sta->sta_idx] = NULL;
+ }
+
+ return;
+
+ error_after_dir:
+ debugfs_remove_recursive(rwnx_debugfs->dir_sta[sta_idx]);
+ rwnx_debugfs->dir_sta[sta->sta_idx] = NULL;
+ error:
+ dev_err(rwnx_hw->dev,
+ "Error while (un)registering debug entry for sta %d\n", sta_idx);
+}
+
+void _rwnx_dbgfs_rc_stat_write(struct rwnx_debugfs *rwnx_debugfs, uint8_t sta_idx)
+{
+ uint8_t widx = rwnx_debugfs->rc_write;
+ if (rwnx_debugfs->rc_sta[widx] != 0XFF) {
+ WARN(1, "Overlap in debugfs rc_sta table\n");
+ }
+
+ if (rwnx_debugfs->unregistering)
+ return;
+
+ rwnx_debugfs->rc_sta[widx] = sta_idx;
+ widx = (widx + 1) % ARRAY_SIZE(rwnx_debugfs->rc_sta);
+ rwnx_debugfs->rc_write = widx;
+
+ schedule_work(&rwnx_debugfs->rc_stat_work);
+}
+
+void rwnx_dbgfs_register_rc_stat(struct rwnx_hw *rwnx_hw, struct rwnx_sta *sta)
+{
+ _rwnx_dbgfs_rc_stat_write(&rwnx_hw->debugfs, sta->sta_idx);
+}
+
+void rwnx_dbgfs_unregister_rc_stat(struct rwnx_hw *rwnx_hw, struct rwnx_sta *sta)
+{
+ _rwnx_dbgfs_rc_stat_write(&rwnx_hw->debugfs, sta->sta_idx);
+}
+#endif /* CONFIG_RWNX_FULLMAC */
+
+int rwnx_dbgfs_register(struct rwnx_hw *rwnx_hw, const char *name)
+{
+#ifdef CONFIG_RWNX_FULLMAC
+ struct dentry *phyd = rwnx_hw->wiphy->debugfsdir;
+ struct dentry *dir_rc;
+#endif /* CONFIG_RWNX_FULLMAC */
+ struct rwnx_debugfs *rwnx_debugfs = &rwnx_hw->debugfs;
+ struct dentry *dir_drv, *dir_diags;
+
+ if (!(dir_drv = debugfs_create_dir(name, phyd)))
+ return -ENOMEM;
+
+ rwnx_debugfs->dir = dir_drv;
+ rwnx_debugfs->unregistering = false;
+
+ if (!(dir_diags = debugfs_create_dir("diags", dir_drv)))
+ goto err;
+
+#ifdef CONFIG_RWNX_FULLMAC
+ if (!(dir_rc = debugfs_create_dir("rc", dir_drv)))
+ goto err;
+ rwnx_debugfs->dir_rc = dir_rc;
+ INIT_WORK(&rwnx_debugfs->rc_stat_work, rwnx_rc_stat_work);
+ INIT_LIST_HEAD(&rwnx_debugfs->rc_config_save);
+ rwnx_debugfs->rc_write = rwnx_debugfs->rc_read = 0;
+ memset(rwnx_debugfs->rc_sta, 0xFF, sizeof(rwnx_debugfs->rc_sta));
+#endif
+
+ DEBUGFS_ADD_U32(tcp_pacing_shift, dir_drv, &rwnx_hw->tcp_pacing_shift,
+ S_IWUSR | S_IRUSR);
+ DEBUGFS_ADD_FILE(stats, dir_drv, S_IWUSR | S_IRUSR);
+ DEBUGFS_ADD_FILE(sys_stats, dir_drv, S_IRUSR);
+ DEBUGFS_ADD_FILE(txq, dir_drv, S_IRUSR);
+ DEBUGFS_ADD_FILE(acsinfo, dir_drv, S_IRUSR);
+#ifdef CONFIG_RWNX_MUMIMO_TX
+ DEBUGFS_ADD_FILE(mu_group, dir_drv, S_IRUSR);
+#endif
+ DEBUGFS_ADD_FILE(regdbg, dir_drv, S_IWUSR);
+ DEBUGFS_ADD_FILE(vendor_hwconfig, dir_drv,S_IWUSR);
+
+#ifdef CONFIG_RWNX_P2P_DEBUGFS
+ {
+ /* Create a p2p directory */
+ struct dentry *dir_p2p;
+ if (!(dir_p2p = debugfs_create_dir("p2p", dir_drv)))
+ goto err;
+
+ /* Add file allowing to control Opportunistic PS */
+ DEBUGFS_ADD_FILE(oppps, dir_p2p, S_IRUSR);
+ /* Add file allowing to control Notice of Absence */
+ DEBUGFS_ADD_FILE(noa, dir_p2p, S_IRUSR);
+ }
+#endif /* CONFIG_RWNX_P2P_DEBUGFS */
+
+ if (rwnx_hw->fwlog_en) {
+ rwnx_fw_log_init(&rwnx_hw->debugfs.fw_log);
+ DEBUGFS_ADD_FILE(fw_log, dir_drv, S_IWUSR | S_IRUSR);
+ }
+#ifdef CONFIG_RWNX_RADAR
+ {
+ struct dentry *dir_radar, *dir_sec;
+ if (!(dir_radar = debugfs_create_dir("radar", dir_drv)))
+ goto err;
+
+ DEBUGFS_ADD_FILE(pulses_prim, dir_radar, S_IRUSR);
+ DEBUGFS_ADD_FILE(detected, dir_radar, S_IRUSR);
+ DEBUGFS_ADD_FILE(enable, dir_radar, S_IRUSR);
+
+ if (rwnx_hw->phy.cnt == 2) {
+ DEBUGFS_ADD_FILE(pulses_sec, dir_radar, S_IRUSR);
+
+ if (!(dir_sec = debugfs_create_dir("sec", dir_radar)))
+ goto err;
+
+ DEBUGFS_ADD_FILE(band, dir_sec, S_IWUSR | S_IRUSR);
+ DEBUGFS_ADD_FILE(type, dir_sec, S_IWUSR | S_IRUSR);
+ DEBUGFS_ADD_FILE(prim20, dir_sec, S_IWUSR | S_IRUSR);
+ DEBUGFS_ADD_FILE(center1, dir_sec, S_IWUSR | S_IRUSR);
+ DEBUGFS_ADD_FILE(center2, dir_sec, S_IWUSR | S_IRUSR);
+ DEBUGFS_ADD_FILE(set, dir_sec, S_IWUSR | S_IRUSR);
+ }
+ }
+#endif /* CONFIG_RWNX_RADAR */
+ return 0;
+
+err:
+ rwnx_dbgfs_unregister(rwnx_hw);
+ return -ENOMEM;
+}
+
+void rwnx_dbgfs_unregister(struct rwnx_hw *rwnx_hw)
+{
+ struct rwnx_debugfs *rwnx_debugfs = &rwnx_hw->debugfs;
+#ifdef CONFIG_RWNX_FULLMAC
+ struct rwnx_rc_config_save *cfg, *next;
+#endif
+
+#ifdef CONFIG_RWNX_FULLMAC
+ list_for_each_entry_safe(cfg, next, &rwnx_debugfs->rc_config_save, list) {
+ list_del(&cfg->list);
+ kfree(cfg);
+ }
+#endif /* CONFIG_RWNX_FULLMAC */
+
+ if (rwnx_hw->fwlog_en)
+ rwnx_fw_log_deinit(&rwnx_hw->debugfs.fw_log);
+
+ if (!rwnx_hw->debugfs.dir)
+ return;
+
+ rwnx_debugfs->unregistering = true;
+#ifdef CONFIG_RWNX_FULLMAC
+ flush_work(&rwnx_debugfs->rc_stat_work);
+#endif
+ debugfs_remove_recursive(rwnx_hw->debugfs.dir);
+ rwnx_hw->debugfs.dir = NULL;
+}
+
+#endif //
diff --git a/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_debugfs.h b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_debugfs.h
new file mode 100644
index 000000000000..5d93f1294a79
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_debugfs.h
@@ -0,0 +1,203 @@
+/**
+ ******************************************************************************
+ *
+ * @file rwnx_debugfs.h
+ *
+ * @brief Miscellaneous utility function definitions
+ *
+ * Copyright (C) RivieraWaves 2012-2019
+ *
+ ******************************************************************************
+ */
+
+
+#ifndef _RWNX_DEBUGFS_H_
+#define _RWNX_DEBUGFS_H_
+
+#include <linux/workqueue.h>
+#include <linux/if_ether.h>
+#include <linux/version.h>
+#include "rwnx_fw_trace.h"
+
+struct rwnx_hw;
+struct rwnx_sta;
+
+/* some macros taken from iwlwifi */
+/* TODO: replace with generic read and fill read buffer in open to avoid double
+ * reads */
+#define DEBUGFS_ADD_FILE(name, parent, mode) do { \
+ if (!debugfs_create_file(#name, mode, parent, rwnx_hw, \
+ &rwnx_dbgfs_##name##_ops)) \
+ goto err; \
+} while (0)
+
+#define DEBUGFS_ADD_BOOL(name, parent, ptr) do { \
+ struct dentry *__tmp; \
+ __tmp = debugfs_create_bool(#name, S_IWUSR | S_IRUSR, \
+ parent, ptr); \
+ if (IS_ERR(__tmp) || !__tmp) \
+ goto err; \
+} while (0)
+
+#define DEBUGFS_ADD_X64(name, parent, ptr) do { \
+ struct dentry *__tmp; \
+ __tmp = debugfs_create_x64(#name, S_IWUSR | S_IRUSR, \
+ parent, ptr); \
+ if (IS_ERR(__tmp) || !__tmp) \
+ goto err; \
+} while (0)
+
+#define DEBUGFS_ADD_U64(name, parent, ptr, mode) do { \
+ struct dentry *__tmp; \
+ __tmp = debugfs_create_u64(#name, mode, \
+ parent, ptr); \
+ if (IS_ERR(__tmp) || !__tmp) \
+ goto err; \
+} while (0)
+
+#define DEBUGFS_ADD_X32(name, parent, ptr) do { \
+ struct dentry *__tmp; \
+ __tmp = debugfs_create_x32(#name, S_IWUSR | S_IRUSR, \
+ parent, ptr); \
+ if (IS_ERR(__tmp) || !__tmp) \
+ goto err; \
+} while (0)
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 7, 0)
+#define DEBUGFS_ADD_U32(name, parent, ptr, mode) do { \
+ debugfs_create_u32(#name, mode, \
+ parent, ptr); \
+} while (0)
+#else
+#define DEBUGFS_ADD_U32(name, parent, ptr, mode) do { \
+ struct dentry *__tmp; \
+ __tmp = debugfs_create_u32(#name, mode, \
+ parent, ptr); \
+ if (IS_ERR(__tmp) || !__tmp) \
+ goto err; \
+ } while (0)
+#endif
+
+
+/* file operation */
+#define DEBUGFS_READ_FUNC(name) \
+ static ssize_t rwnx_dbgfs_##name##_read(struct file *file, \
+ char __user *user_buf, \
+ size_t count, loff_t *ppos);
+
+#define DEBUGFS_WRITE_FUNC(name) \
+ static ssize_t rwnx_dbgfs_##name##_write(struct file *file, \
+ const char __user *user_buf,\
+ size_t count, loff_t *ppos);
+
+#define DEBUGFS_OPEN_FUNC(name) \
+ static int rwnx_dbgfs_##name##_open(struct inode *inode, \
+ struct file *file);
+
+#define DEBUGFS_RELEASE_FUNC(name) \
+ static int rwnx_dbgfs_##name##_release(struct inode *inode, \
+ struct file *file);
+
+#define DEBUGFS_READ_FILE_OPS(name) \
+ DEBUGFS_READ_FUNC(name); \
+static const struct file_operations rwnx_dbgfs_##name##_ops = { \
+ .read = rwnx_dbgfs_##name##_read, \
+ .open = simple_open, \
+ .llseek = generic_file_llseek, \
+};
+
+#define DEBUGFS_WRITE_FILE_OPS(name) \
+ DEBUGFS_WRITE_FUNC(name); \
+static const struct file_operations rwnx_dbgfs_##name##_ops = { \
+ .write = rwnx_dbgfs_##name##_write, \
+ .open = simple_open, \
+ .llseek = generic_file_llseek, \
+};
+
+#define DEBUGFS_READ_WRITE_FILE_OPS(name) \
+ DEBUGFS_READ_FUNC(name); \
+ DEBUGFS_WRITE_FUNC(name); \
+static const struct file_operations rwnx_dbgfs_##name##_ops = { \
+ .write = rwnx_dbgfs_##name##_write, \
+ .read = rwnx_dbgfs_##name##_read, \
+ .open = simple_open, \
+ .llseek = generic_file_llseek, \
+};
+
+#define DEBUGFS_READ_WRITE_OPEN_RELEASE_FILE_OPS(name) \
+ DEBUGFS_READ_FUNC(name); \
+ DEBUGFS_WRITE_FUNC(name); \
+ DEBUGFS_OPEN_FUNC(name); \
+ DEBUGFS_RELEASE_FUNC(name); \
+static const struct file_operations rwnx_dbgfs_##name##_ops = { \
+ .write = rwnx_dbgfs_##name##_write, \
+ .read = rwnx_dbgfs_##name##_read, \
+ .open = rwnx_dbgfs_##name##_open, \
+ .release = rwnx_dbgfs_##name##_release, \
+ .llseek = generic_file_llseek, \
+};
+
+
+#ifdef CONFIG_RWNX_DEBUGFS
+
+struct rwnx_debugfs {
+ unsigned long long rateidx;
+ struct dentry *dir;
+ bool trace_prst;
+
+ char helper_cmd[64];
+ //struct work_struct helper_work;
+ bool helper_scheduled;
+ spinlock_t umh_lock;
+ bool unregistering;
+
+#ifndef CONFIG_RWNX_FHOST
+ struct rwnx_fw_log fw_log;
+#endif /* CONFIG_RWNX_FHOST */
+
+#ifdef CONFIG_RWNX_FULLMAC
+ struct work_struct rc_stat_work;
+ uint8_t rc_sta[NX_REMOTE_STA_MAX];
+ uint8_t rc_write;
+ uint8_t rc_read;
+ struct dentry *dir_rc;
+ struct dentry *dir_sta[NX_REMOTE_STA_MAX];
+ int rc_config[NX_REMOTE_STA_MAX];
+ struct list_head rc_config_save;
+#endif
+};
+
+#ifdef CONFIG_RWNX_FULLMAC
+
+// Max duration in msecs to save rate config for a sta after disconnection
+#define RC_CONFIG_DUR 600000
+
+struct rwnx_rc_config_save {
+ struct list_head list;
+ unsigned long timestamp;
+ int rate;
+ u8 mac_addr[ETH_ALEN];
+};
+#endif
+
+int rwnx_dbgfs_register(struct rwnx_hw *rwnx_hw, const char *name);
+void rwnx_dbgfs_unregister(struct rwnx_hw *rwnx_hw);
+#ifdef CONFIG_RWNX_FULLMAC
+void rwnx_dbgfs_register_rc_stat(struct rwnx_hw *rwnx_hw, struct rwnx_sta *sta);
+void rwnx_dbgfs_unregister_rc_stat(struct rwnx_hw *rwnx_hw, struct rwnx_sta *sta);
+#endif
+#else
+
+struct rwnx_debugfs {
+};
+
+static inline int rwnx_dbgfs_register(struct rwnx_hw *rwnx_hw, const char *name) { return 0; }
+static inline void rwnx_dbgfs_unregister(struct rwnx_hw *rwnx_hw) {}
+#ifdef CONFIG_RWNX_FULLMAC
+static inline void rwnx_dbgfs_register_rc_stat(struct rwnx_hw *rwnx_hw, struct rwnx_sta *sta) {}
+static inline void rwnx_dbgfs_unregister_rc_stat(struct rwnx_hw *rwnx_hw, struct rwnx_sta *sta) {}
+#endif
+#endif /* CONFIG_RWNX_DEBUGFS */
+
+
+#endif /* _RWNX_DEBUGFS_H_ */
diff --git a/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_defs.h b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_defs.h
new file mode 100644
index 000000000000..e1a75a3d742b
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_defs.h
@@ -0,0 +1,767 @@
+/**
+ ******************************************************************************
+ *
+ * @file rwnx_defs.h
+ *
+ * @brief Main driver structure declarations for fullmac driver
+ *
+ * Copyright (C) RivieraWaves 2012-2019
+ *
+ ******************************************************************************
+ */
+
+#ifndef _RWNX_DEFS_H_
+#define _RWNX_DEFS_H_
+
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/dmapool.h>
+#include <linux/skbuff.h>
+#include <net/cfg80211.h>
+#include <linux/slab.h>
+
+#include "rwnx_mod_params.h"
+#include "rwnx_debugfs.h"
+#include "rwnx_tx.h"
+#include "rwnx_rx.h"
+#include "rwnx_radar.h"
+#include "rwnx_utils.h"
+#include "rwnx_mu_group.h"
+#include "rwnx_platform.h"
+#include "rwnx_cmds.h"
+#include "rwnx_compat.h"
+
+#ifdef AICWF_SDIO_SUPPORT
+#include "aicwf_sdio.h"
+#include "sdio_host.h"
+#endif
+
+#ifdef AICWF_USB_SUPPORT
+#include "usb_host.h"
+#endif
+
+#ifdef CONFIG_BR_SUPPORT
+#include "aic_br_ext.h"
+#endif /* CONFIG_BR_SUPPORT */
+
+#define WPI_HDR_LEN 18
+#define WPI_PN_LEN 16
+#define WPI_PN_OFST 2
+#define WPI_MIC_LEN 16
+#define WPI_KEY_LEN 32
+#define WPI_SUBKEY_LEN 16 // WPI key is actually two 16bytes key
+
+#define LEGACY_PS_ID 0
+#define UAPSD_ID 1
+
+#define PS_SP_INTERRUPTED 255
+#define MAC_ADDR_LEN 6
+
+
+#if LINUX_VERSION_CODE >= HIGH_KERNEL_VERSION
+#define IEEE80211_MAX_AMPDU_BUF IEEE80211_MAX_AMPDU_BUF_HE
+#define IEEE80211_HE_PHY_CAP6_TRIG_MU_BEAMFORMER_FB IEEE80211_HE_PHY_CAP6_TRIG_MU_BEAMFORMING_PARTIAL_BW_FB
+#define IEEE80211_HE_PHY_CAP6_TRIG_SU_BEAMFORMER_FB IEEE80211_HE_PHY_CAP6_TRIG_SU_BEAMFORMING_FB
+#define IEEE80211_HE_PHY_CAP3_RX_HE_MU_PPDU_FROM_NON_AP_STA IEEE80211_HE_PHY_CAP3_RX_PARTIAL_BW_SU_IN_20MHZ_MU
+#endif
+
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 5, 0) || defined(CONFIG_VHT_FOR_OLD_KERNEL)
+enum nl80211_ac {
+ NL80211_AC_VO,
+ NL80211_AC_VI,
+ NL80211_AC_BE,
+ NL80211_AC_BK,
+ NL80211_NUM_ACS
+};
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0) || defined(CONFIG_VHT_FOR_OLD_KERNEL)
+struct ieee80211_vht_operation {
+ u8 vht_op_info_chwidth;
+ u8 vht_op_info_chan_center_freq_seg1_idx;
+ u8 vht_op_info_chan_center_freq_seg2_idx;
+ __le16 vht_basic_mcs_set;
+} __packed;
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 7, 0)
+#define NL80211_IFTYPE_P2P_DEVICE 10
+#define IEEE80211_RADIOTAP_AMPDU_STATUS 20
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 8, 0) || defined(CONFIG_VHT_FOR_OLD_KERNEL)
+#define IEEE80211_RADIOTAP_VHT 21
+#define IEEE80211_RADIOTAP_VHT_KNOWN_GI 0x0004
+#define IEEE80211_RADIOTAP_VHT_KNOWN_BANDWIDTH 0x0040
+
+#define IEEE80211_RADIOTAP_VHT_FLAG_STBC 0x01
+#define IEEE80211_RADIOTAP_VHT_FLAG_SGI 0x04
+
+#define NL80211_FEATURE_CELL_BASE_REG_HINTS 1 << 3
+#define NL80211_FEATURE_P2P_DEVICE_NEEDS_CHANNEL 1 << 4
+#define NL80211_FEATURE_SAE 1 << 5
+#define NL80211_FEATURE_LOW_PRIORITY_SCAN 1 << 6
+#define NL80211_FEATURE_SCAN_FLUSH 1 << 7
+#define NL80211_FEATURE_AP_SCAN 1 << 8
+#define NL80211_FEATURE_VIF_TXPOWER 1 << 9
+#define NL80211_FEATURE_NEED_OBSS_SCAN 1 << 10
+#define NL80211_FEATURE_P2P_GO_CTWIN 1 << 11
+#define NL80211_FEATURE_P2P_GO_OPPPS 1 << 12
+
+/* 802.11ac VHT Capabilities */
+#define IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_3895 0x00000000
+#define IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991 0x00000001
+#define IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454 0x00000002
+#define IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ 0x00000004
+#define IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ 0x00000008
+#define IEEE80211_VHT_CAP_RXLDPC 0x00000010
+#define IEEE80211_VHT_CAP_SHORT_GI_80 0x00000020
+#define IEEE80211_VHT_CAP_SHORT_GI_160 0x00000040
+#define IEEE80211_VHT_CAP_TXSTBC 0x00000080
+#define IEEE80211_VHT_CAP_RXSTBC_1 0x00000100
+#define IEEE80211_VHT_CAP_RXSTBC_2 0x00000200
+#define IEEE80211_VHT_CAP_RXSTBC_3 0x00000300
+#define IEEE80211_VHT_CAP_RXSTBC_4 0x00000400
+#define IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE 0x00000800
+#define IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE 0x00001000
+#define IEEE80211_VHT_CAP_BEAMFORMER_ANTENNAS_MAX 0x00006000
+#define IEEE80211_VHT_CAP_SOUNDING_DIMENTION_MAX 0x00030000
+#define IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE 0x00080000
+#define IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE 0x00100000
+#define IEEE80211_VHT_CAP_VHT_TXOP_PS 0x00200000
+#define IEEE80211_VHT_CAP_HTC_VHT 0x00400000
+#define IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT 23
+#define IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK \
+ (7 << IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT)
+#define IEEE80211_VHT_CAP_VHT_LINK_ADAPTATION_VHT_UNSOL_MFB 0x08000000
+#define IEEE80211_VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB 0x0c000000
+#define IEEE80211_VHT_CAP_RX_ANTENNA_PATTERN 0x10000000
+#define IEEE80211_VHT_CAP_TX_ANTENNA_PATTERN 0x20000000
+
+enum ieee80211_vht_mcs_support {
+ IEEE80211_VHT_MCS_SUPPORT_0_7 = 0,
+ IEEE80211_VHT_MCS_SUPPORT_0_8 = 1,
+ IEEE80211_VHT_MCS_SUPPORT_0_9 = 2,
+ IEEE80211_VHT_MCS_NOT_SUPPORTED = 3,
+};
+
+enum nl80211_chan_width {
+ NL80211_CHAN_WIDTH_20_NOHT,
+ NL80211_CHAN_WIDTH_20,
+ NL80211_CHAN_WIDTH_40,
+ NL80211_CHAN_WIDTH_80,
+ NL80211_CHAN_WIDTH_80P80,
+ NL80211_CHAN_WIDTH_160,
+};
+
+struct cfg80211_chan_def {
+ struct ieee80211_channel *chan;
+ enum nl80211_chan_width width;
+ u32 center_freq1;
+ u32 center_freq2;
+};
+
+enum nl80211_mesh_power_mode {
+ NL80211_MESH_POWER_UNKNOWN,
+ NL80211_MESH_POWER_ACTIVE,
+ NL80211_MESH_POWER_LIGHT_SLEEP,
+ NL80211_MESH_POWER_DEEP_SLEEP,
+ __NL80211_MESH_POWER_AFTER_LAST,
+ NL80211_MESH_POWER_MAX = __NL80211_MESH_POWER_AFTER_LAST - 1
+};
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 9, 0)
+#define NL80211_MESHCONF_POWER_MODE 26
+
+/*
+ * TDLS capabililites to be enabled in the 5th byte of the
+ * @WLAN_EID_EXT_CAPABILITY information element
+ */
+#define WLAN_EXT_CAPA5_TDLS_ENABLED BIT(5)
+#define WLAN_EXT_CAPA5_TDLS_PROHIBITED BIT(6)
+
+#define WLAN_EXT_CAPA8_OPMODE_NOTIF BIT(6)
+
+/* TDLS specific payload type in the LLC/SNAP header */
+#define WLAN_TDLS_SNAP_RFTYPE 0x2
+
+#endif
+
+/**
+ * struct rwnx_bcn - Information of the beacon in used (AP mode)
+ *
+ * @head: head portion of beacon (before TIM IE)
+ * @tail: tail portion of beacon (after TIM IE)
+ * @ies: extra IEs (not used ?)
+ * @head_len: length of head data
+ * @tail_len: length of tail data
+ * @ies_len: length of extra IEs data
+ * @tim_len: length of TIM IE
+ * @len: Total beacon len (head + tim + tail + extra)
+ * @dtim: dtim period
+ */
+struct rwnx_bcn {
+ u8 *head;
+ u8 *tail;
+ u8 *ies;
+ size_t head_len;
+ size_t tail_len;
+ size_t ies_len;
+ size_t tim_len;
+ size_t len;
+ u8 dtim;
+};
+
+/**
+ * struct rwnx_key - Key information
+ *
+ * @hw_idx: Idx of the key from hardware point of view
+ */
+struct rwnx_key {
+ u8 hw_idx;
+};
+
+/**
+ * Structure containing information about a Mesh Path
+ */
+struct rwnx_mesh_path {
+ struct list_head list; /* For rwnx_vif.mesh_paths */
+ u8 path_idx; /* Path Index */
+ struct mac_addr tgt_mac_addr; /* Target MAC Address */
+ struct rwnx_sta *p_nhop_sta; /* Pointer to the Next Hop STA */
+};
+
+struct rwnx_mesh_proxy {
+ struct list_head list; /* For rwnx_vif.mesh_proxy */
+ struct mac_addr ext_sta_addr; /* Address of the External STA */
+ struct mac_addr proxy_addr; /* Proxy MAC Address */
+ bool local; /* Indicate if interface is a proxy for the device */
+};
+
+/**
+ * struct rwnx_csa - Information for CSA (Channel Switch Announcement)
+ *
+ * @vif: Pointer to the vif doing the CSA
+ * @bcn: Beacon to use after CSA
+ * @elem: IPC buffer to send the new beacon to the fw
+ * @chandef: defines the channel to use after the switch
+ * @count: Current csa counter
+ * @status: Status of the CSA at fw level
+ * @ch_idx: Index of the new channel context
+ * @work: work scheduled at the end of CSA
+ */
+struct rwnx_csa {
+ struct rwnx_vif *vif;
+ struct rwnx_bcn bcn;
+ struct rwnx_ipc_elem_var elem;
+ struct cfg80211_chan_def chandef;
+ int count;
+ int status;
+ int ch_idx;
+ struct work_struct work;
+};
+
+struct apm_probe_sta {
+ u8 sta_mac_addr[6];
+ u8 vif_idx;
+ u64 probe_id;
+ struct work_struct apmprobestaWork;
+ struct workqueue_struct *apmprobesta_wq;
+};
+
+/// Possible States of the TDLS link.
+enum tdls_status_tag {
+ /// TDLS link is not active (no TDLS peer connected)
+ TDLS_LINK_IDLE,
+ /// TDLS Setup Request transmitted
+ TDLS_SETUP_REQ_TX,
+ /// TDLS Setup Response transmitted
+ TDLS_SETUP_RSP_TX,
+ /// TDLS link is active (TDLS peer connected)
+ TDLS_LINK_ACTIVE,
+ /// TDLS Max Number of states.
+ TDLS_STATE_MAX
+};
+
+/*
+ * Structure used to save information relative to the TDLS peer.
+ * This is also linked within the rwnx_hw vifs list.
+ *
+ */
+struct rwnx_tdls {
+ bool active; /* Indicate if TDLS link is active */
+ bool initiator; /* Indicate if TDLS peer is the TDLS initiator */
+ bool chsw_en; /* Indicate if channel switch is enabled */
+ u8 last_tid; /* TID of the latest MPDU transmitted over the
+ TDLS direct link to the TDLS STA */
+ u16 last_sn; /* Sequence number of the latest MPDU transmitted
+ over the TDLS direct link to the TDLS STA */
+ bool ps_on; /* Indicate if the power save is enabled on the
+ TDLS STA */
+ bool chsw_allowed; /* Indicate if TDLS channel switch is allowed */
+};
+
+
+/**
+ * enum rwnx_ap_flags - AP flags
+ *
+ * @RWNX_AP_ISOLATE Isolate clients (i.e. Don't brige packets transmitted by
+ * one client for another one)
+ */
+enum rwnx_ap_flags {
+ RWNX_AP_ISOLATE = BIT(0),
+};
+
+/*
+ * Structure used to save information relative to the managed interfaces.
+ * This is also linked within the rwnx_hw vifs list.
+ *
+ */
+struct rwnx_vif {
+ struct list_head list;
+ struct rwnx_hw *rwnx_hw;
+ struct wireless_dev wdev;
+ struct net_device *ndev;
+ struct net_device_stats net_stats;
+ struct rwnx_key key[6];
+ atomic_t drv_conn_state;
+ u8 drv_vif_index; /* Identifier of the VIF in driver */
+ u8 vif_index; /* Identifier of the station in FW */
+ u8 ch_index; /* Channel context identifier */
+ bool up; /* Indicate if associated netdev is up
+ (i.e. Interface is created at fw level) */
+ bool use_4addr; /* Should we use 4addresses mode */
+ bool is_resending; /* Indicate if a frame is being resent on this interface */
+ bool user_mpm; /* In case of Mesh Point VIF, indicate if MPM is handled by userspace */
+ bool roc_tdls; /* Indicate if the ROC has been called by a
+ TDLS station */
+ u8 tdls_status; /* Status of the TDLS link */
+ bool tdls_chsw_prohibited; /* Indicate if TDLS Channel Switch is prohibited */
+ bool wep_enabled; /* 1 if WEP is enabled */
+ bool wep_auth_err; /* 1 if auth status code is not supported auth alg when WEP enabled */
+ enum nl80211_auth_type last_auth_type; /* Authentication type (algorithm) sent in the last connection
+ when WEP enabled */
+ union
+ {
+ struct
+ {
+ struct rwnx_sta *ap; /* Pointer to the peer STA entry allocated for
+ the AP */
+ struct rwnx_sta *tdls_sta; /* Pointer to the TDLS station */
+ bool external_auth; /* Indicate if external authentication is in progress */
+ u8 group_cipher_type;
+ u8 paired_cipher_type;
+ //connected network info start
+ char ssid[33];//ssid max is 32, but this has one spare for '\0'
+ int ssid_len;
+ u8 bssid[ETH_ALEN];
+ //connected network info end
+ } sta;
+ struct
+ {
+ u16 flags; /* see rwnx_ap_flags */
+ struct list_head sta_list; /* List of STA connected to the AP */
+ struct rwnx_bcn bcn; /* beacon */
+ u8 bcmc_index; /* Index of the BCMC sta to use */
+ #if (defined CONFIG_HE_FOR_OLD_KERNEL) || (defined CONFIG_VHT_FOR_OLD_KERNEL)
+ u8 aic_index;
+ #endif
+ struct rwnx_csa *csa;
+
+ struct list_head mpath_list; /* List of Mesh Paths used on this interface */
+ struct list_head proxy_list; /* List of Proxies Information used on this interface */
+ bool create_path; /* Indicate if we are waiting for a MESH_CREATE_PATH_CFM
+ message */
+ int generation; /* Increased each time the list of Mesh Paths is updated */
+ enum nl80211_mesh_power_mode mesh_pm; /* mesh power save mode currently set in firmware */
+ enum nl80211_mesh_power_mode next_mesh_pm; /* mesh power save mode for next peer */
+ } ap;
+ struct
+ {
+ struct rwnx_vif *master; /* pointer on master interface */
+ struct rwnx_sta *sta_4a;
+ } ap_vlan;
+ };
+
+ u8_l key_has_add;
+ u8_l is_p2p_vif;
+ struct apm_probe_sta sta_probe;
+
+ #ifdef CONFIG_BR_SUPPORT
+ spinlock_t br_ext_lock;
+ /* unsigned int macclone_completed; */
+ struct nat25_network_db_entry *nethash[NAT25_HASH_SIZE];
+ int pppoe_connection_in_progress;
+ unsigned char pppoe_addr[MACADDRLEN];
+ unsigned char scdb_mac[MACADDRLEN];
+ unsigned char scdb_ip[4];
+ struct nat25_network_db_entry *scdb_entry;
+ unsigned char br_mac[MACADDRLEN];
+ unsigned char br_ip[4];
+
+ struct br_ext_info ethBrExtInfo;
+ #endif /* CONFIG_BR_SUPPORT */
+
+};
+
+#define RWNX_VIF_TYPE(rwnx_vif) (rwnx_vif->wdev.iftype)
+
+/**
+ * Structure used to store information relative to PS mode.
+ *
+ * @active: True when the sta is in PS mode.
+ * If false, other values should be ignored
+ * @pkt_ready: Number of packets buffered for the sta in drv's txq
+ * (1 counter for Legacy PS and 1 for U-APSD)
+ * @sp_cnt: Number of packets that remain to be pushed in the service period.
+ * 0 means that no service period is in progress
+ * (1 counter for Legacy PS and 1 for U-APSD)
+ */
+struct rwnx_sta_ps {
+ bool active;
+ u16 pkt_ready[2];
+ u16 sp_cnt[2];
+};
+
+/**
+ * struct rwnx_rx_rate_stats - Store statistics for RX rates
+ *
+ * @table: Table indicating how many frame has been receive which each
+ * rate index. Rate index is the same as the one used by RC algo for TX
+ * @size: Size of the table array
+ * @cpt: number of frames received
+ */
+struct rwnx_rx_rate_stats {
+ int *table;
+ int size;
+ int cpt;
+ int rate_cnt;
+};
+
+/**
+ * struct rwnx_sta_stats - Structure Used to store statistics specific to a STA
+ *
+ * @last_rx: Hardware vector of the last received frame
+ * @rx_rate: Statistics of the received rates
+ */
+struct rwnx_sta_stats {
+//#ifdef CONFIG_RWNX_DEBUGFS
+ struct hw_vect last_rx;
+ struct rwnx_rx_rate_stats rx_rate;
+//#endif
+};
+
+#if (defined CONFIG_HE_FOR_OLD_KERNEL) || (defined CONFIG_VHT_FOR_OLD_KERNEL)
+struct aic_sta {
+ u8 sta_idx; /* Identifier of the station */
+ bool he; /* Flag indicating if the station supports HE */
+ bool vht; /* Flag indicating if the station supports VHT */
+};
+#endif
+
+/*
+ * Structure used to save information relative to the managed stations.
+ */
+struct rwnx_sta {
+ struct list_head list;
+ u16 aid; /* association ID */
+ u8 sta_idx; /* Identifier of the station */
+ u8 vif_idx; /* Identifier of the VIF (fw id) the station
+ belongs to */
+ u8 vlan_idx; /* Identifier of the VLAN VIF (fw id) the station
+ belongs to (= vif_idx if no vlan in used) */
+ enum nl80211_band band; /* Band */
+ enum nl80211_chan_width width; /* Channel width */
+ u16 center_freq; /* Center frequency */
+ u32 center_freq1; /* Center frequency 1 */
+ u32 center_freq2; /* Center frequency 2 */
+ u8 ch_idx; /* Identifier of the channel
+ context the station belongs to */
+ bool qos; /* Flag indicating if the station
+ supports QoS */
+ u8 acm; /* Bitfield indicating which queues
+ have AC mandatory */
+ u16 uapsd_tids; /* Bitfield indicating which tids are subject to
+ UAPSD */
+ u8 mac_addr[ETH_ALEN]; /* MAC address of the station */
+ struct rwnx_key key;
+ bool valid; /* Flag indicating if the entry is valid */
+ struct rwnx_sta_ps ps; /* Information when STA is in PS (AP only) */
+#ifdef CONFIG_RWNX_BFMER
+ struct rwnx_bfmer_report *bfm_report; /* Beamforming report to be used for
+ VHT TX Beamforming */
+#ifdef CONFIG_RWNX_MUMIMO_TX
+ struct rwnx_sta_group_info group_info; /* MU grouping information for the STA */
+#endif /* CONFIG_RWNX_MUMIMO_TX */
+#endif /* CONFIG_RWNX_BFMER */
+
+ bool ht; /* Flag indicating if the station
+ supports HT */
+ bool vht; /* Flag indicating if the station
+ supports VHT */
+ u32 ac_param[AC_MAX]; /* EDCA parameters */
+ struct rwnx_tdls tdls; /* TDLS station information */
+ struct rwnx_sta_stats stats;
+ enum nl80211_mesh_power_mode mesh_pm; /* link-specific mesh power save mode */
+};
+
+static inline const u8 *rwnx_sta_addr(struct rwnx_sta *rwnx_sta) {
+ return rwnx_sta->mac_addr;
+}
+
+#ifdef CONFIG_RWNX_SPLIT_TX_BUF
+struct rwnx_amsdu_stats {
+ int done;
+ int failed;
+};
+#endif
+
+struct rwnx_stats {
+ int cfm_balance[NX_TXQ_CNT];
+ unsigned long last_rx, last_tx; /* jiffies */
+ int ampdus_tx[IEEE80211_MAX_AMPDU_BUF];
+ int ampdus_rx[IEEE80211_MAX_AMPDU_BUF];
+ int ampdus_rx_map[4];
+ int ampdus_rx_miss;
+#ifdef CONFIG_RWNX_SPLIT_TX_BUF
+ struct rwnx_amsdu_stats amsdus[NX_TX_PAYLOAD_MAX];
+#endif
+ int amsdus_rx[64];
+};
+
+struct rwnx_sec_phy_chan {
+ u16 prim20_freq;
+ u16 center_freq1;
+ u16 center_freq2;
+ enum nl80211_band band;
+ u8 type;
+};
+
+/* Structure that will contains all RoC information received from cfg80211 */
+struct rwnx_roc_elem {
+ struct wireless_dev *wdev;
+ struct ieee80211_channel *chan;
+ unsigned int duration;
+ /* Used to avoid call of CFG80211 callback upon expiration of RoC */
+ bool mgmt_roc;
+ /* Indicate if we have switch on the RoC channel */
+ bool on_chan;
+};
+
+/* Structure containing channel survey information received from MAC */
+struct rwnx_survey_info {
+ // Filled
+ u32 filled;
+ // Amount of time in ms the radio spent on the channel
+ u32 chan_time_ms;
+ // Amount of time the primary channel was sensed busy
+ u32 chan_time_busy_ms;
+ // Noise in dbm
+ s8 noise_dbm;
+};
+
+#define RWNX_CH_NOT_SET 0xFF
+#define RWNX_INVALID_VIF 0xFF
+#define RWNX_INVALID_STA 0xFF
+
+/* Structure containing channel context information */
+struct rwnx_chanctx {
+ struct cfg80211_chan_def chan_def; /* channel description */
+ u8 count; /* number of vif using this ctxt */
+};
+
+/**
+ * rwnx_phy_info - Phy information
+ *
+ * @phy_cnt: Number of phy interface
+ * @cfg: Configuration send to firmware
+ * @sec_chan: Channel configuration of the second phy interface (if phy_cnt > 1)
+ * @limit_bw: Set to true to limit BW on requested channel. Only set to use
+ * VHT with old radio that don't support 80MHz (deprecated)
+ */
+struct rwnx_phy_info {
+ u8 cnt;
+ struct phy_cfg_tag cfg;
+ struct rwnx_sec_phy_chan sec_chan;
+ bool limit_bw;
+};
+
+/* rwnx driver status */
+
+enum rwnx_drv_connect_status {
+ RWNX_DRV_STATUS_DISCONNECTED = 0,
+ RWNX_DRV_STATUS_DISCONNECTING,
+ RWNX_DRV_STATUS_CONNECTING,
+ RWNX_DRV_STATUS_CONNECTED,
+};
+
+
+struct sta_tx_flowctrl {
+ atomic_t tx_pending_cnt;
+ u8 flowctrl;
+};
+
+struct rwnx_hw {
+ struct rwnx_mod_params *mod_params;
+ struct device *dev;
+#ifdef AICWF_SDIO_SUPPORT
+ struct aic_sdio_dev *sdiodev;
+#endif
+#ifdef AICWF_USB_SUPPORT
+ struct aic_usb_dev *usbdev;
+#endif
+ struct wiphy *wiphy;
+ struct list_head vifs;
+ struct rwnx_vif *vif_table[NX_VIRT_DEV_MAX + NX_REMOTE_STA_MAX]; /* indexed with fw id */
+ struct rwnx_sta sta_table[NX_REMOTE_STA_MAX + NX_VIRT_DEV_MAX];
+ #ifdef CONFIG_HE_FOR_OLD_KERNEL
+ struct aic_sta aic_table[NX_REMOTE_STA_MAX + NX_VIRT_DEV_MAX];
+ #endif
+ struct rwnx_survey_info survey[SCAN_CHANNEL_MAX];
+ struct cfg80211_scan_request *scan_request;
+#ifdef CONFIG_SCHED_SCAN
+ struct cfg80211_sched_scan_request *sched_scan_req;
+#endif
+ struct rwnx_chanctx chanctx_table[NX_CHAN_CTXT_CNT];
+ u8 cur_chanctx;
+
+ u8 monitor_vif; /* FW id of the monitor interface, RWNX_INVALID_VIF if no monitor vif at fw level */
+
+ /* RoC Management */
+ struct rwnx_roc_elem *roc_elem; /* Information provided by cfg80211 in its remain on channel request */
+ u32 roc_cookie_cnt; /* Counter used to identify RoC request sent by cfg80211 */
+
+ struct rwnx_cmd_mgr *cmd_mgr;
+
+ unsigned long drv_flags;
+ struct rwnx_plat *plat;
+
+ spinlock_t tx_lock;
+ spinlock_t cb_lock;
+ struct mutex mutex; /* per-device perimeter lock */
+
+ struct tasklet_struct task;
+ struct mm_version_cfm version_cfm; /* Lower layers versions - obtained via MM_VERSION_REQ */
+
+ u32 tcp_pacing_shift;
+
+ /* IPC */
+ struct ipc_host_env_tag *ipc_env;
+#ifdef AICWF_SDIO_SUPPORT
+ struct sdio_host_env_tag sdio_env;
+#endif
+#ifdef AICWF_USB_SUPPORT
+ struct usb_host_env_tag usb_env;
+#endif
+
+ struct rwnx_ipc_elem_pool e2amsgs_pool;
+ struct rwnx_ipc_elem_pool dbgmsgs_pool;
+ struct rwnx_ipc_elem_pool e2aradars_pool;
+ struct rwnx_ipc_elem_var pattern_elem;
+ struct rwnx_ipc_dbgdump_elem dbgdump_elem;
+ struct rwnx_ipc_elem_pool e2arxdesc_pool;
+ struct rwnx_ipc_skb_elem *e2aunsuprxvec_elems;
+ //struct rwnx_ipc_rxbuf_elems rxbuf_elems;
+ struct rwnx_ipc_elem_var scan_ie;
+
+ struct kmem_cache *sw_txhdr_cache;
+
+ struct rwnx_debugfs debugfs;
+ struct rwnx_stats stats;
+
+#ifdef CONFIG_PREALLOC_TXQ
+ struct rwnx_txq *txq;
+#else
+ struct rwnx_txq txq[NX_NB_TXQ];
+#endif
+ struct rwnx_hwq hwq[NX_TXQ_CNT];
+
+ u8 avail_idx_map;
+ u8 vif_started;
+ bool adding_sta;
+ struct rwnx_phy_info phy;
+
+ struct rwnx_radar radar;
+
+ /* extended capabilities supported */
+ u8 ext_capa[8];
+
+#ifdef CONFIG_RWNX_MUMIMO_TX
+ struct rwnx_mu_info mu;
+#endif
+ u8 is_p2p_alive;
+ u8 is_p2p_connected;
+ struct timer_list p2p_alive_timer;
+ struct rwnx_vif *p2p_dev_vif;
+ atomic_t p2p_alive_timer_count;
+ bool band_5g_support;
+ bool fwlog_en;
+
+ struct work_struct apmStalossWork;
+ struct workqueue_struct *apmStaloss_wq;
+ u8 apm_vif_idx;
+ u8 sta_mac_addr[6];
+#ifdef CONFIG_SCHED_SCAN
+ bool is_sched_scan;
+#endif//CONFIG_SCHED_SCAN
+
+ struct sta_tx_flowctrl sta_flowctrl[NX_REMOTE_STA_MAX];
+#if 0
+ bool he_flag;
+#endif
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0)
+ struct mac_chan_op ap_chan;
+ struct ieee80211_channel set_chan;
+#endif
+#ifdef CONFIG_VHT_FOR_OLD_KERNEL
+ struct ieee80211_sta_vht_cap vht_cap_2G;
+ struct ieee80211_sta_vht_cap vht_cap_5G;
+#endif
+
+#ifdef CONFIG_USE_WIRELESS_EXT
+ bool wext_scan;
+ struct completion wext_scan_com;
+ struct list_head wext_scanre_list;
+ char wext_essid[32];
+ int support_freqs[SCAN_CHANNEL_MAX];
+ int support_freqs_number;
+#endif
+};
+
+u8 *rwnx_build_bcn(struct rwnx_bcn *bcn, struct cfg80211_beacon_data *new);
+
+void rwnx_chanctx_link(struct rwnx_vif *vif, u8 idx,
+ struct cfg80211_chan_def *chandef);
+void rwnx_chanctx_unlink(struct rwnx_vif *vif);
+int rwnx_chanctx_valid(struct rwnx_hw *rwnx_hw, u8 idx);
+
+extern u8 chip_id;
+static inline bool is_multicast_sta(int sta_idx)
+{
+
+ if((g_rwnx_plat->usbdev->chipid == PRODUCT_ID_AIC8801) ||
+ ((g_rwnx_plat->usbdev->chipid == PRODUCT_ID_AIC8800DC ||
+ g_rwnx_plat->usbdev->chipid == PRODUCT_ID_AIC8800DW) && chip_id < 3)){
+ return (sta_idx >= NX_REMOTE_STA_MAX_FOR_OLD_IC);
+ }else{
+ return (sta_idx >= NX_REMOTE_STA_MAX);
+ }
+
+}
+struct rwnx_sta *rwnx_get_sta(struct rwnx_hw *rwnx_hw, const u8 *mac_addr);
+
+static inline uint8_t master_vif_idx(struct rwnx_vif *vif)
+{
+ if (unlikely(vif->wdev.iftype == NL80211_IFTYPE_AP_VLAN)) {
+ return vif->ap_vlan.master->vif_index;
+ } else {
+ return vif->vif_index;
+ }
+}
+
+void rwnx_external_auth_enable(struct rwnx_vif *vif);
+void rwnx_external_auth_disable(struct rwnx_vif *vif);
+
+#endif /* _RWNX_DEFS_H_ */
diff --git a/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_dini.c b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_dini.c
new file mode 100644
index 000000000000..03e1ced322c5
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_dini.c
@@ -0,0 +1,294 @@
+/**
+ ******************************************************************************
+ *
+ * @file rwnx_dini.c - Add support for dini platform
+ *
+ * Copyright (C) RivieraWaves 2012-2019
+ *
+ ******************************************************************************
+ */
+
+#include "rwnx_dini.h"
+#include "rwnx_defs.h"
+#include "rwnx_irqs.h"
+#include "reg_access.h"
+
+/* Config FPGA is accessed via bar0 */
+#define CFPGA_DMA0_CTRL_REG 0x02C
+#define CFPGA_DMA1_CTRL_REG 0x04C
+#define CFPGA_DMA2_CTRL_REG 0x06C
+#define CFPGA_UINTR_SRC_REG 0x0E8
+#define CFPGA_UINTR_MASK_REG 0x0EC
+#define CFPGA_BAR4_HIADDR_REG 0x100
+#define CFPGA_BAR4_LOADDR_REG 0x104
+#define CFPGA_BAR4_LOADDR_MASK_REG 0x110
+#define CFPGA_BAR_TOUT 0x120
+
+#define CFPGA_DMA_CTRL_ENABLE 0x00001400
+#define CFPGA_DMA_CTRL_DISABLE 0x00001000
+#define CFPGA_DMA_CTRL_CLEAR 0x00001800
+#define CFPGA_DMA_CTRL_REREAD_TIME_MASK (BIT(10) - 1)
+
+#define CFPGA_BAR4_LOADDR_MASK_MAX 0xFF000000
+
+#define CFPGA_PCIEX_IT 0x00000001
+#define CFPGA_ALL_ITS 0x0000000F
+
+/* Programmable BAR4 Window start address */
+#define CPU_RAM_WINDOW_HIGH 0x00000000
+#define CPU_RAM_WINDOW_LOW 0x00000000
+#define AHB_BRIDGE_WINDOW_HIGH 0x00000000
+#define AHB_BRIDGE_WINDOW_LOW 0x60000000
+
+struct rwnx_dini
+{
+ u8 *pci_bar0_vaddr;
+ u8 *pci_bar4_vaddr;
+};
+
+static const u32 mv_cfg_fpga_dma_ctrl_regs[] = {
+ CFPGA_DMA0_CTRL_REG,
+ CFPGA_DMA1_CTRL_REG,
+ CFPGA_DMA2_CTRL_REG
+};
+
+/* This also clears running transactions */
+static void dini_dma_on(struct rwnx_dini *rwnx_dini)
+{
+ int i;
+ u32 reread_time;
+ volatile void *reg;
+
+ for (i = 0; i < ARRAY_SIZE(mv_cfg_fpga_dma_ctrl_regs); i++) {
+ reg = rwnx_dini->pci_bar0_vaddr + mv_cfg_fpga_dma_ctrl_regs[i];
+ reread_time = readl(reg) & CFPGA_DMA_CTRL_REREAD_TIME_MASK;
+
+ writel(CFPGA_DMA_CTRL_CLEAR | reread_time, reg);
+ writel(CFPGA_DMA_CTRL_ENABLE | reread_time, reg);
+ }
+}
+
+/* This also clears running transactions */
+static void dini_dma_off(struct rwnx_dini *rwnx_dini)
+{
+ int i;
+ u32 reread_time;
+ volatile void *reg;
+
+ for (i = 0; i < ARRAY_SIZE(mv_cfg_fpga_dma_ctrl_regs); i++) {
+ reg = rwnx_dini->pci_bar0_vaddr + mv_cfg_fpga_dma_ctrl_regs[i];
+ reread_time = readl(reg) & CFPGA_DMA_CTRL_REREAD_TIME_MASK;
+
+ writel(CFPGA_DMA_CTRL_DISABLE | reread_time, reg);
+ writel(CFPGA_DMA_CTRL_CLEAR | reread_time, reg);
+ }
+}
+
+
+/* Configure address range for BAR4.
+ * By default BAR4_LOADDR_MASK value is 0xFF000000, then there is no need to
+ * change it because the addresses we need to access are covered by this mask
+ */
+static void dini_set_bar4_win(u32 low, u32 high, struct rwnx_dini *rwnx_dini)
+{
+ writel(low, rwnx_dini->pci_bar0_vaddr + CFPGA_BAR4_LOADDR_REG);
+ writel(high, rwnx_dini->pci_bar0_vaddr + CFPGA_BAR4_HIADDR_REG);
+ writel(CFPGA_BAR4_LOADDR_MASK_MAX,
+ rwnx_dini->pci_bar0_vaddr + CFPGA_BAR4_LOADDR_MASK_REG);
+}
+
+
+/**
+ * Enable User Interrupts of CFPGA that trigger PCIe IRQs on PCIE_10
+ * and request the corresponding IRQ line
+ */
+int rwnx_cfpga_irq_enable(struct rwnx_hw *rwnx_hw)
+{
+ struct rwnx_plat *rwnx_plat = rwnx_hw->plat;
+ struct rwnx_dini *rwnx_dini = (struct rwnx_dini *)rwnx_plat->priv;
+ unsigned int cfpga_uintr_mask;
+ volatile void *reg;
+ int ret;
+
+ /* sched_setscheduler on ONESHOT threaded irq handler for BCNs ? */
+ if ((ret = request_irq(rwnx_hw->plat->pci_dev->irq, rwnx_irq_hdlr, 0,
+ "rwnx", rwnx_hw)))
+ return ret;
+
+ reg = rwnx_dini->pci_bar0_vaddr + CFPGA_UINTR_MASK_REG;
+ cfpga_uintr_mask = readl(reg);
+ writel(cfpga_uintr_mask | CFPGA_PCIEX_IT, reg);
+
+ return ret;
+}
+
+/**
+ * Disable User Interrupts of CFPGA that trigger PCIe IRQs on PCIE_10
+ * and free the corresponding IRQ line
+ */
+int rwnx_cfpga_irq_disable(struct rwnx_hw *rwnx_hw)
+{
+ struct rwnx_plat *rwnx_plat = rwnx_hw->plat;
+ struct rwnx_dini *rwnx_dini = (struct rwnx_dini *)rwnx_plat->priv;
+ unsigned int cfpga_uintr_mask;
+ volatile void *reg;
+
+ reg = rwnx_dini->pci_bar0_vaddr + CFPGA_UINTR_MASK_REG;
+ cfpga_uintr_mask = readl(reg);
+ writel(cfpga_uintr_mask & ~CFPGA_PCIEX_IT, reg);
+
+ free_irq(rwnx_hw->plat->pci_dev->irq, rwnx_hw);
+
+ return 0;
+}
+
+static int rwnx_dini_platform_enable(struct rwnx_hw *rwnx_hw)
+{
+ struct rwnx_plat *rwnx_plat = rwnx_hw->plat;
+ struct rwnx_dini *rwnx_dini = (struct rwnx_dini *)rwnx_plat->priv;
+
+#ifdef CONFIG_RWNX_SDM
+ writel(0x0000FFFF, rwnx_dini->pci_bar0_vaddr + CFPGA_BAR_TOUT);
+#endif
+
+ dini_dma_on(rwnx_dini);
+ return rwnx_cfpga_irq_enable(rwnx_hw);
+}
+
+static int rwnx_dini_platform_disable(struct rwnx_hw *rwnx_hw)
+{
+ struct rwnx_plat *rwnx_plat = rwnx_hw->plat;
+ struct rwnx_dini *rwnx_dini = (struct rwnx_dini *)rwnx_plat->priv;
+ int ret;
+
+ ret = rwnx_cfpga_irq_disable(rwnx_hw);
+ dini_dma_off(rwnx_dini);
+ return ret;
+}
+
+static void rwnx_dini_platform_deinit(struct rwnx_plat *rwnx_plat)
+{
+ struct rwnx_dini *rwnx_dini = (struct rwnx_dini *)rwnx_plat->priv;
+
+ pci_disable_device(rwnx_plat->pci_dev);
+ iounmap(rwnx_dini->pci_bar0_vaddr);
+ iounmap(rwnx_dini->pci_bar4_vaddr);
+ pci_release_regions(rwnx_plat->pci_dev);
+
+ kfree(rwnx_plat);
+}
+
+static u8* rwnx_dini_get_address(struct rwnx_plat *rwnx_plat, int addr_name,
+ unsigned int offset)
+{
+ struct rwnx_dini *rwnx_dini = (struct rwnx_dini *)rwnx_plat->priv;
+
+ if (WARN(addr_name >= RWNX_ADDR_MAX, "Invalid address %d", addr_name))
+ return NULL;
+
+ if (addr_name == RWNX_ADDR_CPU)
+ dini_set_bar4_win(CPU_RAM_WINDOW_LOW, CPU_RAM_WINDOW_HIGH, rwnx_dini);
+ else
+ dini_set_bar4_win(AHB_BRIDGE_WINDOW_LOW, AHB_BRIDGE_WINDOW_HIGH, rwnx_dini);
+
+ return rwnx_dini->pci_bar4_vaddr + offset;
+}
+
+static void rwnx_dini_ack_irq(struct rwnx_plat *rwnx_plat)
+{
+ struct rwnx_dini *rwnx_dini = (struct rwnx_dini *)rwnx_plat->priv;
+
+ writel(CFPGA_ALL_ITS, rwnx_dini->pci_bar0_vaddr + CFPGA_UINTR_SRC_REG);
+}
+
+static const u32 rwnx_dini_config_reg[] = {
+ NXMAC_DEBUG_PORT_SEL_ADDR,
+ SYSCTRL_DIAG_CONF_ADDR,
+ RF_V6_DIAGPORT_CONF1_ADDR,
+ RF_v6_PHYDIAG_CONF1_ADDR,
+};
+
+static int rwnx_dini_get_config_reg(struct rwnx_plat *rwnx_plat, const u32 **list)
+{
+ if (!list)
+ return 0;
+
+ *list = rwnx_dini_config_reg;
+ return ARRAY_SIZE(rwnx_dini_config_reg);
+}
+
+/**
+ * rwnx_dini_platform_init - Initialize the DINI platform
+ *
+ * @pci_dev PCI device
+ * @rwnx_plat Pointer on struct rwnx_stat * to be populated
+ *
+ * @return 0 on success, < 0 otherwise
+ *
+ * Allocate and initialize a rwnx_plat structure for the dini platform.
+ */
+int rwnx_dini_platform_init(struct pci_dev *pci_dev, struct rwnx_plat **rwnx_plat)
+{
+ struct rwnx_dini *rwnx_dini;
+ u16 pci_cmd;
+ int ret = 0;
+
+ *rwnx_plat = kzalloc(sizeof(struct rwnx_plat) + sizeof(struct rwnx_dini),
+ GFP_KERNEL);
+ if (!*rwnx_plat)
+ return -ENOMEM;
+
+ rwnx_dini = (struct rwnx_dini *)(*rwnx_plat)->priv;
+
+ /* Hotplug fixups */
+ pci_read_config_word(pci_dev, PCI_COMMAND, &pci_cmd);
+ pci_cmd |= PCI_COMMAND_PARITY | PCI_COMMAND_SERR;
+ pci_write_config_word(pci_dev, PCI_COMMAND, pci_cmd);
+ pci_write_config_byte(pci_dev, PCI_CACHE_LINE_SIZE, L1_CACHE_BYTES >> 2);
+
+ if ((ret = pci_enable_device(pci_dev))) {
+ dev_err(&(pci_dev->dev), "pci_enable_device failed\n");
+ goto out_enable;
+ }
+
+ pci_set_master(pci_dev);
+
+ if ((ret = pci_request_regions(pci_dev, KBUILD_MODNAME))) {
+ dev_err(&(pci_dev->dev), "pci_request_regions failed\n");
+ goto out_request;
+ }
+
+ if (!(rwnx_dini->pci_bar0_vaddr = (u8 *)pci_ioremap_bar(pci_dev, 0))) {
+ dev_err(&(pci_dev->dev), "pci_ioremap_bar(%d) failed\n", 0);
+ ret = -ENOMEM;
+ goto out_bar0;
+ }
+ if (!(rwnx_dini->pci_bar4_vaddr = (u8 *)pci_ioremap_bar(pci_dev, 4))) {
+ dev_err(&(pci_dev->dev), "pci_ioremap_bar(%d) failed\n", 4);
+ ret = -ENOMEM;
+ goto out_bar4;
+ }
+
+ (*rwnx_plat)->enable = rwnx_dini_platform_enable;
+ (*rwnx_plat)->disable = rwnx_dini_platform_disable;
+ (*rwnx_plat)->deinit = rwnx_dini_platform_deinit;
+ (*rwnx_plat)->get_address = rwnx_dini_get_address;
+ (*rwnx_plat)->ack_irq = rwnx_dini_ack_irq;
+ (*rwnx_plat)->get_config_reg = rwnx_dini_get_config_reg;
+
+#ifdef CONFIG_RWNX_SDM
+ writel(0x0000FFFF, rwnx_dini->pci_bar0_vaddr + CFPGA_BAR_TOUT);
+#endif
+
+ return 0;
+
+ out_bar4:
+ iounmap(rwnx_dini->pci_bar0_vaddr);
+ out_bar0:
+ pci_release_regions(pci_dev);
+ out_request:
+ pci_disable_device(pci_dev);
+ out_enable:
+ kfree(*rwnx_plat);
+ return ret;
+}
diff --git a/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_dini.h b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_dini.h
new file mode 100644
index 000000000000..2d8cf35851ec
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_dini.h
@@ -0,0 +1,20 @@
+/**
+ ****************************************************************************************
+ *
+ * @file rwnx_dini.h
+ *
+ * Copyright (C) RivieraWaves 2012-2019
+ *
+ ******************************************************************************
+ */
+
+#ifndef _RWNX_DINI_H_
+#define _RWNX_DINI_H_
+
+#include <linux/pci.h>
+#include "rwnx_platform.h"
+
+int rwnx_dini_platform_init(struct pci_dev *pci_dev,
+ struct rwnx_plat **rwnx_plat);
+
+#endif /* _RWNX_DINI_H_ */
diff --git a/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_events.h b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_events.h
new file mode 100644
index 000000000000..7fba5097cb1d
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_events.h
@@ -0,0 +1,1243 @@
+/**
+ ******************************************************************************
+ *
+ * @file rwnx_events.h
+ *
+ * @brief Trace events definition
+ *
+ * Copyright (C) RivieraWaves 2012-2019
+ *
+ ******************************************************************************
+ */
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM rwnx
+
+#if !defined(_RWNX_EVENTS_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _RWNX_EVENTS_H
+
+#include <linux/tracepoint.h>
+#ifndef CONFIG_RWNX_FHOST
+#include "rwnx_tx.h"
+#endif
+#include "rwnx_compat.h"
+
+/*****************************************************************************
+ * TRACE function for MGMT TX (FULLMAC)
+ ****************************************************************************/
+#ifdef CONFIG_RWNX_FULLMAC
+#include "linux/ieee80211.h"
+#if defined(CONFIG_TRACEPOINTS) && defined(CREATE_TRACE_POINTS)
+#include <linux/trace_seq.h>
+
+/* P2P Public Action Frames Definitions (see WiFi P2P Technical Specification, section 4.2.8) */
+/* IEEE 802.11 Public Action Usage Category - Define P2P public action frames */
+#define MGMT_ACTION_PUBLIC_CAT (0x04)
+/* Offset of OUI Subtype field in P2P Action Frame format */
+#define MGMT_ACTION_OUI_SUBTYPE_OFFSET (6)
+/* P2P Public Action Frame Types */
+enum p2p_action_type {
+ P2P_ACTION_GO_NEG_REQ = 0, /* GO Negociation Request */
+ P2P_ACTION_GO_NEG_RSP, /* GO Negociation Response */
+ P2P_ACTION_GO_NEG_CFM, /* GO Negociation Confirmation */
+ P2P_ACTION_INVIT_REQ, /* P2P Invitation Request */
+ P2P_ACTION_INVIT_RSP, /* P2P Invitation Response */
+ P2P_ACTION_DEV_DISC_REQ, /* Device Discoverability Request */
+ P2P_ACTION_DEV_DISC_RSP, /* Device Discoverability Response */
+ P2P_ACTION_PROV_DISC_REQ, /* Provision Discovery Request */
+ P2P_ACTION_PROV_DISC_RSP, /* Provision Discovery Response */
+};
+
+const char *ftrace_print_mgmt_info(struct trace_seq *p, u16 frame_control, u8 cat, u8 type, u8 p2p) {
+ const char *ret = trace_seq_buffer_ptr(p);
+
+ switch (frame_control & IEEE80211_FCTL_STYPE) {
+ case (IEEE80211_STYPE_ASSOC_REQ): trace_seq_printf(p, "Association Request"); break;
+ case (IEEE80211_STYPE_ASSOC_RESP): trace_seq_printf(p, "Association Response"); break;
+ case (IEEE80211_STYPE_REASSOC_REQ): trace_seq_printf(p, "Reassociation Request"); break;
+ case (IEEE80211_STYPE_REASSOC_RESP): trace_seq_printf(p, "Reassociation Response"); break;
+ case (IEEE80211_STYPE_PROBE_REQ): trace_seq_printf(p, "Probe Request"); break;
+ case (IEEE80211_STYPE_PROBE_RESP): trace_seq_printf(p, "Probe Response"); break;
+ case (IEEE80211_STYPE_BEACON): trace_seq_printf(p, "Beacon"); break;
+ case (IEEE80211_STYPE_ATIM): trace_seq_printf(p, "ATIM"); break;
+ case (IEEE80211_STYPE_DISASSOC): trace_seq_printf(p, "Disassociation"); break;
+ case (IEEE80211_STYPE_AUTH): trace_seq_printf(p, "Authentication"); break;
+ case (IEEE80211_STYPE_DEAUTH): trace_seq_printf(p, "Deauthentication"); break;
+ case (IEEE80211_STYPE_ACTION):
+ trace_seq_printf(p, "Action");
+ if (cat == MGMT_ACTION_PUBLIC_CAT && type == 0x9)
+ switch (p2p) {
+ case (P2P_ACTION_GO_NEG_REQ): trace_seq_printf(p, ": GO Negociation Request"); break;
+ case (P2P_ACTION_GO_NEG_RSP): trace_seq_printf(p, ": GO Negociation Response"); break;
+ case (P2P_ACTION_GO_NEG_CFM): trace_seq_printf(p, ": GO Negociation Confirmation"); break;
+ case (P2P_ACTION_INVIT_REQ): trace_seq_printf(p, ": P2P Invitation Request"); break;
+ case (P2P_ACTION_INVIT_RSP): trace_seq_printf(p, ": P2P Invitation Response"); break;
+ case (P2P_ACTION_DEV_DISC_REQ): trace_seq_printf(p, ": Device Discoverability Request"); break;
+ case (P2P_ACTION_DEV_DISC_RSP): trace_seq_printf(p, ": Device Discoverability Response"); break;
+ case (P2P_ACTION_PROV_DISC_REQ): trace_seq_printf(p, ": Provision Discovery Request"); break;
+ case (P2P_ACTION_PROV_DISC_RSP): trace_seq_printf(p, ": Provision Discovery Response"); break;
+ default: trace_seq_printf(p, "Unknown p2p %d", p2p); break;
+ }
+ else {
+ switch (cat) {
+ case 0: trace_seq_printf(p, ":Spectrum %d", type); break;
+ case 1: trace_seq_printf(p, ":QOS %d", type); break;
+ case 2: trace_seq_printf(p, ":DLS %d", type); break;
+ case 3: trace_seq_printf(p, ":BA %d", type); break;
+ case 4: trace_seq_printf(p, ":Public %d", type); break;
+ case 5: trace_seq_printf(p, ":Radio Measure %d", type); break;
+ case 6: trace_seq_printf(p, ":Fast BSS %d", type); break;
+ case 7: trace_seq_printf(p, ":HT Action %d", type); break;
+ case 8: trace_seq_printf(p, ":SA Query %d", type); break;
+ case 9: trace_seq_printf(p, ":Protected Public %d", type); break;
+ case 10: trace_seq_printf(p, ":WNM %d", type); break;
+ case 11: trace_seq_printf(p, ":Unprotected WNM %d", type); break;
+ case 12: trace_seq_printf(p, ":TDLS %d", type); break;
+ case 13: trace_seq_printf(p, ":Mesh %d", type); break;
+ case 14: trace_seq_printf(p, ":MultiHop %d", type); break;
+ case 15: trace_seq_printf(p, ":Self Protected %d", type); break;
+ case 126: trace_seq_printf(p, ":Vendor protected"); break;
+ case 127: trace_seq_printf(p, ":Vendor"); break;
+ default: trace_seq_printf(p, ":Unknown category %d", cat); break;
+ }
+ }
+ break;
+ default: trace_seq_printf(p, "Unknown subtype %d", frame_control & IEEE80211_FCTL_STYPE); break;
+ }
+
+ trace_seq_putc(p, 0);
+
+ return ret;
+}
+#endif /* defined(CONFIG_TRACEPOINTS) && defined(CREATE_TRACE_POINTS) */
+
+#undef __print_mgmt_info
+#define __print_mgmt_info(frame_control, cat, type, p2p) ftrace_print_mgmt_info(p, frame_control, cat, type, p2p)
+
+TRACE_EVENT(
+ roc,
+ TP_PROTO(u8 vif_idx, u16 freq, unsigned int duration),
+ TP_ARGS(vif_idx, freq, duration),
+ TP_STRUCT__entry(
+ __field(u8, vif_idx)
+ __field(u16, freq)
+ __field(unsigned int, duration)
+ ),
+ TP_fast_assign(
+ __entry->vif_idx = vif_idx;
+ __entry->freq = freq;
+ __entry->duration = duration;
+ ),
+ TP_printk("f=%d vif=%d dur=%d",
+ __entry->freq, __entry->vif_idx, __entry->duration)
+);
+
+TRACE_EVENT(
+ cancel_roc,
+ TP_PROTO(u8 vif_idx),
+ TP_ARGS(vif_idx),
+ TP_STRUCT__entry(
+ __field(u8, vif_idx)
+ ),
+ TP_fast_assign(
+ __entry->vif_idx = vif_idx;
+ ),
+ TP_printk("vif=%d", __entry->vif_idx)
+);
+
+TRACE_EVENT(
+ roc_exp,
+ TP_PROTO(u8 vif_idx),
+ TP_ARGS(vif_idx),
+ TP_STRUCT__entry(
+ __field(u8, vif_idx)
+ ),
+ TP_fast_assign(
+ __entry->vif_idx = vif_idx;
+ ),
+ TP_printk("vif=%d", __entry->vif_idx)
+);
+
+TRACE_EVENT(
+ switch_roc,
+ TP_PROTO(u8 vif_idx),
+ TP_ARGS(vif_idx),
+ TP_STRUCT__entry(
+ __field(u8, vif_idx)
+ ),
+ TP_fast_assign(
+ __entry->vif_idx = vif_idx;
+ ),
+ TP_printk("vif=%d", __entry->vif_idx)
+);
+
+DECLARE_EVENT_CLASS(
+ mgmt_template,
+ TP_PROTO(u16 freq, u8 vif_idx, u8 sta_idx, struct ieee80211_mgmt *mgmt),
+ TP_ARGS(freq, vif_idx, sta_idx, mgmt),
+ TP_STRUCT__entry(
+ __field(u16, freq)
+ __field(u8, vif_idx)
+ __field(u8, sta_idx)
+ __field(u16, frame_control)
+ __field(u8, action_cat)
+ __field(u8, action_type)
+ __field(u8, action_p2p)
+ ),
+ TP_fast_assign(
+ __entry->freq = freq;
+ __entry->vif_idx = vif_idx;
+ __entry->sta_idx = sta_idx;
+ __entry->frame_control = mgmt->frame_control;
+ __entry->action_cat = mgmt->u.action.category;
+ __entry->action_type = mgmt->u.action.u.wme_action.action_code;
+ __entry->action_p2p = *((u8 *)&mgmt->u.action.category
+ + MGMT_ACTION_OUI_SUBTYPE_OFFSET);
+ ),
+ TP_printk("f=%d vif=%d sta=%d -> %s",
+ __entry->freq, __entry->vif_idx, __entry->sta_idx,
+ __print_mgmt_info(__entry->frame_control, __entry->action_cat,
+ __entry->action_type, __entry->action_p2p))
+);
+
+DEFINE_EVENT(mgmt_template, mgmt_tx,
+ TP_PROTO(u16 freq, u8 vif_idx, u8 sta_idx, struct ieee80211_mgmt *mgmt),
+ TP_ARGS(freq, vif_idx, sta_idx, mgmt));
+
+DEFINE_EVENT(mgmt_template, mgmt_rx,
+ TP_PROTO(u16 freq, u8 vif_idx, u8 sta_idx, struct ieee80211_mgmt *mgmt),
+ TP_ARGS(freq, vif_idx, sta_idx, mgmt));
+
+TRACE_EVENT(
+ mgmt_cfm,
+ TP_PROTO(u8 vif_idx, u8 sta_idx, bool acked),
+ TP_ARGS(vif_idx, sta_idx, acked),
+ TP_STRUCT__entry(
+ __field(u8, vif_idx)
+ __field(u8, sta_idx)
+ __field(bool, acked)
+ ),
+ TP_fast_assign(
+ __entry->vif_idx = vif_idx;
+ __entry->sta_idx = sta_idx;
+ __entry->acked = acked;
+ ),
+ TP_printk("vif=%d sta=%d ack=%d",
+ __entry->vif_idx, __entry->sta_idx, __entry->acked)
+);
+#endif /* CONFIG_RWNX_FULLMAC */
+
+/*****************************************************************************
+ * TRACE function for TXQ
+ ****************************************************************************/
+#ifndef CONFIG_RWNX_FHOST
+#if defined(CONFIG_TRACEPOINTS) && defined(CREATE_TRACE_POINTS)
+
+#include <linux/trace_seq.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 2, 0)
+#include <linux/trace_events.h>
+#else
+#include <linux/ftrace_event.h>
+#endif
+
+const char *
+ftrace_print_txq(struct trace_seq *p, int txq_idx) {
+ const char *ret = trace_seq_buffer_ptr(p);
+
+ if (txq_idx == TXQ_INACTIVE) {
+ trace_seq_printf(p, "[INACTIVE]");
+ } else if (txq_idx < NX_FIRST_VIF_TXQ_IDX) {
+ trace_seq_printf(p, "[STA %d/%d]",
+ txq_idx / NX_NB_TXQ_PER_STA,
+ txq_idx % NX_NB_TXQ_PER_STA);
+#ifdef CONFIG_RWNX_FULLMAC
+ } else if (txq_idx < NX_FIRST_UNK_TXQ_IDX) {
+ trace_seq_printf(p, "[BC/MC %d]",
+ txq_idx - NX_FIRST_BCMC_TXQ_IDX);
+ } else if (txq_idx < NX_OFF_CHAN_TXQ_IDX) {
+ trace_seq_printf(p, "[UNKNOWN %d]",
+ txq_idx - NX_FIRST_UNK_TXQ_IDX);
+ } else if (txq_idx == NX_OFF_CHAN_TXQ_IDX) {
+ trace_seq_printf(p, "[OFFCHAN]");
+#else
+ } else if (txq_idx < NX_NB_TXQ) {
+ txq_idx -= NX_FIRST_VIF_TXQ_IDX;
+ trace_seq_printf(p, "[VIF %d/%d]",
+ txq_idx / NX_NB_TXQ_PER_VIF,
+ txq_idx % NX_NB_TXQ_PER_VIF);
+#endif
+ } else {
+ trace_seq_printf(p, "[ERROR %d]", txq_idx);
+ }
+
+ trace_seq_putc(p, 0);
+
+ return ret;
+}
+
+const char *
+ftrace_print_sta(struct trace_seq *p, int sta_idx) {
+ const char *ret = trace_seq_buffer_ptr(p);
+
+ if (sta_idx < NX_REMOTE_STA_MAX) {
+ trace_seq_printf(p, "[STA %d]", sta_idx);
+ } else {
+ trace_seq_printf(p, "[BC/MC %d]", sta_idx - NX_REMOTE_STA_MAX);
+ }
+
+ trace_seq_putc(p, 0);
+
+ return ret;
+}
+
+const char *
+ftrace_print_hwq(struct trace_seq *p, int hwq_idx) {
+
+ static const struct trace_print_flags symbols[] =
+ {{RWNX_HWQ_BK, "BK"},
+ {RWNX_HWQ_BE, "BE"},
+ {RWNX_HWQ_VI, "VI"},
+ {RWNX_HWQ_VO, "VO"},
+#ifdef CONFIG_RWNX_FULLMAC
+ {RWNX_HWQ_BCMC, "BCMC"},
+#else
+ {RWNX_HWQ_BCN, "BCN"},
+#endif
+ { -1, NULL }};
+ return trace_print_symbols_seq(p, hwq_idx, symbols);
+}
+
+const char *
+ftrace_print_hwq_cred(struct trace_seq *p, u8 *cred) {
+ const char *ret = trace_seq_buffer_ptr(p);
+
+#if CONFIG_USER_MAX == 1
+ trace_seq_printf(p, "%d", cred[0]);
+#else
+ int i;
+
+ for (i = 0; i < CONFIG_USER_MAX - 1; i++)
+ trace_seq_printf(p, "%d-", cred[i]);
+ trace_seq_printf(p, "%d", cred[i]);
+#endif
+
+ trace_seq_putc(p, 0);
+ return ret;
+}
+
+const char *
+ftrace_print_mu_info(struct trace_seq *p, u8 mu_info) {
+ const char *ret = trace_seq_buffer_ptr(p);
+
+ if (mu_info)
+ trace_seq_printf(p, "MU: %d-%d", (mu_info & 0x3f), (mu_info >> 6));
+
+ trace_seq_putc(p, 0);
+ return ret;
+}
+
+const char *
+ftrace_print_mu_group(struct trace_seq *p, int nb_user, u8 *users) {
+ const char *ret = trace_seq_buffer_ptr(p);
+ int i;
+
+ if (users[0] != 0xff)
+ trace_seq_printf(p, "(%d", users[0]);
+ else
+ trace_seq_printf(p, "(-");
+ for (i = 1; i < CONFIG_USER_MAX ; i++) {
+ if (users[i] != 0xff)
+ trace_seq_printf(p, ",%d", users[i]);
+ else
+ trace_seq_printf(p, ",-");
+ }
+
+ trace_seq_printf(p, ")");
+ trace_seq_putc(p, 0);
+ return ret;
+}
+
+const char *
+ftrace_print_amsdu(struct trace_seq *p, u16 nb_pkt) {
+ const char *ret = trace_seq_buffer_ptr(p);
+
+ if (nb_pkt > 1)
+ trace_seq_printf(p, "(AMSDU %d)", nb_pkt);
+
+ trace_seq_putc(p, 0);
+ return ret;
+}
+#endif /* defined(CONFIG_TRACEPOINTS) && defined(CREATE_TRACE_POINTS) */
+
+#undef __print_txq
+#define __print_txq(txq_idx) ftrace_print_txq(p, txq_idx)
+
+#undef __print_sta
+#define __print_sta(sta_idx) ftrace_print_sta(p, sta_idx)
+
+#undef __print_hwq
+#define __print_hwq(hwq) ftrace_print_hwq(p, hwq)
+
+#undef __print_hwq_cred
+#define __print_hwq_cred(cred) ftrace_print_hwq_cred(p, cred)
+
+#undef __print_mu_info
+#define __print_mu_info(mu_info) ftrace_print_mu_info(p, mu_info)
+
+#undef __print_mu_group
+#define __print_mu_group(nb, users) ftrace_print_mu_group(p, nb, users)
+
+#undef __print_amsdu
+#define __print_amsdu(nb_pkt) ftrace_print_amsdu(p, nb_pkt)
+
+#ifdef CONFIG_RWNX_FULLMAC
+
+TRACE_EVENT(
+ txq_select,
+ TP_PROTO(int txq_idx, u16 pkt_ready_up, struct sk_buff *skb),
+ TP_ARGS(txq_idx, pkt_ready_up, skb),
+ TP_STRUCT__entry(
+ __field(u16, txq_idx)
+ __field(u16, pkt_ready)
+ __field(struct sk_buff *, skb)
+ ),
+ TP_fast_assign(
+ __entry->txq_idx = txq_idx;
+ __entry->pkt_ready = pkt_ready_up;
+ __entry->skb = skb;
+ ),
+ TP_printk("%s pkt_ready_up=%d skb=%p", __print_txq(__entry->txq_idx),
+ __entry->pkt_ready, __entry->skb)
+);
+
+#endif /* CONFIG_RWNX_FULLMAC */
+
+DECLARE_EVENT_CLASS(
+ hwq_template,
+ TP_PROTO(u8 hwq_idx),
+ TP_ARGS(hwq_idx),
+ TP_STRUCT__entry(
+ __field(u8, hwq_idx)
+ ),
+ TP_fast_assign(
+ __entry->hwq_idx = hwq_idx;
+ ),
+ TP_printk("%s", __print_hwq(__entry->hwq_idx))
+);
+
+DEFINE_EVENT(hwq_template, hwq_flowctrl_stop,
+ TP_PROTO(u8 hwq_idx),
+ TP_ARGS(hwq_idx));
+
+DEFINE_EVENT(hwq_template, hwq_flowctrl_start,
+ TP_PROTO(u8 hwq_idx),
+ TP_ARGS(hwq_idx));
+
+
+DECLARE_EVENT_CLASS(
+ txq_template,
+ TP_PROTO(struct rwnx_txq *txq),
+ TP_ARGS(txq),
+ TP_STRUCT__entry(
+ __field(u16, txq_idx)
+ ),
+ TP_fast_assign(
+ __entry->txq_idx = txq->idx;
+ ),
+ TP_printk("%s", __print_txq(__entry->txq_idx))
+);
+
+DEFINE_EVENT(txq_template, txq_add_to_hw,
+ TP_PROTO(struct rwnx_txq *txq),
+ TP_ARGS(txq));
+
+DEFINE_EVENT(txq_template, txq_del_from_hw,
+ TP_PROTO(struct rwnx_txq *txq),
+ TP_ARGS(txq));
+
+#ifdef CONFIG_RWNX_FULLMAC
+
+DEFINE_EVENT(txq_template, txq_flowctrl_stop,
+ TP_PROTO(struct rwnx_txq *txq),
+ TP_ARGS(txq));
+
+DEFINE_EVENT(txq_template, txq_flowctrl_restart,
+ TP_PROTO(struct rwnx_txq *txq),
+ TP_ARGS(txq));
+
+#endif /* CONFIG_RWNX_FULLMAC */
+
+TRACE_EVENT(
+ process_txq,
+ TP_PROTO(struct rwnx_txq *txq),
+ TP_ARGS(txq),
+ TP_STRUCT__entry(
+ __field(u16, txq_idx)
+ __field(u16, len)
+ __field(u16, len_retry)
+ __field(s8, credit)
+ #ifdef CONFIG_RWNX_FULLMAC
+ __field(u16, limit)
+ #endif /* CONFIG_RWNX_FULLMAC*/
+ ),
+ TP_fast_assign(
+ __entry->txq_idx = txq->idx;
+ __entry->len = skb_queue_len(&txq->sk_list);
+ #ifdef CONFIG_MAC80211_TXQ
+ __entry->len += txq->nb_ready_mac80211;
+ #endif
+ __entry->len_retry = txq->nb_retry;
+ __entry->credit = txq->credits;
+ #ifdef CONFIG_RWNX_FULLMAC
+ __entry->limit = txq->push_limit;
+ #endif /* CONFIG_RWNX_FULLMAC*/
+ ),
+
+ #ifdef CONFIG_RWNX_FULLMAC
+ TP_printk("%s txq_credits=%d, len=%d, retry_len=%d, push_limit=%d",
+ __print_txq(__entry->txq_idx), __entry->credit,
+ __entry->len, __entry->len_retry, __entry->limit)
+ #else
+ TP_printk("%s txq_credits=%d, len=%d, retry_len=%d",
+ __print_txq(__entry->txq_idx), __entry->credit,
+ __entry->len, __entry->len_retry)
+ #endif /* CONFIG_RWNX_FULLMAC*/
+);
+
+DECLARE_EVENT_CLASS(
+ txq_reason_template,
+ TP_PROTO(struct rwnx_txq *txq, u16 reason),
+ TP_ARGS(txq, reason),
+ TP_STRUCT__entry(
+ __field(u16, txq_idx)
+ __field(u16, reason)
+ __field(u16, status)
+ ),
+ TP_fast_assign(
+ __entry->txq_idx = txq->idx;
+ __entry->reason = reason;
+ __entry->status = txq->status;
+ ),
+ TP_printk("%s reason=%s status=%s",
+ __print_txq(__entry->txq_idx),
+ __print_symbolic(__entry->reason,
+ {RWNX_TXQ_STOP_FULL, "FULL"},
+ {RWNX_TXQ_STOP_CSA, "CSA"},
+ {RWNX_TXQ_STOP_STA_PS, "PS"},
+ {RWNX_TXQ_STOP_VIF_PS, "VPS"},
+ {RWNX_TXQ_STOP_CHAN, "CHAN"},
+ {RWNX_TXQ_STOP_MU_POS, "MU"}),
+ __print_flags(__entry->status, "|",
+ {RWNX_TXQ_IN_HWQ_LIST, "IN LIST"},
+ {RWNX_TXQ_STOP_FULL, "FULL"},
+ {RWNX_TXQ_STOP_CSA, "CSA"},
+ {RWNX_TXQ_STOP_STA_PS, "PS"},
+ {RWNX_TXQ_STOP_VIF_PS, "VPS"},
+ {RWNX_TXQ_STOP_CHAN, "CHAN"},
+ {RWNX_TXQ_STOP_MU_POS, "MU"},
+ {RWNX_TXQ_NDEV_FLOW_CTRL, "FLW_CTRL"}))
+);
+
+DEFINE_EVENT(txq_reason_template, txq_start,
+ TP_PROTO(struct rwnx_txq *txq, u16 reason),
+ TP_ARGS(txq, reason));
+
+DEFINE_EVENT(txq_reason_template, txq_stop,
+ TP_PROTO(struct rwnx_txq *txq, u16 reason),
+ TP_ARGS(txq, reason));
+
+
+TRACE_EVENT(
+ push_desc,
+ TP_PROTO(struct sk_buff *skb, struct rwnx_sw_txhdr *sw_txhdr, int push_flags),
+
+ TP_ARGS(skb, sw_txhdr, push_flags),
+
+ TP_STRUCT__entry(
+ __field(struct sk_buff *, skb)
+ __field(unsigned int, len)
+ __field(u16, tx_queue)
+ __field(u8, hw_queue)
+ __field(u8, push_flag)
+ __field(u32, flag)
+ __field(s8, txq_cred)
+ __field(u8, hwq_cred)
+ __field(u16, pkt_cnt)
+ __field(u8, mu_info)
+ ),
+ TP_fast_assign(
+ __entry->skb = skb;
+ __entry->tx_queue = sw_txhdr->txq->idx;
+ __entry->push_flag = push_flags;
+ __entry->hw_queue = sw_txhdr->txq->hwq->id;
+ __entry->txq_cred = sw_txhdr->txq->credits;
+ //__entry->hwq_cred = sw_txhdr->txq->hwq->credits[RWNX_TXQ_POS_ID(sw_txhdr->txq)];
+#ifdef CONFIG_RWNX_SPLIT_TX_BUF
+ __entry->pkt_cnt = sw_txhdr->desc.host.packet_cnt;
+#endif
+#ifdef CONFIG_RWNX_FULLMAC
+ __entry->flag = sw_txhdr->desc.host.flags;
+#ifdef CONFIG_RWNX_SPLIT_TX_BUF
+#ifdef CONFIG_RWNX_AMSDUS_TX
+ if (sw_txhdr->amsdu.len)
+ __entry->len = sw_txhdr->amsdu.len;
+ else
+#endif /* CONFIG_RWNX_AMSDUS_TX */
+ __entry->len = sw_txhdr->desc.host.packet_len[0];
+#else
+ __entry->len = sw_txhdr->desc.host.packet_len;
+#endif /* CONFIG_RWNX_SPLIT_TX_BUF */
+
+#else /* !CONFIG_RWNX_FULLMAC */
+ __entry->flag = sw_txhdr->desc.umac.flags;
+ __entry->len = sw_txhdr->frame_len;
+ __entry->sn = sw_txhdr->sn;
+#endif /* CONFIG_RWNX_FULLMAC */
+#ifdef CONFIG_RWNX_MUMIMO_TX
+ __entry->mu_info = sw_txhdr->desc.host.mumimo_info;
+#else
+ __entry->mu_info = 0;
+#endif
+ ),
+
+#ifdef CONFIG_RWNX_FULLMAC
+ TP_printk("%s skb=%p (len=%d) hw_queue=%s cred_txq=%d cred_hwq=%d %s flag=%s %s%s%s",
+ __print_txq(__entry->tx_queue), __entry->skb, __entry->len,
+ __print_hwq(__entry->hw_queue),
+ __entry->txq_cred, __entry->hwq_cred,
+ __print_mu_info(__entry->mu_info),
+ __print_flags(__entry->flag, "|",
+ {TXU_CNTRL_RETRY, "RETRY"},
+ {TXU_CNTRL_MORE_DATA, "MOREDATA"},
+ {TXU_CNTRL_MGMT, "MGMT"},
+ {TXU_CNTRL_MGMT_NO_CCK, "NO_CCK"},
+ {TXU_CNTRL_MGMT_ROBUST, "ROBUST"},
+ {TXU_CNTRL_AMSDU, "AMSDU"},
+ {TXU_CNTRL_USE_4ADDR, "4ADDR"},
+ {TXU_CNTRL_EOSP, "EOSP"},
+ {TXU_CNTRL_MESH_FWD, "MESH_FWD"},
+ {TXU_CNTRL_TDLS, "TDLS"}),
+ (__entry->push_flag & RWNX_PUSH_IMMEDIATE) ? "(IMMEDIATE)" : "",
+ (!(__entry->flag & TXU_CNTRL_RETRY) &&
+ (__entry->push_flag & RWNX_PUSH_RETRY)) ? "(SW_RETRY)" : "",
+ __print_amsdu(__entry->pkt_cnt))
+#else
+ TP_printk("%s skb=%p (len=%d) hw_queue=%s cred_txq=%d cred_hwq=%d %s flag=%x (%s) sn=%d %s",
+ __print_txq(__entry->tx_queue), __entry->skb, __entry->len,
+ __print_hwq(__entry->hw_queue), __entry->txq_cred, __entry->hwq_cred,
+ __print_mu_info(__entry->mu_info),
+ __entry->flag,
+ __print_flags(__entry->push_flag, "|",
+ {RWNX_PUSH_RETRY, "RETRY"},
+ {RWNX_PUSH_IMMEDIATE, "IMMEDIATE"}),
+ __entry->sn, __print_amsdu(__entry->pkt_cnt))
+#endif /* CONFIG_RWNX_FULLMAC */
+);
+
+
+TRACE_EVENT(
+ txq_queue_skb,
+ TP_PROTO(struct sk_buff *skb, struct rwnx_txq *txq, bool retry),
+ TP_ARGS(skb, txq, retry),
+ TP_STRUCT__entry(
+ __field(struct sk_buff *, skb)
+ __field(u16, txq_idx)
+ __field(s8, credit)
+ __field(u16, q_len)
+ __field(u16, q_len_retry)
+ __field(bool, retry)
+ ),
+ TP_fast_assign(
+ __entry->skb = skb;
+ __entry->txq_idx = txq->idx;
+ __entry->credit = txq->credits;
+ __entry->q_len = skb_queue_len(&txq->sk_list);
+ __entry->q_len_retry = txq->nb_retry;
+ __entry->retry = retry;
+ ),
+
+ TP_printk("%s skb=%p retry=%d txq_credits=%d queue_len=%d (retry = %d)",
+ __print_txq(__entry->txq_idx), __entry->skb, __entry->retry,
+ __entry->credit, __entry->q_len, __entry->q_len_retry)
+);
+
+#ifdef CONFIG_MAC80211_TXQ
+TRACE_EVENT(
+ txq_wake,
+ TP_PROTO(struct rwnx_txq *txq),
+ TP_ARGS(txq),
+ TP_STRUCT__entry(
+ __field(u16, txq_idx)
+ __field(u16, q_len)
+ ),
+ TP_fast_assign(
+ __entry->txq_idx = txq->idx;
+ __entry->q_len = txq->nb_ready_mac80211;
+ ),
+
+ TP_printk("%s mac80211_queue_len=%d", __print_txq(__entry->txq_idx), __entry->q_len)
+);
+
+TRACE_EVENT(
+ txq_drop,
+ TP_PROTO(struct rwnx_txq *txq, unsigned long nb_drop),
+ TP_ARGS(txq, nb_drop),
+ TP_STRUCT__entry(
+ __field(u16, txq_idx)
+ __field(u16, nb_drop)
+ ),
+ TP_fast_assign(
+ __entry->txq_idx = txq->idx;
+ __entry->nb_drop = nb_drop;
+ ),
+
+ TP_printk("%s %u pkt have been dropped by codel in mac80211 txq",
+ __print_txq(__entry->txq_idx), __entry->nb_drop)
+);
+
+#endif
+
+
+DECLARE_EVENT_CLASS(
+ idx_template,
+ TP_PROTO(u16 idx),
+ TP_ARGS(idx),
+ TP_STRUCT__entry(
+ __field(u16, idx)
+ ),
+ TP_fast_assign(
+ __entry->idx = idx;
+ ),
+ TP_printk("idx=%d", __entry->idx)
+);
+
+
+DEFINE_EVENT(idx_template, txq_vif_start,
+ TP_PROTO(u16 idx),
+ TP_ARGS(idx));
+
+DEFINE_EVENT(idx_template, txq_vif_stop,
+ TP_PROTO(u16 idx),
+ TP_ARGS(idx));
+
+TRACE_EVENT(
+ process_hw_queue,
+ TP_PROTO(struct rwnx_hwq *hwq),
+ TP_ARGS(hwq),
+ TP_STRUCT__entry(
+ __field(u16, hwq)
+ __array(u8, credits, CONFIG_USER_MAX)
+ ),
+ TP_fast_assign(
+ //int i;
+ __entry->hwq = hwq->id;
+ //for (i=0; i < CONFIG_USER_MAX; i ++)
+ // __entry->credits[i] = hwq->credits[i];
+ ),
+ TP_printk("hw_queue=%s hw_credits=%s",
+ __print_hwq(__entry->hwq), __print_hwq_cred(__entry->credits))
+);
+
+DECLARE_EVENT_CLASS(
+ sta_idx_template,
+ TP_PROTO(u16 idx),
+ TP_ARGS(idx),
+ TP_STRUCT__entry(
+ __field(u16, idx)
+ ),
+ TP_fast_assign(
+ __entry->idx = idx;
+ ),
+ TP_printk("%s", __print_sta(__entry->idx))
+);
+
+DEFINE_EVENT(sta_idx_template, txq_sta_start,
+ TP_PROTO(u16 idx),
+ TP_ARGS(idx));
+
+DEFINE_EVENT(sta_idx_template, txq_sta_stop,
+ TP_PROTO(u16 idx),
+ TP_ARGS(idx));
+
+#ifdef CONFIG_RWNX_FULLMAC
+
+DEFINE_EVENT(sta_idx_template, ps_disable,
+ TP_PROTO(u16 idx),
+ TP_ARGS(idx));
+
+#endif /* CONFIG_RWNX_FULLMAC */
+
+TRACE_EVENT(
+ skb_confirm,
+ TP_PROTO(struct sk_buff *skb, struct rwnx_txq *txq, struct rwnx_hwq *hwq,
+#ifdef CONFIG_RWNX_FULLMAC
+ struct tx_cfm_tag *cfm
+#else
+ u8 cfm
+#endif
+ ),
+
+ TP_ARGS(skb, txq, hwq, cfm),
+
+ TP_STRUCT__entry(
+ __field(struct sk_buff *, skb)
+ __field(u16, txq_idx)
+ __field(u8, hw_queue)
+ __array(u8, hw_credit, CONFIG_USER_MAX)
+ __field(s8, sw_credit)
+ __field(s8, sw_credit_up)
+#ifdef CONFIG_RWNX_FULLMAC
+ __field(u8, ampdu_size)
+#ifdef CONFIG_RWNX_SPLIT_TX_BUF
+ __field(u16, amsdu)
+#endif /* CONFIG_RWNX_SPLIT_TX_BUF */
+ __field(u16, sn)
+#endif /* CONFIG_RWNX_FULLMAC*/
+ ),
+
+ TP_fast_assign(
+ //int i;
+ __entry->skb = skb;
+ __entry->txq_idx = txq->idx;
+ __entry->hw_queue = hwq->id;
+ //for (i = 0 ; i < CONFIG_USER_MAX ; i++)
+ // __entry->hw_credit[i] = hwq->credits[i];
+ __entry->sw_credit = txq->credits;
+#if defined CONFIG_RWNX_FULLMAC
+ __entry->sw_credit_up = cfm->credits;
+ __entry->ampdu_size = cfm->ampdu_size;
+#ifdef CONFIG_RWNX_SPLIT_TX_BUF
+ __entry->amsdu = cfm->amsdu_size;
+ __entry->sn = cfm->sn;
+#endif
+#else
+ __entry->sw_credit_up = cfm
+#endif /* CONFIG_RWNX_FULLMAC */
+ ),
+
+ TP_printk("%s skb=%p hw_queue=%s, hw_credits=%s, txq_credits=%d (+%d)"
+#ifdef CONFIG_RWNX_FULLMAC
+ " sn=%u ampdu=%d"
+#ifdef CONFIG_RWNX_SPLIT_TX_BUF
+ " amsdu=%u"
+#endif
+#endif
+ , __print_txq(__entry->txq_idx), __entry->skb,
+ __print_hwq(__entry->hw_queue),
+ __print_hwq_cred(__entry->hw_credit),
+ __entry->sw_credit, __entry->sw_credit_up
+#ifdef CONFIG_RWNX_FULLMAC
+ , __entry->sn, __entry->ampdu_size
+#ifdef CONFIG_RWNX_SPLIT_TX_BUF
+ , __entry->amsdu
+#endif
+#endif
+ )
+);
+
+TRACE_EVENT(
+ credit_update,
+ TP_PROTO(struct rwnx_txq *txq, s8_l cred_up),
+
+ TP_ARGS(txq, cred_up),
+
+ TP_STRUCT__entry(
+ __field(struct sk_buff *, skb)
+ __field(u16, txq_idx)
+ __field(s8, sw_credit)
+ __field(s8, sw_credit_up)
+ ),
+
+ TP_fast_assign(
+ __entry->txq_idx = txq->idx;
+ __entry->sw_credit = txq->credits;
+ __entry->sw_credit_up = cred_up;
+ ),
+
+ TP_printk("%s txq_credits=%d (%+d)", __print_txq(__entry->txq_idx),
+ __entry->sw_credit, __entry->sw_credit_up)
+)
+
+#ifdef CONFIG_RWNX_FULLMAC
+
+DECLARE_EVENT_CLASS(
+ ps_template,
+ TP_PROTO(struct rwnx_sta *sta),
+ TP_ARGS(sta),
+ TP_STRUCT__entry(
+ __field(u16, idx)
+ __field(u16, ready_ps)
+ __field(u16, sp_ps)
+ __field(u16, ready_uapsd)
+ __field(u16, sp_uapsd)
+ ),
+ TP_fast_assign(
+ __entry->idx = sta->sta_idx;
+ __entry->ready_ps = sta->ps.pkt_ready[LEGACY_PS_ID];
+ __entry->sp_ps = sta->ps.sp_cnt[LEGACY_PS_ID];
+ __entry->ready_uapsd = sta->ps.pkt_ready[UAPSD_ID];
+ __entry->sp_uapsd = sta->ps.sp_cnt[UAPSD_ID];
+ ),
+
+ TP_printk("%s [PS] ready=%d sp=%d [UAPSD] ready=%d sp=%d",
+ __print_sta(__entry->idx), __entry->ready_ps, __entry->sp_ps,
+ __entry->ready_uapsd, __entry->sp_uapsd)
+);
+
+DEFINE_EVENT(ps_template, ps_queue,
+ TP_PROTO(struct rwnx_sta *sta),
+ TP_ARGS(sta));
+
+DEFINE_EVENT(ps_template, ps_push,
+ TP_PROTO(struct rwnx_sta *sta),
+ TP_ARGS(sta));
+
+DEFINE_EVENT(ps_template, ps_enable,
+ TP_PROTO(struct rwnx_sta *sta),
+ TP_ARGS(sta));
+
+TRACE_EVENT(
+ ps_traffic_update,
+ TP_PROTO(u16 sta_idx, u8 traffic, bool uapsd),
+
+ TP_ARGS(sta_idx, traffic, uapsd),
+
+ TP_STRUCT__entry(
+ __field(u16, sta_idx)
+ __field(u8, traffic)
+ __field(bool, uapsd)
+ ),
+
+ TP_fast_assign(
+ __entry->sta_idx = sta_idx;
+ __entry->traffic = traffic;
+ __entry->uapsd = uapsd;
+ ),
+
+ TP_printk("%s %s%s traffic available ", __print_sta(__entry->sta_idx),
+ __entry->traffic ? "" : "no more ",
+ __entry->uapsd ? "U-APSD" : "legacy PS")
+);
+
+TRACE_EVENT(
+ ps_traffic_req,
+ TP_PROTO(struct rwnx_sta *sta, u16 pkt_req, u8 ps_id),
+ TP_ARGS(sta, pkt_req, ps_id),
+ TP_STRUCT__entry(
+ __field(u16, idx)
+ __field(u16, pkt_req)
+ __field(u8, ps_id)
+ __field(u16, ready)
+ __field(u16, sp)
+ ),
+ TP_fast_assign(
+ __entry->idx = sta->sta_idx;
+ __entry->pkt_req = pkt_req;
+ __entry->ps_id = ps_id;
+ __entry->ready = sta->ps.pkt_ready[ps_id];
+ __entry->sp = sta->ps.sp_cnt[ps_id];
+ ),
+
+ TP_printk("%s %s traffic request %d pkt (ready=%d, sp=%d)",
+ __print_sta(__entry->idx),
+ __entry->ps_id == UAPSD_ID ? "U-APSD" : "legacy PS" ,
+ __entry->pkt_req, __entry->ready, __entry->sp)
+);
+
+
+#ifdef CONFIG_RWNX_AMSDUS_TX
+TRACE_EVENT(
+ amsdu_subframe,
+ TP_PROTO(struct rwnx_sw_txhdr *sw_txhdr),
+ TP_ARGS(sw_txhdr),
+ TP_STRUCT__entry(
+ __field(struct sk_buff *, skb)
+ __field(u16, txq_idx)
+ __field(u8, nb)
+ __field(u32, len)
+ ),
+ TP_fast_assign(
+ __entry->skb = sw_txhdr->skb;
+ __entry->nb = sw_txhdr->amsdu.nb;
+ __entry->len = sw_txhdr->amsdu.len;
+ __entry->txq_idx = sw_txhdr->txq->idx;
+ ),
+
+ TP_printk("%s skb=%p %s nb_subframe=%d, len=%u",
+ __print_txq(__entry->txq_idx), __entry->skb,
+ (__entry->nb == 2) ? "Start new AMSDU" : "Add subframe",
+ __entry->nb, __entry->len)
+);
+#endif
+
+#endif /* CONFIG_RWNX_FULLMAC */
+
+#ifdef CONFIG_RWNX_MUMIMO_TX
+TRACE_EVENT(
+ mu_group_update,
+ TP_PROTO(struct rwnx_mu_group *group),
+ TP_ARGS(group),
+ TP_STRUCT__entry(
+ __field(u8, nb_user)
+ __field(u8, group_id)
+ __array(u8, users, CONFIG_USER_MAX)
+ ),
+ TP_fast_assign(
+ int i;
+ __entry->nb_user = group->user_cnt;
+ for (i = 0; i < CONFIG_USER_MAX ; i++) {
+ if (group->users[i]) {
+ __entry->users[i] = group->users[i]->sta_idx;
+ } else {
+ __entry->users[i] = 0xff;
+ }
+ }
+
+ __entry->group_id = group->group_id;
+ ),
+
+ TP_printk("Group-id = %d, Users = %s",
+ __entry->group_id,
+ __print_mu_group(__entry->nb_user, __entry->users))
+);
+
+TRACE_EVENT(
+ mu_group_delete,
+ TP_PROTO(int group_id),
+ TP_ARGS(group_id),
+ TP_STRUCT__entry(
+ __field(u8, group_id)
+ ),
+ TP_fast_assign(
+ __entry->group_id = group_id;
+ ),
+
+ TP_printk("Group-id = %d", __entry->group_id)
+);
+
+TRACE_EVENT(
+ mu_group_selection,
+ TP_PROTO(struct rwnx_sta *sta, int group_id),
+ TP_ARGS(sta, group_id),
+ TP_STRUCT__entry(
+ __field(u8, sta_idx)
+ __field(u8, group_id)
+ ),
+ TP_fast_assign(
+ __entry->sta_idx = sta->sta_idx;
+ __entry->group_id = group_id;
+ ),
+
+ TP_printk("[Sta %d] Group-id = %d", __entry->sta_idx, __entry->group_id)
+);
+
+TRACE_EVENT(
+ txq_select_mu_group,
+ TP_PROTO(struct rwnx_txq *txq, int group_id, int pos),
+
+ TP_ARGS(txq, group_id, pos),
+
+ TP_STRUCT__entry(
+ __field(u16, txq_idx)
+ __field(u8, group_id)
+ __field(u8, pos)
+ ),
+ TP_fast_assign(
+ __entry->txq_idx = txq->idx;
+ __entry->group_id = group_id;
+ __entry->pos = pos;
+ ),
+
+ TP_printk("%s: group=%d pos=%d", __print_txq(__entry->txq_idx),
+ __entry->group_id, __entry->pos)
+);
+
+#endif /* CONFIG_RWNX_MUMIMO_TX */
+#endif /* ! CONFIG_RWNX_FHOST */
+
+/*****************************************************************************
+ * TRACE functions for MESH
+ ****************************************************************************/
+#ifdef CONFIG_RWNX_FULLMAC
+DECLARE_EVENT_CLASS(
+ mesh_path_template,
+ TP_PROTO(struct rwnx_mesh_path *mesh_path),
+ TP_ARGS(mesh_path),
+ TP_STRUCT__entry(
+ __field(u8, idx)
+ __field(u8, next_hop_sta)
+ __array(u8, tgt_mac, ETH_ALEN)
+ ),
+
+ TP_fast_assign(
+ __entry->idx = mesh_path->path_idx;
+ memcpy(__entry->tgt_mac, &mesh_path->tgt_mac_addr, ETH_ALEN);
+ if (mesh_path->p_nhop_sta)
+ __entry->next_hop_sta = mesh_path->p_nhop_sta->sta_idx;
+ else
+ __entry->next_hop_sta = 0xff;
+ ),
+
+ TP_printk("Mpath(%d): target=%pM next_hop=STA-%d",
+ __entry->idx, __entry->tgt_mac, __entry->next_hop_sta)
+);
+
+DEFINE_EVENT(mesh_path_template, mesh_create_path,
+ TP_PROTO(struct rwnx_mesh_path *mesh_path),
+ TP_ARGS(mesh_path));
+
+DEFINE_EVENT(mesh_path_template, mesh_delete_path,
+ TP_PROTO(struct rwnx_mesh_path *mesh_path),
+ TP_ARGS(mesh_path));
+
+DEFINE_EVENT(mesh_path_template, mesh_update_path,
+ TP_PROTO(struct rwnx_mesh_path *mesh_path),
+ TP_ARGS(mesh_path));
+
+#endif /* CONFIG_RWNX_FULLMAC */
+
+/*****************************************************************************
+ * TRACE functions for RADAR
+ ****************************************************************************/
+#ifdef CONFIG_RWNX_RADAR
+TRACE_EVENT(
+ radar_pulse,
+ TP_PROTO(u8 chain, struct radar_pulse *pulse),
+ TP_ARGS(chain, pulse),
+ TP_STRUCT__entry(
+ __field(u8, chain)
+ __field(s16, freq)
+ __field(u16, pri)
+ __field(u8, len)
+ __field(u8, fom)
+ ),
+ TP_fast_assign(
+ __entry->freq = pulse->freq * 2;
+ __entry->len = pulse->len * 2;
+ __entry->fom = pulse->fom * 6;
+ __entry->pri = pulse->rep;
+ __entry->chain = chain;
+ ),
+
+ TP_printk("%s: PRI=%.5d LEN=%.3d FOM=%.2d%% freq=%dMHz ",
+ __print_symbolic(__entry->chain,
+ {RWNX_RADAR_RIU, "RIU"},
+ {RWNX_RADAR_FCU, "FCU"}),
+ __entry->pri, __entry->len, __entry->fom, __entry->freq)
+ );
+
+TRACE_EVENT(
+ radar_detected,
+ TP_PROTO(u8 chain, u8 region, s16 freq, u8 type, u16 pri),
+ TP_ARGS(chain, region, freq, type, pri),
+ TP_STRUCT__entry(
+ __field(u8, chain)
+ __field(u8, region)
+ __field(s16, freq)
+ __field(u8, type)
+ __field(u16, pri)
+ ),
+ TP_fast_assign(
+ __entry->chain = chain;
+ __entry->region = region;
+ __entry->freq = freq;
+ __entry->type = type;
+ __entry->pri = pri;
+ ),
+ TP_printk("%s: region=%s type=%d freq=%dMHz (pri=%dus)",
+ __print_symbolic(__entry->chain,
+ {RWNX_RADAR_RIU, "RIU"},
+ {RWNX_RADAR_FCU, "FCU"}),
+ __print_symbolic(__entry->region,
+ {NL80211_DFS_UNSET, "UNSET"},
+ {NL80211_DFS_FCC, "FCC"},
+ {NL80211_DFS_ETSI, "ETSI"},
+ {NL80211_DFS_JP, "JP"}),
+ __entry->type, __entry->freq, __entry->pri)
+);
+
+TRACE_EVENT(
+ radar_set_region,
+ TP_PROTO(u8 region),
+ TP_ARGS(region),
+ TP_STRUCT__entry(
+ __field(u8, region)
+ ),
+ TP_fast_assign(
+ __entry->region = region;
+ ),
+ TP_printk("region=%s",
+ __print_symbolic(__entry->region,
+ {NL80211_DFS_UNSET, "UNSET"},
+ {NL80211_DFS_FCC, "FCC"},
+ {NL80211_DFS_ETSI, "ETSI"},
+ {NL80211_DFS_JP, "JP"}))
+);
+
+TRACE_EVENT(
+ radar_enable_detection,
+ TP_PROTO(u8 region, u8 enable, u8 chain),
+ TP_ARGS(region, enable, chain),
+ TP_STRUCT__entry(
+ __field(u8, region)
+ __field(u8, chain)
+ __field(u8, enable)
+ ),
+ TP_fast_assign(
+ __entry->chain = chain;
+ __entry->enable = enable;
+ __entry->region = region;
+ ),
+ TP_printk("%s: %s radar detection %s",
+ __print_symbolic(__entry->chain,
+ {RWNX_RADAR_RIU, "RIU"},
+ {RWNX_RADAR_FCU, "FCU"}),
+ __print_symbolic(__entry->enable,
+ {RWNX_RADAR_DETECT_DISABLE, "Disable"},
+ {RWNX_RADAR_DETECT_ENABLE, "Enable (no report)"},
+ {RWNX_RADAR_DETECT_REPORT, "Enable"}),
+ __entry->enable == RWNX_RADAR_DETECT_DISABLE ? "" :
+ __print_symbolic(__entry->region,
+ {NL80211_DFS_UNSET, "UNSET"},
+ {NL80211_DFS_FCC, "FCC"},
+ {NL80211_DFS_ETSI, "ETSI"},
+ {NL80211_DFS_JP, "JP"}))
+);
+#endif /* CONFIG_RWNX_RADAR */
+
+/*****************************************************************************
+ * TRACE functions for IPC message
+ ****************************************************************************/
+#include "rwnx_strs.h"
+
+DECLARE_EVENT_CLASS(
+ ipc_msg_template,
+ TP_PROTO(u16 id),
+ TP_ARGS(id),
+ TP_STRUCT__entry(
+ __field(u16, id)
+ ),
+ TP_fast_assign(
+ __entry->id = id;
+ ),
+
+ TP_printk("%s (%d - %d)", RWNX_ID2STR(__entry->id),
+ MSG_T(__entry->id), MSG_I(__entry->id))
+);
+
+DEFINE_EVENT(ipc_msg_template, msg_send,
+ TP_PROTO(u16 id),
+ TP_ARGS(id));
+
+DEFINE_EVENT(ipc_msg_template, msg_recv,
+ TP_PROTO(u16 id),
+ TP_ARGS(id));
+
+
+
+#endif /* !defined(_RWNX_EVENTS_H) || defined(TRACE_HEADER_MULTI_READ) */
+
+#undef TRACE_INCLUDE_PATH
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_PATH .
+#define TRACE_INCLUDE_FILE rwnx_events
+#include <trace/define_trace.h>
diff --git a/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_fw_dump.c b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_fw_dump.c
new file mode 100644
index 000000000000..eb47c02f5a7b
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_fw_dump.c
@@ -0,0 +1,568 @@
+/**
+ ******************************************************************************
+ *
+ * @file rwnx_fw_dump.c
+ *
+ * @brief Definition of debug fs entries to process fw dump
+ *
+ * Copyright (C) RivieraWaves 2012-2019
+ *
+ ******************************************************************************
+ */
+
+
+#include <linux/kmod.h>
+#include <linux/debugfs.h>
+
+#include "rwnx_defs.h"
+#include "rwnx_debugfs.h"
+
+static ssize_t rwnx_dbgfs_rhd_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct rwnx_hw *priv = file->private_data;
+ struct dbg_debug_dump_tag *dump = priv->dbgdump_elem.buf.addr;
+ ssize_t read;
+
+ mutex_lock(&priv->dbgdump_elem.mutex);
+ if (!priv->debugfs.trace_prst) {
+ mutex_unlock(&priv->dbgdump_elem.mutex);
+ return 0;
+ }
+
+ read = simple_read_from_buffer(user_buf, count, ppos,
+ dump->rhd_mem,
+ dump->dbg_info.rhd_len);
+
+ mutex_unlock(&priv->dbgdump_elem.mutex);
+ return read;
+}
+
+DEBUGFS_READ_FILE_OPS(rhd);
+
+static ssize_t rwnx_dbgfs_rbd_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct rwnx_hw *priv = file->private_data;
+ struct dbg_debug_dump_tag *dump = priv->dbgdump_elem.buf.addr;
+ ssize_t read;
+
+ mutex_lock(&priv->dbgdump_elem.mutex);
+ if (!priv->debugfs.trace_prst) {
+ mutex_unlock(&priv->dbgdump_elem.mutex);
+ return 0;
+ }
+
+ read = simple_read_from_buffer(user_buf, count, ppos,
+ dump->rbd_mem,
+ dump->dbg_info.rbd_len);
+
+ mutex_unlock(&priv->dbgdump_elem.mutex);
+ return read;
+}
+
+DEBUGFS_READ_FILE_OPS(rbd);
+
+static ssize_t rwnx_dbgfs_thdx_read(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos, int idx)
+{
+ struct rwnx_hw *priv = file->private_data;
+ struct dbg_debug_dump_tag *dump = priv->dbgdump_elem.buf.addr;
+ ssize_t read;
+
+ mutex_lock(&priv->dbgdump_elem.mutex);
+ if (!priv->debugfs.trace_prst) {
+ mutex_unlock(&priv->dbgdump_elem.mutex);
+ return 0;
+ }
+
+ read = simple_read_from_buffer(user_buf, count, ppos,
+ &dump->thd_mem[idx],
+ dump->dbg_info.thd_len[idx]);
+
+ mutex_unlock(&priv->dbgdump_elem.mutex);
+ return read;
+}
+
+static ssize_t rwnx_dbgfs_thd0_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ return rwnx_dbgfs_thdx_read(file, user_buf, count, ppos, 0);
+}
+DEBUGFS_READ_FILE_OPS(thd0);
+
+static ssize_t rwnx_dbgfs_thd1_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ return rwnx_dbgfs_thdx_read(file, user_buf, count, ppos, 1);
+}
+DEBUGFS_READ_FILE_OPS(thd1);
+
+static ssize_t rwnx_dbgfs_thd2_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ return rwnx_dbgfs_thdx_read(file, user_buf, count, ppos, 2);
+}
+DEBUGFS_READ_FILE_OPS(thd2);
+
+static ssize_t rwnx_dbgfs_thd3_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ return rwnx_dbgfs_thdx_read(file, user_buf, count, ppos, 3);
+}
+DEBUGFS_READ_FILE_OPS(thd3);
+
+#if (NX_TXQ_CNT == 5)
+static ssize_t rwnx_dbgfs_thd4_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ return rwnx_dbgfs_thdx_read(file, user_buf, count, ppos, 4);
+}
+DEBUGFS_READ_FILE_OPS(thd4);
+#endif
+
+static ssize_t rwnx_dbgfs_mactrace_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct rwnx_hw *priv = file->private_data;
+ struct dbg_debug_dump_tag *dump = priv->dbgdump_elem.buf.addr;
+ ssize_t read;
+
+ mutex_lock(&priv->dbgdump_elem.mutex);
+ if (!priv->debugfs.trace_prst) {
+ char msg[64];
+
+ scnprintf(msg, sizeof(msg), "Force trigger\n");
+ rwnx_dbgfs_trigger_fw_dump(priv, msg);
+
+ mutex_unlock(&priv->dbgdump_elem.mutex);
+ return 0;
+ }
+
+ read = simple_read_from_buffer(user_buf, count, ppos,
+ dump->la_mem,
+ dump->dbg_info.la_conf.trace_len);
+
+ mutex_unlock(&priv->dbgdump_elem.mutex);
+
+ return read;
+}
+DEBUGFS_READ_FILE_OPS(mactrace);
+
+static ssize_t rwnx_dbgfs_macdiags_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct rwnx_hw *priv = file->private_data;
+ struct dbg_debug_dump_tag *dump = priv->dbgdump_elem.buf.addr;
+ ssize_t read;
+
+ mutex_lock(&priv->dbgdump_elem.mutex);
+ if (!priv->debugfs.trace_prst) {
+ mutex_unlock(&priv->dbgdump_elem.mutex);
+ return 0;
+ }
+
+ read = simple_read_from_buffer(user_buf, count, ppos,
+ dump->dbg_info.diags_mac,
+ DBG_DIAGS_MAC_MAX * 2);
+
+ mutex_unlock(&priv->dbgdump_elem.mutex);
+ return read;
+}
+
+DEBUGFS_READ_FILE_OPS(macdiags);
+
+static ssize_t rwnx_dbgfs_phydiags_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct rwnx_hw *priv = file->private_data;
+ struct dbg_debug_dump_tag *dump = priv->dbgdump_elem.buf.addr;
+ ssize_t read;
+
+ mutex_lock(&priv->dbgdump_elem.mutex);
+ if (!priv->debugfs.trace_prst) {
+ mutex_unlock(&priv->dbgdump_elem.mutex);
+ return 0;
+ }
+
+ read = simple_read_from_buffer(user_buf, count, ppos,
+ dump->dbg_info.diags_phy,
+ DBG_DIAGS_PHY_MAX * 2);
+
+ mutex_unlock(&priv->dbgdump_elem.mutex);
+ return read;
+}
+
+DEBUGFS_READ_FILE_OPS(phydiags);
+
+static ssize_t rwnx_dbgfs_hwdiags_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct rwnx_hw *priv = file->private_data;
+ struct dbg_debug_dump_tag *dump = priv->dbgdump_elem.buf.addr;
+ char buf[16];
+ int ret;
+
+ mutex_lock(&priv->dbgdump_elem.mutex);
+ if (!priv->debugfs.trace_prst) {
+ mutex_unlock(&priv->dbgdump_elem.mutex);
+ return 0;
+ }
+
+ ret = scnprintf(buf, min_t(size_t, sizeof(buf) - 1, count),
+ "%08X\n", dump->dbg_info.hw_diag);
+
+ mutex_unlock(&priv->dbgdump_elem.mutex);
+ return simple_read_from_buffer(user_buf, count, ppos, buf, ret);
+}
+
+DEBUGFS_READ_FILE_OPS(hwdiags);
+
+static ssize_t rwnx_dbgfs_plfdiags_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct rwnx_hw *priv = file->private_data;
+ struct dbg_debug_dump_tag *dump = priv->dbgdump_elem.buf.addr;
+ char buf[16];
+ int ret;
+
+ mutex_lock(&priv->dbgdump_elem.mutex);
+ if (!priv->debugfs.trace_prst) {
+ mutex_unlock(&priv->dbgdump_elem.mutex);
+ return 0;
+ }
+
+ ret = scnprintf(buf, min_t(size_t, sizeof(buf) - 1, count),
+ "%08X\n", dump->dbg_info.la_conf.diag_conf);
+
+ mutex_unlock(&priv->dbgdump_elem.mutex);
+ return simple_read_from_buffer(user_buf, count, ppos, buf, ret);
+}
+
+DEBUGFS_READ_FILE_OPS(plfdiags);
+
+static ssize_t rwnx_dbgfs_swdiags_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct rwnx_hw *priv = file->private_data;
+ struct dbg_debug_dump_tag *dump = priv->dbgdump_elem.buf.addr;
+ ssize_t read;
+
+ mutex_lock(&priv->dbgdump_elem.mutex);
+ if (!priv->debugfs.trace_prst) {
+ mutex_unlock(&priv->dbgdump_elem.mutex);
+ return 0;
+ }
+
+ read = simple_read_from_buffer(user_buf, count, ppos,
+ &dump->dbg_info.sw_diag,
+ dump->dbg_info.sw_diag_len);
+
+ mutex_unlock(&priv->dbgdump_elem.mutex);
+ return read;
+}
+
+DEBUGFS_READ_FILE_OPS(swdiags);
+
+static ssize_t rwnx_dbgfs_error_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct rwnx_hw *priv = file->private_data;
+ struct dbg_debug_dump_tag *dump = priv->dbgdump_elem.buf.addr;
+ ssize_t read;
+
+ mutex_lock(&priv->dbgdump_elem.mutex);
+ if (!priv->debugfs.trace_prst) {
+ mutex_unlock(&priv->dbgdump_elem.mutex);
+ return 0;
+ }
+
+ read = simple_read_from_buffer(user_buf, count, ppos,
+ dump->dbg_info.error,
+ strlen((char *)dump->dbg_info.error));
+
+ mutex_unlock(&priv->dbgdump_elem.mutex);
+ return read;
+}
+
+DEBUGFS_READ_FILE_OPS(error);
+
+static ssize_t rwnx_dbgfs_rxdesc_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct rwnx_hw *priv = file->private_data;
+ struct dbg_debug_dump_tag *dump = priv->dbgdump_elem.buf.addr;
+ char buf[32];
+ int ret;
+ ssize_t read;
+
+ mutex_lock(&priv->dbgdump_elem.mutex);
+ if (!priv->debugfs.trace_prst) {
+ mutex_unlock(&priv->dbgdump_elem.mutex);
+ return 0;
+ }
+
+ ret = scnprintf(buf, min_t(size_t, sizeof(buf) - 1, count),
+ "%08X\n%08X\n", dump->dbg_info.rhd,
+ dump->dbg_info.rbd);
+ read = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
+
+ mutex_unlock(&priv->dbgdump_elem.mutex);
+ return read;
+}
+
+DEBUGFS_READ_FILE_OPS(rxdesc);
+
+static ssize_t rwnx_dbgfs_txdesc_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct rwnx_hw *priv = file->private_data;
+ struct dbg_debug_dump_tag *dump = priv->dbgdump_elem.buf.addr;
+ char buf[64];
+ int len = 0;
+ int i;
+
+ mutex_lock(&priv->dbgdump_elem.mutex);
+ if (!priv->debugfs.trace_prst) {
+ mutex_unlock(&priv->dbgdump_elem.mutex);
+ return 0;
+ }
+
+ for (i = 0; i < NX_TXQ_CNT; i++) {
+ len += scnprintf(&buf[len], min_t(size_t, sizeof(buf) - len - 1, count),
+ "%08X\n", dump->dbg_info.thd[i]);
+ }
+
+ mutex_unlock(&priv->dbgdump_elem.mutex);
+ return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+DEBUGFS_READ_FILE_OPS(txdesc);
+
+static ssize_t rwnx_dbgfs_macrxptr_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct rwnx_hw *priv = file->private_data;
+ struct dbg_debug_dump_tag *dump = priv->dbgdump_elem.buf.addr;
+ ssize_t read;
+
+ mutex_lock(&priv->dbgdump_elem.mutex);
+ if (!priv->debugfs.trace_prst) {
+ mutex_unlock(&priv->dbgdump_elem.mutex);
+ return 0;
+ }
+
+ read = simple_read_from_buffer(user_buf, count, ppos,
+ &dump->dbg_info.rhd_hw_ptr,
+ 2 * sizeof(dump->dbg_info.rhd_hw_ptr));
+
+ mutex_unlock(&priv->dbgdump_elem.mutex);
+ return read;
+}
+
+DEBUGFS_READ_FILE_OPS(macrxptr);
+
+static ssize_t rwnx_dbgfs_lamacconf_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct rwnx_hw *priv = file->private_data;
+ struct dbg_debug_dump_tag *dump = priv->dbgdump_elem.buf.addr;
+ ssize_t read;
+
+ mutex_lock(&priv->dbgdump_elem.mutex);
+ if (!priv->debugfs.trace_prst) {
+ mutex_unlock(&priv->dbgdump_elem.mutex);
+ return 0;
+ }
+
+ read = simple_read_from_buffer(user_buf, count, ppos,
+ dump->dbg_info.la_conf.conf,
+ LA_CONF_LEN * 4);
+
+ mutex_unlock(&priv->dbgdump_elem.mutex);
+ return read;
+}
+DEBUGFS_READ_FILE_OPS(lamacconf);
+
+static ssize_t rwnx_dbgfs_chaninfo_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct rwnx_hw *priv = file->private_data;
+ struct dbg_debug_dump_tag *dump = priv->dbgdump_elem.buf.addr;
+ char buf[4 * 32];
+ int ret;
+
+ mutex_lock(&priv->dbgdump_elem.mutex);
+ if (!priv->debugfs.trace_prst) {
+ mutex_unlock(&priv->dbgdump_elem.mutex);
+ return 0;
+ }
+
+ ret = scnprintf(buf, min_t(size_t, sizeof(buf) - 1, count),
+ "type: %d\n"
+ "prim20_freq: %d MHz\n"
+ "center1_freq: %d MHz\n"
+ "center2_freq: %d MHz\n",
+ (dump->dbg_info.chan_info.info1 >> 8) & 0xFF,
+ (dump->dbg_info.chan_info.info1 >> 16) & 0xFFFF,
+ (dump->dbg_info.chan_info.info2 >> 0) & 0xFFFF,
+ (dump->dbg_info.chan_info.info2 >> 16) & 0xFFFF);
+
+ mutex_unlock(&priv->dbgdump_elem.mutex);
+ return simple_read_from_buffer(user_buf, count, ppos, buf, ret);
+}
+
+DEBUGFS_READ_FILE_OPS(chaninfo);
+
+static ssize_t rwnx_dbgfs_um_helper_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct rwnx_hw *priv = file->private_data;
+ char buf[sizeof(priv->debugfs.helper_cmd)];
+ int ret;
+
+ ret = scnprintf(buf, min_t(size_t, sizeof(buf) - 1, count),
+ "%s", priv->debugfs.helper_cmd);
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, ret);
+}
+
+static ssize_t rwnx_dbgfs_um_helper_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct rwnx_hw *priv = file->private_data;
+ int eobuf = min_t(size_t, sizeof(priv->debugfs.helper_cmd) - 1, count);
+
+ priv->debugfs.helper_cmd[eobuf] = '\0';
+ if (copy_from_user(priv->debugfs.helper_cmd, user_buf, eobuf))
+ return -EFAULT;
+
+ return count;
+}
+
+DEBUGFS_READ_WRITE_FILE_OPS(um_helper);
+
+/*
+ * Calls a userspace pgm
+ */
+int rwnx_um_helper(struct rwnx_debugfs *rwnx_debugfs, const char *cmd)
+{
+ char *envp[] = { "PATH=/sbin:/usr/sbin:/bin:/usr/bin", NULL };
+ char **argv;
+ int argc, ret;
+
+ if (!rwnx_debugfs->dir ||
+ !strlen((cmd = cmd ? cmd : rwnx_debugfs->helper_cmd)))
+ return 0;
+ argv = argv_split(in_interrupt() ? GFP_ATOMIC : GFP_KERNEL, cmd, &argc);
+ if (!argc)
+ return PTR_ERR(argv);
+
+ if ((ret = call_usermodehelper(argv[0], argv, envp,
+ UMH_WAIT_PROC | UMH_KILLABLE)))
+ printk(KERN_CRIT "Failed to call %s (%s returned %d)\n",
+ argv[0], cmd, ret);
+ argv_free(argv);
+
+ return ret;
+}
+
+static void rwnx_um_helper_work(struct work_struct *ws)
+{
+ struct rwnx_debugfs *rwnx_debugfs = container_of(ws, struct rwnx_debugfs,
+ helper_work);
+ struct rwnx_hw *rwnx_hw = container_of(rwnx_debugfs, struct rwnx_hw,
+ debugfs);
+ rwnx_um_helper(rwnx_debugfs, NULL);
+ if (!rwnx_debugfs->unregistering)
+ rwnx_umh_done(rwnx_hw);
+ rwnx_debugfs->helper_scheduled = false;
+}
+
+int rwnx_trigger_um_helper(struct rwnx_debugfs *rwnx_debugfs)
+{
+ struct rwnx_hw *rwnx_hw = container_of(rwnx_debugfs, struct rwnx_hw,
+ debugfs);
+
+ if (rwnx_debugfs->helper_scheduled == true) {
+ dev_err(rwnx_hw->dev, "%s: Already scheduled\n", __func__);
+ return -EBUSY;
+ }
+
+ spin_lock_bh(&rwnx_debugfs->umh_lock);
+ if (rwnx_debugfs->unregistering) {
+ spin_unlock_bh(&rwnx_debugfs->umh_lock);
+ dev_err(rwnx_hw->dev, "%s: unregistering\n", __func__);
+ return -ENOENT;
+ }
+ rwnx_debugfs->helper_scheduled = true;
+ schedule_work(&rwnx_debugfs->helper_work);
+ spin_unlock_bh(&rwnx_debugfs->umh_lock);
+
+ return 0;
+}
+
+int rwnx_dbgfs_register_fw_dump(struct rwnx_hw *rwnx_hw,
+ struct dentry *dir_drv,
+ struct dentry *dir_diags)
+{
+
+ struct rwnx_debugfs *rwnx_debugfs = &rwnx_hw->debugfs;
+
+ BUILD_BUG_ON(sizeof(CONFIG_RWNX_UM_HELPER_DFLT) >=
+ sizeof(rwnx_debugfs->helper_cmd));
+ strncpy(rwnx_debugfs->helper_cmd,
+ CONFIG_RWNX_UM_HELPER_DFLT, sizeof(rwnx_debugfs->helper_cmd));
+ INIT_WORK(&rwnx_debugfs->helper_work, rwnx_um_helper_work);
+ DEBUGFS_ADD_FILE(um_helper, dir_drv, S_IWUSR | S_IRUSR);
+
+ rwnx_debugfs->trace_prst = rwnx_debugfs->helper_scheduled = false;
+ spin_lock_init(&rwnx_debugfs->umh_lock);
+ DEBUGFS_ADD_FILE(rhd, dir_diags, S_IRUSR);
+ DEBUGFS_ADD_FILE(rbd, dir_diags, S_IRUSR);
+ DEBUGFS_ADD_FILE(thd0, dir_diags, S_IRUSR);
+ DEBUGFS_ADD_FILE(thd1, dir_diags, S_IRUSR);
+ DEBUGFS_ADD_FILE(thd2, dir_diags, S_IRUSR);
+ DEBUGFS_ADD_FILE(thd3, dir_diags, S_IRUSR);
+#if (NX_TXQ_CNT == 5)
+ DEBUGFS_ADD_FILE(thd4, dir_diags, S_IRUSR);
+#endif
+ DEBUGFS_ADD_FILE(mactrace, dir_diags, S_IRUSR);
+ DEBUGFS_ADD_FILE(macdiags, dir_diags, S_IRUSR);
+ DEBUGFS_ADD_FILE(phydiags, dir_diags, S_IRUSR);
+ DEBUGFS_ADD_FILE(plfdiags, dir_diags, S_IRUSR);
+ DEBUGFS_ADD_FILE(hwdiags, dir_diags, S_IRUSR);
+ DEBUGFS_ADD_FILE(swdiags, dir_diags, S_IRUSR);
+ DEBUGFS_ADD_FILE(error, dir_diags, S_IRUSR);
+ DEBUGFS_ADD_FILE(rxdesc, dir_diags, S_IRUSR);
+ DEBUGFS_ADD_FILE(txdesc, dir_diags, S_IRUSR);
+ DEBUGFS_ADD_FILE(macrxptr, dir_diags, S_IRUSR);
+ DEBUGFS_ADD_FILE(lamacconf, dir_diags, S_IRUSR);
+ DEBUGFS_ADD_FILE(chaninfo, dir_diags, S_IRUSR);
+
+ return 0;
+
+ err:
+ return -1;
+}
diff --git a/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_fw_trace.c b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_fw_trace.c
new file mode 100644
index 000000000000..fa965830866a
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_fw_trace.c
@@ -0,0 +1,47 @@
+/**
+ ******************************************************************************
+ *
+ * @file rwnx_fw_trace.c
+ *
+ * Copyright (C) RivieraWaves 2017-2019
+ *
+ ******************************************************************************
+ */
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/delay.h>
+#include "rwnx_fw_trace.h"
+
+int rwnx_fw_log_init(struct rwnx_fw_log *fw_log)
+{
+ u8 *buf = kmalloc(FW_LOG_SIZE, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ fw_log->buf.data = buf;
+ fw_log->buf.start = fw_log->buf.data;
+ fw_log->buf.size = 0;
+ fw_log->buf.end = fw_log->buf.data;
+ fw_log->buf.dataend = fw_log->buf.data + FW_LOG_SIZE;
+ spin_lock_init(&fw_log->lock);
+
+ printk("fw_log_init: %lx, %lx\n", (unsigned long)fw_log->buf.start, (unsigned long)(fw_log->buf.dataend));
+ return 0;
+}
+
+void rwnx_fw_log_deinit(struct rwnx_fw_log *fw_log)
+{
+ if (!fw_log)
+ return;
+
+ if (fw_log->buf.data)
+ kfree(fw_log->buf.data);
+ fw_log->buf.start = NULL;
+ fw_log->buf.end = NULL;
+ fw_log->buf.size = 0;
+}
+
diff --git a/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_fw_trace.h b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_fw_trace.h
new file mode 100644
index 000000000000..7956790957c3
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_fw_trace.h
@@ -0,0 +1,35 @@
+/**
+ ******************************************************************************
+ *
+ * rwnx_fw_trace.h
+ *
+ * Copyright (C) RivieraWaves 2017-2019
+ *
+ ******************************************************************************
+ */
+
+#ifndef _RWNX_FW_TRACE_H_
+#define _RWNX_FW_TRACE_H_
+
+#include <linux/mutex.h>
+#include <linux/wait.h>
+#include <linux/workqueue.h>
+
+#define FW_LOG_SIZE (10240)
+
+struct rwnx_fw_log_buf {
+ uint8_t *data;
+ uint8_t *start;
+ uint8_t *end;
+ uint8_t *dataend;
+ uint32_t size;
+};
+
+struct rwnx_fw_log {
+ struct rwnx_fw_log_buf buf;
+ spinlock_t lock;
+};
+
+int rwnx_fw_log_init(struct rwnx_fw_log *fw_log);
+void rwnx_fw_log_deinit(struct rwnx_fw_log *fw_log);
+#endif /* _RWNX_FW_TRACE_H_ */
diff --git a/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_gki.c b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_gki.c
new file mode 100644
index 000000000000..a6bf565f5c09
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_gki.c
@@ -0,0 +1,18 @@
+#include <linux/version.h>
+#include <linux/skbuff.h>
+
+
+void rwnx_gki_skb_append(struct sk_buff *old, struct sk_buff *newsk, struct sk_buff_head *list)
+{
+ unsigned long flags;
+ struct sk_buff *prev = old;
+ struct sk_buff *next = prev->next;
+ spin_lock_irqsave(&list->lock, flags);
+ WRITE_ONCE(newsk->next, next);
+ WRITE_ONCE(newsk->prev, prev);
+ WRITE_ONCE(next->prev, newsk);
+ WRITE_ONCE(prev->next, newsk);
+ list->qlen++;
+ spin_unlock_irqrestore(&list->lock, flags);
+}
+
diff --git a/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_gki.h b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_gki.h
new file mode 100644
index 000000000000..5ec1291f8bd9
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_gki.h
@@ -0,0 +1,11 @@
+#ifndef __RWNX_GKI_H
+#define __RWNX_GKI_H
+
+#ifdef CONFIG_GKI
+void rwnx_gki_skb_append(struct sk_buff *old, struct sk_buff *newsk, struct sk_buff_head *list);
+#define rwnx_skb_append rwnx_gki_skb_append
+#else
+#define rwnx_skb_append skb_append
+#endif//CONFIG_GKI
+
+#endif
diff --git a/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_irqs.c b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_irqs.c
new file mode 100644
index 000000000000..65e24938d31a
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_irqs.c
@@ -0,0 +1,67 @@
+/**
+ ******************************************************************************
+ *
+ * @file rwnx_irqs.c
+ *
+ * Copyright (C) RivieraWaves 2012-2019
+ *
+ ******************************************************************************
+ */
+#include <linux/interrupt.h>
+
+#include "rwnx_defs.h"
+#include "ipc_host.h"
+#include "rwnx_prof.h"
+
+/**
+ * rwnx_irq_hdlr - IRQ handler
+ *
+ * Handler registerd by the platform driver
+ */
+irqreturn_t rwnx_irq_hdlr(int irq, void *dev_id)
+{
+ struct rwnx_hw *rwnx_hw = (struct rwnx_hw *)dev_id;
+ disable_irq_nosync(irq);
+ tasklet_schedule(&rwnx_hw->task);
+ return IRQ_HANDLED;
+}
+
+/**
+ * rwnx_task - Bottom half for IRQ handler
+ *
+ * Read irq status and process accordingly
+ */
+void rwnx_task(unsigned long data)
+{
+ struct rwnx_hw *rwnx_hw = (struct rwnx_hw *)data;
+ REG_SW_SET_PROFILING(rwnx_hw, SW_PROF_RWNX_IPC_IRQ_HDLR);
+
+#if 0
+ struct rwnx_plat *rwnx_plat = rwnx_hw->plat;
+ u32 status, statuses = 0;
+
+ /* Ack unconditionnally in case ipc_host_get_status does not see the irq */
+ rwnx_plat->ack_irq(rwnx_plat);
+
+ while ((status = ipc_host_get_status(rwnx_hw->ipc_env))) {
+ statuses |= status;
+ /* All kinds of IRQs will be handled in one shot (RX, MSG, DBG, ...)
+ * this will ack IPC irqs not the cfpga irqs */
+ ipc_host_irq(rwnx_hw->ipc_env, status);
+
+ rwnx_plat->ack_irq(rwnx_plat);
+ }
+#endif
+ //if (statuses & IPC_IRQ_E2A_RXDESC)
+ // rwnx_hw->stats.last_rx = now;
+ //if (statuses & IPC_IRQ_E2A_TXCFM)
+ // rwnx_hw->stats.last_tx = now;
+ AICWFDBG(LOGTRACE, "rwnx_task\n");
+ spin_lock_bh(&rwnx_hw->tx_lock);
+ rwnx_hwq_process_all(rwnx_hw);
+ spin_unlock_bh(&rwnx_hw->tx_lock);
+#if 0
+ enable_irq(rwnx_platform_get_irq(rwnx_plat));
+#endif
+ REG_SW_CLEAR_PROFILING(rwnx_hw, SW_PROF_RWNX_IPC_IRQ_HDLR);
+}
diff --git a/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_irqs.h b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_irqs.h
new file mode 100644
index 000000000000..d3fb4519c615
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_irqs.h
@@ -0,0 +1,20 @@
+/**
+ ******************************************************************************
+ *
+ * @file rwnx_irqs.h
+ *
+ * Copyright (C) RivieraWaves 2012-2019
+ *
+ ******************************************************************************
+ */
+#ifndef _RWNX_IRQS_H_
+#define _RWNX_IRQS_H_
+
+#include <linux/interrupt.h>
+
+/* IRQ handler to be registered by platform driver */
+irqreturn_t rwnx_irq_hdlr(int irq, void *dev_id);
+
+void rwnx_task(unsigned long data);
+
+#endif /* _RWNX_IRQS_H_ */
diff --git a/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_main.c b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_main.c
new file mode 100644
index 000000000000..3fb4879185ba
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_main.c
@@ -0,0 +1,9565 @@
+/**
+ ******************************************************************************
+ *
+ * @file rwnx_main.c
+ *
+ * @brief Entry point of the RWNX driver
+ *
+ * Copyright (C) RivieraWaves 2012-2019
+ *
+ ******************************************************************************
+ */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/inetdevice.h>
+#include <net/cfg80211.h>
+#include <net/ip.h>
+#include <linux/etherdevice.h>
+#include <linux/netdevice.h>
+#include <net/netlink.h>
+#include <linux/wireless.h>
+#include <linux/if_arp.h>
+#include <linux/ctype.h>
+#include <linux/random.h>
+#include "rwnx_defs.h"
+#include "rwnx_dini.h"
+#include "rwnx_msg_tx.h"
+#include "reg_access.h"
+#include "hal_desc.h"
+#include "rwnx_debugfs.h"
+#include "rwnx_cfgfile.h"
+#include "rwnx_irqs.h"
+#include "rwnx_radar.h"
+#include "rwnx_version.h"
+#ifdef CONFIG_RWNX_BFMER
+#include "rwnx_bfmer.h"
+#endif //(CONFIG_RWNX_BFMER)
+#include "rwnx_tdls.h"
+#include "rwnx_events.h"
+#include "rwnx_compat.h"
+#include "rwnx_version.h"
+#include "rwnx_main.h"
+#include "aicwf_txrxif.h"
+#include "aicwf_compat_8800dc.h"
+#include "aicwf_compat_8800d80.h"
+
+
+#ifdef CONFIG_USE_WIRELESS_EXT
+#include "aicwf_wext_linux.h"
+#endif
+
+#ifdef AICWF_SDIO_SUPPORT
+#include "aicwf_sdio.h"
+#endif
+#ifdef AICWF_USB_SUPPORT
+#include "aicwf_usb.h"
+#endif
+#include <linux/semaphore.h>
+
+#define RW_DRV_DESCRIPTION "RivieraWaves 11nac driver for Linux cfg80211"
+#define RW_DRV_COPYRIGHT "Copyright(c) 2015-2017 RivieraWaves"
+#define RW_DRV_AUTHOR "RivieraWaves S.A.S"
+
+#define RWNX_PRINT_CFM_ERR(req) \
+ printk(KERN_CRIT "%s: Status Error(%d)\n", #req, (&req##_cfm)->status)
+
+#define RWNX_HT_CAPABILITIES \
+{ \
+ .ht_supported = true, \
+ .cap = 0, \
+ .ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K, \
+ .ampdu_density = IEEE80211_HT_MPDU_DENSITY_16, \
+ .mcs = { \
+ .rx_mask = { 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, \
+ .rx_highest = cpu_to_le16(65), \
+ .tx_params = IEEE80211_HT_MCS_TX_DEFINED, \
+ }, \
+}
+
+#define RWNX_VHT_CAPABILITIES \
+{ \
+ .vht_supported = false, \
+ .cap = \
+ (7 << IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT),\
+ .vht_mcs = { \
+ .rx_mcs_map = cpu_to_le16( \
+ IEEE80211_VHT_MCS_SUPPORT_0_9 << 0 | \
+ IEEE80211_VHT_MCS_NOT_SUPPORTED << 2 | \
+ IEEE80211_VHT_MCS_NOT_SUPPORTED << 4 | \
+ IEEE80211_VHT_MCS_NOT_SUPPORTED << 6 | \
+ IEEE80211_VHT_MCS_NOT_SUPPORTED << 8 | \
+ IEEE80211_VHT_MCS_NOT_SUPPORTED << 10 | \
+ IEEE80211_VHT_MCS_NOT_SUPPORTED << 12 | \
+ IEEE80211_VHT_MCS_NOT_SUPPORTED << 14), \
+ .tx_mcs_map = cpu_to_le16( \
+ IEEE80211_VHT_MCS_SUPPORT_0_9 << 0 | \
+ IEEE80211_VHT_MCS_NOT_SUPPORTED << 2 | \
+ IEEE80211_VHT_MCS_NOT_SUPPORTED << 4 | \
+ IEEE80211_VHT_MCS_NOT_SUPPORTED << 6 | \
+ IEEE80211_VHT_MCS_NOT_SUPPORTED << 8 | \
+ IEEE80211_VHT_MCS_NOT_SUPPORTED << 10 | \
+ IEEE80211_VHT_MCS_NOT_SUPPORTED << 12 | \
+ IEEE80211_VHT_MCS_NOT_SUPPORTED << 14), \
+ } \
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0) || defined(CONFIG_HE_FOR_OLD_KERNEL)
+#define RWNX_HE_CAPABILITIES \
+{ \
+ .has_he = false, \
+ .he_cap_elem = { \
+ .mac_cap_info[0] = 0, \
+ .mac_cap_info[1] = 0, \
+ .mac_cap_info[2] = 0, \
+ .mac_cap_info[3] = 0, \
+ .mac_cap_info[4] = 0, \
+ .mac_cap_info[5] = 0, \
+ .phy_cap_info[0] = 0, \
+ .phy_cap_info[1] = 0, \
+ .phy_cap_info[2] = 0, \
+ .phy_cap_info[3] = 0, \
+ .phy_cap_info[4] = 0, \
+ .phy_cap_info[5] = 0, \
+ .phy_cap_info[6] = 0, \
+ .phy_cap_info[7] = 0, \
+ .phy_cap_info[8] = 0, \
+ .phy_cap_info[9] = 0, \
+ .phy_cap_info[10] = 0, \
+ }, \
+ .he_mcs_nss_supp = { \
+ .rx_mcs_80 = cpu_to_le16(0xfffa), \
+ .tx_mcs_80 = cpu_to_le16(0xfffa), \
+ .rx_mcs_160 = cpu_to_le16(0xffff), \
+ .tx_mcs_160 = cpu_to_le16(0xffff), \
+ .rx_mcs_80p80 = cpu_to_le16(0xffff), \
+ .tx_mcs_80p80 = cpu_to_le16(0xffff), \
+ }, \
+ .ppe_thres = {0x08, 0x1c, 0x07}, \
+}
+#else
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)
+#define RWNX_HE_CAPABILITIES \
+{ \
+ .has_he = false, \
+ .he_cap_elem = { \
+ .mac_cap_info[0] = 0, \
+ .mac_cap_info[1] = 0, \
+ .mac_cap_info[2] = 0, \
+ .mac_cap_info[3] = 0, \
+ .mac_cap_info[4] = 0, \
+ .phy_cap_info[0] = 0, \
+ .phy_cap_info[1] = 0, \
+ .phy_cap_info[2] = 0, \
+ .phy_cap_info[3] = 0, \
+ .phy_cap_info[4] = 0, \
+ .phy_cap_info[5] = 0, \
+ .phy_cap_info[6] = 0, \
+ .phy_cap_info[7] = 0, \
+ .phy_cap_info[8] = 0, \
+ }, \
+ .he_mcs_nss_supp = { \
+ .rx_mcs_80 = cpu_to_le16(0xfffa), \
+ .tx_mcs_80 = cpu_to_le16(0xfffa), \
+ .rx_mcs_160 = cpu_to_le16(0xffff), \
+ .tx_mcs_160 = cpu_to_le16(0xffff), \
+ .rx_mcs_80p80 = cpu_to_le16(0xffff), \
+ .tx_mcs_80p80 = cpu_to_le16(0xffff), \
+ }, \
+ .ppe_thres = {0x08, 0x1c, 0x07}, \
+}
+#endif
+#endif
+
+#define RATE(_bitrate, _hw_rate, _flags) { \
+ .bitrate = (_bitrate), \
+ .flags = (_flags), \
+ .hw_value = (_hw_rate), \
+}
+
+#define CHAN(_freq) { \
+ .center_freq = (_freq), \
+ .max_power = 30, /* FIXME */ \
+}
+
+#ifdef ANDROID_PLATFORM
+#define HIGH_KERNEL_VERSION KERNEL_VERSION(5, 15, 41)
+#else
+#define HIGH_KERNEL_VERSION KERNEL_VERSION(5, 0, 0)
+#endif
+
+
+static struct ieee80211_rate rwnx_ratetable[] = {
+ RATE(10, 0x00, 0),
+ RATE(20, 0x01, IEEE80211_RATE_SHORT_PREAMBLE),
+ RATE(55, 0x02, IEEE80211_RATE_SHORT_PREAMBLE),
+ RATE(110, 0x03, IEEE80211_RATE_SHORT_PREAMBLE),
+ RATE(60, 0x04, 0),
+ RATE(90, 0x05, 0),
+ RATE(120, 0x06, 0),
+ RATE(180, 0x07, 0),
+ RATE(240, 0x08, 0),
+ RATE(360, 0x09, 0),
+ RATE(480, 0x0A, 0),
+ RATE(540, 0x0B, 0),
+};
+
+/* The channels indexes here are not used anymore */
+static struct ieee80211_channel rwnx_2ghz_channels[] = {
+ CHAN(2412),
+ CHAN(2417),
+ CHAN(2422),
+ CHAN(2427),
+ CHAN(2432),
+ CHAN(2437),
+ CHAN(2442),
+ CHAN(2447),
+ CHAN(2452),
+ CHAN(2457),
+ CHAN(2462),
+ CHAN(2467),
+ CHAN(2472),
+ CHAN(2484),
+ // Extra channels defined only to be used for PHY measures.
+ // Enabled only if custregd and custchan parameters are set
+ CHAN(2390),
+ CHAN(2400),
+ CHAN(2410),
+ CHAN(2420),
+ CHAN(2430),
+ CHAN(2440),
+ CHAN(2450),
+ CHAN(2460),
+ CHAN(2470),
+ CHAN(2480),
+ CHAN(2490),
+ CHAN(2500),
+ CHAN(2510),
+};
+
+//#ifdef USE_5G
+static struct ieee80211_channel rwnx_5ghz_channels[] = {
+ CHAN(5180), // 36 - 20MHz
+ CHAN(5200), // 40 - 20MHz
+ CHAN(5220), // 44 - 20MHz
+ CHAN(5240), // 48 - 20MHz
+ CHAN(5260), // 52 - 20MHz
+ CHAN(5280), // 56 - 20MHz
+ CHAN(5300), // 60 - 20MHz
+ CHAN(5320), // 64 - 20MHz
+ CHAN(5500), // 100 - 20MHz
+ CHAN(5520), // 104 - 20MHz
+ CHAN(5540), // 108 - 20MHz
+ CHAN(5560), // 112 - 20MHz
+ CHAN(5580), // 116 - 20MHz
+ CHAN(5600), // 120 - 20MHz
+ CHAN(5620), // 124 - 20MHz
+ CHAN(5640), // 128 - 20MHz
+ CHAN(5660), // 132 - 20MHz
+ CHAN(5680), // 136 - 20MHz
+ CHAN(5700), // 140 - 20MHz
+ CHAN(5720), // 144 - 20MHz
+ CHAN(5745), // 149 - 20MHz
+ CHAN(5765), // 153 - 20MHz
+ CHAN(5785), // 157 - 20MHz
+ CHAN(5805), // 161 - 20MHz
+ CHAN(5825), // 165 - 20MHz
+ // Extra channels defined only to be used for PHY measures.
+ // Enabled only if custregd and custchan parameters are set
+ CHAN(5190),
+ CHAN(5210),
+ CHAN(5230),
+ CHAN(5250),
+ CHAN(5270),
+ CHAN(5290),
+ CHAN(5310),
+ CHAN(5330),
+ CHAN(5340),
+ CHAN(5350),
+ CHAN(5360),
+ CHAN(5370),
+ CHAN(5380),
+ CHAN(5390),
+ CHAN(5400),
+ CHAN(5410),
+ CHAN(5420),
+ CHAN(5430),
+ CHAN(5440),
+ CHAN(5450),
+ CHAN(5460),
+ CHAN(5470),
+ CHAN(5480),
+ CHAN(5490),
+ CHAN(5510),
+ CHAN(5530),
+ CHAN(5550),
+ CHAN(5570),
+ CHAN(5590),
+ CHAN(5610),
+ CHAN(5630),
+ CHAN(5650),
+ CHAN(5670),
+ CHAN(5690),
+ CHAN(5710),
+ CHAN(5730),
+ CHAN(5750),
+ CHAN(5760),
+ CHAN(5770),
+ CHAN(5780),
+ CHAN(5790),
+ CHAN(5800),
+ CHAN(5810),
+ CHAN(5820),
+ CHAN(5830),
+ CHAN(5840),
+ CHAN(5850),
+ CHAN(5860),
+ CHAN(5870),
+ CHAN(5880),
+ CHAN(5890),
+ CHAN(5900),
+ CHAN(5910),
+ CHAN(5920),
+ CHAN(5930),
+ CHAN(5940),
+ CHAN(5950),
+ CHAN(5960),
+ CHAN(5970),
+};
+//#endif
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)) || defined(CONFIG_HE_FOR_OLD_KERNEL)
+struct ieee80211_sband_iftype_data rwnx_he_capa = {
+ .types_mask = BIT(NL80211_IFTYPE_STATION)|BIT(NL80211_IFTYPE_AP),
+ .he_cap = RWNX_HE_CAPABILITIES,
+};
+#endif
+
+static struct ieee80211_supported_band rwnx_band_2GHz = {
+ .channels = rwnx_2ghz_channels,
+ .n_channels = ARRAY_SIZE(rwnx_2ghz_channels) - 13, // -13 to exclude extra channels
+ .bitrates = rwnx_ratetable,
+ .n_bitrates = ARRAY_SIZE(rwnx_ratetable),
+ .ht_cap = RWNX_HT_CAPABILITIES,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)
+ .vht_cap = RWNX_VHT_CAPABILITIES,
+#endif
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)
+ .iftype_data = &rwnx_he_capa,
+ .n_iftype_data = 1,
+#endif
+};
+
+//#ifdef USE_5G
+static struct ieee80211_supported_band rwnx_band_5GHz = {
+ .channels = rwnx_5ghz_channels,
+ .n_channels = ARRAY_SIZE(rwnx_5ghz_channels) - 59, // -59 to exclude extra channels
+ .bitrates = &rwnx_ratetable[4],
+ .n_bitrates = ARRAY_SIZE(rwnx_ratetable) - 4,
+ .ht_cap = RWNX_HT_CAPABILITIES,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)
+ .vht_cap = RWNX_VHT_CAPABILITIES,
+#endif
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)
+ .iftype_data = &rwnx_he_capa,
+ .n_iftype_data = 1,
+#endif
+};
+//#endif
+
+static struct ieee80211_iface_limit rwnx_limits[] = {
+ { .max = 1,
+ .types = BIT(NL80211_IFTYPE_STATION)},
+ { .max = 1,
+ .types = BIT(NL80211_IFTYPE_AP)},
+#ifdef CONFIG_USE_P2P0
+ { .max = 2,
+#else
+ { .max = 1,
+#endif
+ .types = BIT(NL80211_IFTYPE_P2P_CLIENT) | BIT(NL80211_IFTYPE_P2P_GO)},
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 7, 0)
+#ifndef CONFIG_USE_P2P0
+ { .max = 1,
+ .types = BIT(NL80211_IFTYPE_P2P_DEVICE),
+ }
+#endif
+#endif
+};
+
+static struct ieee80211_iface_limit rwnx_limits_dfs[] = {
+ { .max = NX_VIRT_DEV_MAX, .types = BIT(NL80211_IFTYPE_AP)}
+};
+
+static const struct ieee80211_iface_combination rwnx_combinations[] = {
+ {
+ .limits = rwnx_limits,
+ .n_limits = ARRAY_SIZE(rwnx_limits),
+ .num_different_channels = NX_CHAN_CTXT_CNT,
+ .max_interfaces = NX_VIRT_DEV_MAX,
+ },
+ /* Keep this combination as the last one */
+ {
+ .limits = rwnx_limits_dfs,
+ .n_limits = ARRAY_SIZE(rwnx_limits_dfs),
+ .num_different_channels = 1,
+ .max_interfaces = NX_VIRT_DEV_MAX,
+ .radar_detect_widths = (BIT(NL80211_CHAN_WIDTH_20_NOHT) |
+ BIT(NL80211_CHAN_WIDTH_20) |
+ BIT(NL80211_CHAN_WIDTH_40) |
+ BIT(NL80211_CHAN_WIDTH_80)),
+ }
+};
+
+/* There isn't a lot of sense in it, but you can transmit anything you like */
+static struct ieee80211_txrx_stypes
+rwnx_default_mgmt_stypes[NUM_NL80211_IFTYPES] = {
+ [NL80211_IFTYPE_STATION] = {
+ .tx = 0xffff,
+ .rx = (BIT(IEEE80211_STYPE_ACTION >> 4) |
+ BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
+ BIT(IEEE80211_STYPE_AUTH >> 4)),
+ },
+ [NL80211_IFTYPE_AP] = {
+ .tx = 0xffff,
+ .rx = (BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |
+ BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) |
+ BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
+ BIT(IEEE80211_STYPE_DISASSOC >> 4) |
+ BIT(IEEE80211_STYPE_AUTH >> 4) |
+ BIT(IEEE80211_STYPE_DEAUTH >> 4) |
+ BIT(IEEE80211_STYPE_ACTION >> 4)),
+ },
+ [NL80211_IFTYPE_AP_VLAN] = {
+ /* copy AP */
+ .tx = 0xffff,
+ .rx = (BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |
+ BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) |
+ BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
+ BIT(IEEE80211_STYPE_DISASSOC >> 4) |
+ BIT(IEEE80211_STYPE_AUTH >> 4) |
+ BIT(IEEE80211_STYPE_DEAUTH >> 4) |
+ BIT(IEEE80211_STYPE_ACTION >> 4)),
+ },
+ [NL80211_IFTYPE_P2P_CLIENT] = {
+ .tx = 0xffff,
+ .rx = (BIT(IEEE80211_STYPE_ACTION >> 4) |
+ BIT(IEEE80211_STYPE_PROBE_REQ >> 4)),
+ },
+ [NL80211_IFTYPE_P2P_GO] = {
+ .tx = 0xffff,
+ .rx = (BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |
+ BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) |
+ BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
+ BIT(IEEE80211_STYPE_DISASSOC >> 4) |
+ BIT(IEEE80211_STYPE_AUTH >> 4) |
+ BIT(IEEE80211_STYPE_DEAUTH >> 4) |
+ BIT(IEEE80211_STYPE_ACTION >> 4)),
+ },
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 7, 0)
+ [NL80211_IFTYPE_P2P_DEVICE] = {
+ .tx = 0xffff,
+ .rx = (BIT(IEEE80211_STYPE_ACTION >> 4) |
+ BIT(IEEE80211_STYPE_PROBE_REQ >> 4)),
+ },
+#endif
+ [NL80211_IFTYPE_MESH_POINT] = {
+ .tx = 0xffff,
+ .rx = (BIT(IEEE80211_STYPE_ACTION >> 4) |
+ BIT(IEEE80211_STYPE_AUTH >> 4) |
+ BIT(IEEE80211_STYPE_DEAUTH >> 4)),
+ },
+};
+
+
+static u32 cipher_suites[] = {
+ WLAN_CIPHER_SUITE_WEP40,
+ WLAN_CIPHER_SUITE_WEP104,
+ WLAN_CIPHER_SUITE_TKIP,
+ WLAN_CIPHER_SUITE_CCMP,
+ WLAN_CIPHER_SUITE_AES_CMAC, // reserved entries to enable AES-CMAC and/or SMS4
+ WLAN_CIPHER_SUITE_SMS4,
+ 0,
+};
+#define NB_RESERVED_CIPHER 1;
+
+static const int rwnx_ac2hwq[1][NL80211_NUM_ACS] = {
+ {
+ [NL80211_TXQ_Q_VO] = RWNX_HWQ_VO,
+ [NL80211_TXQ_Q_VI] = RWNX_HWQ_VI,
+ [NL80211_TXQ_Q_BE] = RWNX_HWQ_BE,
+ [NL80211_TXQ_Q_BK] = RWNX_HWQ_BK
+ }
+};
+
+const int rwnx_tid2hwq[IEEE80211_NUM_TIDS] = {
+ RWNX_HWQ_BE,
+ RWNX_HWQ_BK,
+ RWNX_HWQ_BK,
+ RWNX_HWQ_BE,
+ RWNX_HWQ_VI,
+ RWNX_HWQ_VI,
+ RWNX_HWQ_VO,
+ RWNX_HWQ_VO,
+ /* TID_8 is used for management frames */
+ RWNX_HWQ_VO,
+ /* At the moment, all others TID are mapped to BE */
+ RWNX_HWQ_BE,
+ RWNX_HWQ_BE,
+ RWNX_HWQ_BE,
+ RWNX_HWQ_BE,
+ RWNX_HWQ_BE,
+ RWNX_HWQ_BE,
+ RWNX_HWQ_BE,
+};
+
+static const int rwnx_hwq2uapsd[NL80211_NUM_ACS] = {
+ [RWNX_HWQ_VO] = IEEE80211_WMM_IE_STA_QOSINFO_AC_VO,
+ [RWNX_HWQ_VI] = IEEE80211_WMM_IE_STA_QOSINFO_AC_VI,
+ [RWNX_HWQ_BE] = IEEE80211_WMM_IE_STA_QOSINFO_AC_BE,
+ [RWNX_HWQ_BK] = IEEE80211_WMM_IE_STA_QOSINFO_AC_BK,
+};
+
+#define P2P_ALIVE_TIME_MS (1*1000)
+#define P2P_ALIVE_TIME_COUNT 200
+
+extern uint8_t scanning;
+extern uint8_t p2p_working;
+struct semaphore aicwf_deinit_sem;
+atomic_t aicwf_deinit_atomic;
+
+int aicwf_dbg_level = LOGERROR|LOGINFO;
+module_param(aicwf_dbg_level, int, 0660);
+
+int testmode = 0;
+char aic_fw_path[200];
+
+extern void set_testmode(int);
+
+
+void rwnx_skb_align_8bytes(struct sk_buff *skb){
+#ifdef CONFIG_ALIGN_8BYTES
+ int align __maybe_unused;
+ u8 *data;
+ size_t len = 0;
+
+ align = ((unsigned long)(skb->data + 14)) & 7;
+ if (align) {
+ if (WARN_ON(skb_headroom(skb) < 7)) {
+ dev_kfree_skb(skb);
+ skb = NULL;
+ } else {
+ //printk("AIDEN align1:%d rx_skb->data + 14:%p\r\n", align, skb->data + 14);
+ data = skb->data;
+ len = skb_headlen(skb);
+ skb->data -= align;
+ memmove(skb->data, data, len);
+ skb_set_tail_pointer(skb, len);
+ //printk("AIDEN align2:%d rx_skb->data + 14:%p\r\n", align, skb->data + 14);
+ }
+ }
+#endif
+}
+
+int rwnx_init_cmd_array(void);
+void rwnx_free_cmd_array(void);
+
+void rwnx_data_dump(char* tag, void* data, unsigned long len){
+ unsigned long i = 0;
+ char* data_ = (char* )data;
+
+ AICWFDBG(LOGDATA, "%s %s len:(%lu)\r\n", __func__, tag, len);
+
+ for (i = 0; i < len; i += 16){
+ AICWFDBG(LOGDATA, "%02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\r\n",
+ data_[0 + i],
+ data_[1 + i],
+ data_[2 + i],
+ data_[3 + i],
+ data_[4 + i],
+ data_[5 + i],
+ data_[6 + i],
+ data_[7 + i],
+ data_[8 + i],
+ data_[9 + i],
+ data_[10 + i],
+ data_[11 + i],
+ data_[12 + i],
+ data_[13 + i],
+ data_[14 + i],
+ data_[15 + i]);
+ }
+
+}
+
+#define ASSOC_REQ 0x00
+#define ASSOC_RSP 0x10
+#define PROBE_REQ 0x40
+#define PROBE_RSP 0x50
+#define ACTION 0xD0
+#define AUTH 0xB0
+#define DEAUTH 0xC0
+#define QOS 0x88
+
+#define ACTION_MAC_HDR_LEN 24
+#define QOS_MAC_HDR_LEN 26
+
+void rwnx_frame_parser(char* tag, char* data, unsigned long len){
+ char print_data[100];
+ int print_index = 0;
+
+ memset(print_data, 0, 100);
+
+ if(data[0] == ASSOC_REQ){
+ sprintf(&print_data[print_index], "%s", "ASSOC_REQ \r\n");
+ print_index = strlen(print_data);
+ }else if(data[0] == ASSOC_RSP){
+ sprintf(&print_data[print_index], "%s", "ASSOC_RSP \r\n");
+ print_index = strlen(print_data);
+ }else if(data[0] == PROBE_REQ){
+ sprintf(&print_data[print_index], "%s", "PROBE_REQ \r\n");
+ print_index = strlen(print_data);
+ }else if(data[0] == PROBE_RSP){
+ sprintf(&print_data[print_index], "%s", "PROBE_RSP \r\n");
+ print_index = strlen(print_data);
+ }else if(data[0] == ACTION){
+ sprintf(&print_data[print_index], "%s", "ACTION ");
+ print_index = strlen(print_data);
+ if(data[ACTION_MAC_HDR_LEN] == 0x04 && data[ACTION_MAC_HDR_LEN + 6] == 0x00){
+ sprintf(&print_data[print_index], "%s", "GO_NEG_REQ \r\n");
+ }else if(data[ACTION_MAC_HDR_LEN] == 0x04 && data[ACTION_MAC_HDR_LEN + 6] == 0x01){
+ sprintf(&print_data[print_index], "%s", "GO_NEG_RSP \r\n");
+ }else if(data[ACTION_MAC_HDR_LEN] == 0x04 && data[ACTION_MAC_HDR_LEN + 6] == 0x02){
+ sprintf(&print_data[print_index], "%s", "GO_NEG_CFM \r\n");
+ }else if(data[ACTION_MAC_HDR_LEN] == 0x04 && data[ACTION_MAC_HDR_LEN + 6] == 0x03){
+ sprintf(&print_data[print_index], "%s", "P2P_INV_REQ \r\n");
+ }else if(data[ACTION_MAC_HDR_LEN] == 0x04 && data[ACTION_MAC_HDR_LEN + 6] == 0x04){
+ sprintf(&print_data[print_index], "%s", "P2P_INV_RSP \r\n");
+ }else if(data[ACTION_MAC_HDR_LEN] == 0x04 && data[ACTION_MAC_HDR_LEN + 6] == 0x05){
+ sprintf(&print_data[print_index], "%s", "DD_REQ \r\n");
+ }else if(data[ACTION_MAC_HDR_LEN] == 0x04 && data[ACTION_MAC_HDR_LEN + 6] == 0x06){
+ sprintf(&print_data[print_index], "%s", "DD_RSP \r\n");
+ }else if(data[ACTION_MAC_HDR_LEN] == 0x04 && data[ACTION_MAC_HDR_LEN + 6] == 0x07){
+ sprintf(&print_data[print_index], "%s", "PD_REQ \r\n");
+ }else if(data[ACTION_MAC_HDR_LEN] == 0x04 && data[ACTION_MAC_HDR_LEN + 6] == 0x08){
+ sprintf(&print_data[print_index], "%s", "PD_RSP \r\n");
+ }else{
+ sprintf(&print_data[print_index], "%s(0x%x 0x%x) \r\n", "UNKNOW",
+ data[ACTION_MAC_HDR_LEN], data[ACTION_MAC_HDR_LEN + 6]);
+ }
+ print_index = strlen(print_data);
+ }else if(data[0] == AUTH){
+ sprintf(&print_data[print_index], "%s", "AUTH \r\n");
+ print_index = strlen(print_data);
+ }else if(data[0] == DEAUTH){
+ sprintf(&print_data[print_index], "%s", "DEAUTH \r\n");
+ print_index = strlen(print_data);
+ }else if(data[0] == QOS){
+ if(data[QOS_MAC_HDR_LEN + 6] == 0x88 && data[QOS_MAC_HDR_LEN + 7] == 0x8E){
+ sprintf(&print_data[print_index], "%s", "QOS_802.1X ");
+ if(data[QOS_MAC_HDR_LEN + 9] == 0x03){
+ sprintf(&print_data[print_index], "%s", "EAPOL \r\n");
+ }else if(data[QOS_MAC_HDR_LEN + 9] == 0x00){
+ sprintf(&print_data[print_index], "%s", "EAP_PACKAGE ");
+ print_index = strlen(print_data);
+ if(data[QOS_MAC_HDR_LEN + 12] == 0x01){
+ sprintf(&print_data[print_index], "%s", "EAP_REQ \r\n");
+ }else if(data[QOS_MAC_HDR_LEN + 12] == 0x02){
+ sprintf(&print_data[print_index], "%s", "EAP_RSP \r\n");
+ }else if(data[QOS_MAC_HDR_LEN + 12] == 0x04){
+ sprintf(&print_data[print_index], "%s", "EAP_FAIL \r\n");
+ }else{
+ sprintf(&print_data[print_index], "%s", "UNKNOW \r\n");
+ }
+ }else if(data[QOS_MAC_HDR_LEN + 9] == 0x01){
+ sprintf(&print_data[print_index], "%s","EAP_START \r\n");
+ }
+ print_index = strlen(print_data);
+ }
+ }
+
+ if(print_index > 0){
+ AICWFDBG(LOGDATA, "%s %s", tag, print_data);
+ }
+
+#if 0
+ if(data[0] == ASSOC_REQ){
+ printk("%s %s ASSOC_REQ \r\n", __func__, tag);
+ }else if(data[0] == ASSOC_RSP){
+ printk("%s %s ASSOC_RSP \r\n", __func__, tag);
+ }else if(data[0] == PROBE_REQ){
+ printk("%s %s PROBE_REQ \r\n", __func__, tag);
+ }else if(data[0] == PROBE_RSP){
+ printk("%s %s PROBE_RSP \r\n", __func__, tag);
+ }else if(data[0] == ACTION){
+ printk("%s %s ACTION ", __func__, tag);
+ if(data[ACTION_MAC_HDR_LEN] == 0x04 && data[ACTION_MAC_HDR_LEN + 6] == 0x00){
+ printk("GO NEG REQ \r\n");
+ }else if(data[ACTION_MAC_HDR_LEN] == 0x04 && data[ACTION_MAC_HDR_LEN + 6] == 0x01){
+ printk("GO NEG RSP \r\n");
+ }else if(data[ACTION_MAC_HDR_LEN] == 0x04 && data[ACTION_MAC_HDR_LEN + 6] == 0x02){
+ printk("GO NEG CFM \r\n");
+ }else if(data[ACTION_MAC_HDR_LEN] == 0x04 && data[ACTION_MAC_HDR_LEN + 6] == 0x03){
+ printk("P2P INV REQ \r\n");
+ }else if(data[ACTION_MAC_HDR_LEN] == 0x04 && data[ACTION_MAC_HDR_LEN + 6] == 0x04){
+ printk("P2P INV RSP \r\n");
+ }else if(data[ACTION_MAC_HDR_LEN] == 0x04 && data[ACTION_MAC_HDR_LEN + 6] == 0x05){
+ printk("DD REQ \r\n");
+ }else if(data[ACTION_MAC_HDR_LEN] == 0x04 && data[ACTION_MAC_HDR_LEN + 6] == 0x06){
+ printk("DD RSP \r\n");
+ }else if(data[ACTION_MAC_HDR_LEN] == 0x04 && data[ACTION_MAC_HDR_LEN + 6] == 0x07){
+ printk("PD REQ \r\n");
+ }else if(data[ACTION_MAC_HDR_LEN] == 0x04 && data[ACTION_MAC_HDR_LEN + 6] == 0x08){
+ printk("PD RSP \r\n");
+ }else{
+ printk("\r\n");
+ }
+
+ }else if(data[0] == AUTH){
+ printk("%s %s AUTH \r\n", __func__, tag);
+ }else if(data[0] == DEAUTH){
+ printk("%s %s DEAUTH \r\n", __func__, tag);
+ }else if(data[0] == QOS){
+ if(data[QOS_MAC_HDR_LEN + 6] == 0x88 && data[QOS_MAC_HDR_LEN + 7] == 0x8E){
+ printk("%s %s QOS 802.1X ", __func__, tag);
+ if(data[QOS_MAC_HDR_LEN + 9] == 0x03){
+ printk("EAPOL");
+ }else if(data[QOS_MAC_HDR_LEN + 9] == 0x00){
+ printk("EAP PACKAGE ");
+ if(data[QOS_MAC_HDR_LEN + 12] == 0x01){
+ printk("EAP REQ\r\n");
+ }else if(data[QOS_MAC_HDR_LEN + 12] == 0x02){
+ printk("EAP RSP\r\n");
+ }else if(data[QOS_MAC_HDR_LEN + 12] == 0x04){
+ printk("EAP FAIL\r\n");
+ }else{
+ printk("\r\n");
+ }
+ }else if(data[QOS_MAC_HDR_LEN + 9] == 0x01){
+ printk("EAP START \r\n");
+
+ }
+ }
+ }
+#endif
+}
+
+/*********************************************************************
+ * helper
+ *********************************************************************/
+struct rwnx_sta *rwnx_get_sta(struct rwnx_hw *rwnx_hw, const u8 *mac_addr)
+{
+ int i;
+
+ for (i = 0; i < NX_REMOTE_STA_MAX; i++) {
+ struct rwnx_sta *sta = &rwnx_hw->sta_table[i];
+ if (sta->valid && (memcmp(mac_addr, &sta->mac_addr, 6) == 0))
+ return sta;
+ }
+
+ return NULL;
+}
+
+void rwnx_enable_wapi(struct rwnx_hw *rwnx_hw)
+{
+ //cipher_suites[rwnx_hw->wiphy->n_cipher_suites] = WLAN_CIPHER_SUITE_SMS4;
+ rwnx_hw->wiphy->n_cipher_suites ++;
+ rwnx_hw->wiphy->flags |= WIPHY_FLAG_CONTROL_PORT_PROTOCOL;
+}
+
+void rwnx_enable_mfp(struct rwnx_hw *rwnx_hw)
+{
+ cipher_suites[rwnx_hw->wiphy->n_cipher_suites] = WLAN_CIPHER_SUITE_AES_CMAC;
+ rwnx_hw->wiphy->n_cipher_suites ++;
+}
+
+#ifdef CONFIG_SET_VENDOR_EXTENSION_IE
+extern u8_l vendor_extension_data[256];
+extern u8_l vendor_extension_len;
+
+void rwnx_insert_vendor_extension_in_bcn(struct rwnx_bcn *bcn){
+ u8_l temp_ie[256];
+ u8_l vendor_extension_subelement[3] = {0x00,0x37,0x2A};
+ u8_l vendor_extension_id[2] = {0x10,0x49};
+ u8_l wps_ie[1] = {0xDD};
+ u8_l wps_len_index = 0;
+ int index = 0;
+ int vendor_extension_subelement_len = 0;
+ int find_wps_ie = 0;
+
+ memset(temp_ie, 0, 256);
+
+ //find wps_ie
+ for(index = 0; index < bcn->tail_len; index++){
+ if(bcn->tail[index] == wps_ie[0]){
+ find_wps_ie = 1;
+ wps_len_index = index + 1;
+ }
+
+ if(find_wps_ie && bcn->tail[index] == vendor_extension_id[0]){
+ if(bcn->tail[index + 1] == vendor_extension_id[1]){
+ break;
+ }
+ }
+ }
+
+
+ //find vendor_extension_subelement
+ for(index = 0; index < bcn->tail_len; index++){
+ if(bcn->tail[index] == vendor_extension_id[0]){
+ index++;
+ if(index == bcn->tail_len){
+ return;
+ }
+ if(bcn->tail[index] == vendor_extension_id[1] &&
+ bcn->tail[index + 3] == vendor_extension_subelement[0]&&
+ bcn->tail[index + 4] == vendor_extension_subelement[1]&&
+ bcn->tail[index + 5] == vendor_extension_subelement[2]){
+ index = index + 2;
+ vendor_extension_subelement_len = bcn->tail[index];
+ printk("%s find vendor_extension_subelement,index:%d len:%d\r\n", __func__, index, bcn->tail[index]);
+ break;
+ }
+ }
+ }
+ index = index + vendor_extension_subelement_len;
+
+ //insert vendor extension
+ memcpy(&temp_ie[0], bcn->tail, index + 1);
+ memcpy(&temp_ie[index + 1], vendor_extension_data, vendor_extension_len/*sizeof(vendor_extension_data)*/);//insert vendor extension data
+ memcpy(&temp_ie[index + 1 + vendor_extension_len/*sizeof(vendor_extension_data)*/], &bcn->tail[index + 1], bcn->tail_len - index);
+
+ memcpy(bcn->tail, temp_ie, bcn->tail_len + vendor_extension_len/*sizeof(vendor_extension_data)*/);
+ bcn->tail_len = bcn->tail_len + vendor_extension_len/*sizeof(vendor_extension_data)*/;
+
+ bcn->tail[wps_len_index] = bcn->tail[wps_len_index] + vendor_extension_len;
+ //rwnx_data_dump((char*)__func__, (void*)ie_req->ie, ie_req->add_ie_len);
+}
+#endif
+
+u8 *rwnx_build_bcn(struct rwnx_bcn *bcn, struct cfg80211_beacon_data *new)
+{
+ u8 *buf, *pos;
+
+ if (new->head) {
+ u8 *head = kmalloc(new->head_len, GFP_KERNEL);
+
+ if (!head)
+ return NULL;
+
+ if (bcn->head)
+ kfree(bcn->head);
+
+ bcn->head = head;
+ bcn->head_len = new->head_len;
+ memcpy(bcn->head, new->head, new->head_len);
+ }
+ if (new->tail) {
+ u8 *tail = kmalloc(new->tail_len, GFP_KERNEL);
+
+ if (!tail)
+ return NULL;
+
+ if (bcn->tail)
+ kfree(bcn->tail);
+
+ bcn->tail = tail;
+ bcn->tail_len = new->tail_len;
+ memcpy(bcn->tail, new->tail, new->tail_len);
+ }
+
+ if (!bcn->head)
+ return NULL;
+
+ bcn->tim_len = 6;
+ bcn->len = bcn->head_len + bcn->tail_len + bcn->ies_len + bcn->tim_len;
+#ifdef CONFIG_SET_VENDOR_EXTENSION_IE
+ buf = kmalloc(bcn->len + vendor_extension_len, GFP_KERNEL);
+#else
+ buf = kmalloc(bcn->len, GFP_KERNEL);
+#endif
+ if (!buf)
+ return NULL;
+
+ // Build the beacon buffer
+ pos = buf;
+ memcpy(pos, bcn->head, bcn->head_len);
+ pos += bcn->head_len;
+ *pos++ = WLAN_EID_TIM;
+ *pos++ = 4;
+ *pos++ = 0;
+ *pos++ = bcn->dtim;
+ *pos++ = 0;
+ *pos++ = 0;
+ if (bcn->tail) {
+#ifdef CONFIG_SET_VENDOR_EXTENSION_IE
+ rwnx_insert_vendor_extension_in_bcn(bcn);
+#endif
+ memcpy(pos, bcn->tail, bcn->tail_len);
+ pos += bcn->tail_len;
+ }
+ if (bcn->ies) {
+ memcpy(pos, bcn->ies, bcn->ies_len);
+ }
+
+ return buf;
+}
+
+
+static void rwnx_del_bcn(struct rwnx_bcn *bcn)
+{
+ if (bcn->head) {
+ kfree(bcn->head);
+ bcn->head = NULL;
+ }
+ bcn->head_len = 0;
+
+ if (bcn->tail) {
+ kfree(bcn->tail);
+ bcn->tail = NULL;
+ }
+ bcn->tail_len = 0;
+
+ if (bcn->ies) {
+ kfree(bcn->ies);
+ bcn->ies = NULL;
+ }
+ bcn->ies_len = 0;
+ bcn->tim_len = 0;
+ bcn->dtim = 0;
+ bcn->len = 0;
+}
+
+/**
+ * Link channel ctxt to a vif and thus increments count for this context.
+ */
+void rwnx_chanctx_link(struct rwnx_vif *vif, u8 ch_idx,
+ struct cfg80211_chan_def *chandef)
+{
+ struct rwnx_chanctx *ctxt;
+
+ if (ch_idx >= NX_CHAN_CTXT_CNT) {
+ WARN(1, "Invalid channel ctxt id %d", ch_idx);
+ return;
+ }
+
+ vif->ch_index = ch_idx;
+ ctxt = &vif->rwnx_hw->chanctx_table[ch_idx];
+ ctxt->count++;
+
+ // For now chandef is NULL for STATION interface
+ if (chandef) {
+ if (!ctxt->chan_def.chan)
+ ctxt->chan_def = *chandef;
+ else {
+ // TODO. check that chandef is the same as the one already
+ // set for this ctxt
+ }
+ }
+}
+
+/**
+ * Unlink channel ctxt from a vif and thus decrements count for this context
+ */
+void rwnx_chanctx_unlink(struct rwnx_vif *vif)
+{
+ struct rwnx_chanctx *ctxt;
+
+ if (vif->ch_index == RWNX_CH_NOT_SET)
+ return;
+
+ ctxt = &vif->rwnx_hw->chanctx_table[vif->ch_index];
+
+ if (ctxt->count == 0) {
+ WARN(1, "Chan ctxt ref count is already 0");
+ } else {
+ ctxt->count--;
+ }
+
+ if (ctxt->count == 0) {
+ if (vif->ch_index == vif->rwnx_hw->cur_chanctx) {
+ /* If current chan ctxt is no longer linked to a vif
+ disable radar detection (no need to check if it was activated) */
+ rwnx_radar_detection_enable(&vif->rwnx_hw->radar,
+ RWNX_RADAR_DETECT_DISABLE,
+ RWNX_RADAR_RIU);
+ }
+ /* set chan to null, so that if this ctxt is relinked to a vif that
+ don't have channel information, don't use wrong information */
+ ctxt->chan_def.chan = NULL;
+ }
+ vif->ch_index = RWNX_CH_NOT_SET;
+}
+
+int rwnx_chanctx_valid(struct rwnx_hw *rwnx_hw, u8 ch_idx)
+{
+ if (ch_idx >= NX_CHAN_CTXT_CNT ||
+ rwnx_hw->chanctx_table[ch_idx].chan_def.chan == NULL) {
+ return 0;
+ }
+
+ return 1;
+}
+
+static void rwnx_del_csa(struct rwnx_vif *vif)
+{
+ //struct rwnx_hw *rwnx_hw = vif->rwnx_hw;
+ struct rwnx_csa *csa = vif->ap.csa;
+
+ if (!csa)
+ return;
+
+ rwnx_del_bcn(&csa->bcn);
+ kfree(csa);
+ vif->ap.csa = NULL;
+}
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)
+static void rwnx_csa_finish(struct work_struct *ws)
+{
+
+ struct rwnx_csa *csa = container_of(ws, struct rwnx_csa, work);
+ struct rwnx_vif *vif = csa->vif;
+ struct rwnx_hw *rwnx_hw = vif->rwnx_hw;
+ int error = csa->status;
+ u8 *buf, *pos;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ buf = kmalloc(csa->bcn.len, GFP_KERNEL);
+ if (!buf) {
+ printk ("%s buf fail\n", __func__);
+ return;
+ }
+ pos = buf;
+
+ memcpy(pos, csa->bcn.head, csa->bcn.head_len);
+ pos += csa->bcn.head_len;
+ *pos++ = WLAN_EID_TIM;
+ *pos++ = 4;
+ *pos++ = 0;
+ *pos++ = csa->bcn.dtim;
+ *pos++ = 0;
+ *pos++ = 0;
+ if (csa->bcn.tail) {
+ memcpy(pos, csa->bcn.tail, csa->bcn.tail_len);
+ pos += csa->bcn.tail_len;
+ }
+ if (csa->bcn.ies) {
+ memcpy(pos, csa->bcn.ies, csa->bcn.ies_len);
+ }
+
+ if (!error) {
+ error = rwnx_send_bcn(rwnx_hw, buf, vif->vif_index, csa->bcn.len);
+ if (error)
+ return;
+ error = rwnx_send_bcn_change(rwnx_hw, vif->vif_index, csa->elem.dma_addr,
+ csa->bcn.len, csa->bcn.head_len,
+ csa->bcn.tim_len, NULL);
+ }
+
+ if (error) {
+ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0))
+ cfg80211_stop_iface(rwnx_hw->wiphy, &vif->wdev, GFP_KERNEL);
+ #else
+ cfg80211_disconnected(vif->ndev, 0, NULL, 0, 0, GFP_KERNEL);
+ #endif
+ } else {
+ mutex_lock(&vif->wdev.mtx);
+ __acquire(&vif->wdev.mtx);
+ spin_lock_bh(&rwnx_hw->cb_lock);
+ rwnx_chanctx_unlink(vif);
+ rwnx_chanctx_link(vif, csa->ch_idx, &csa->chandef);
+ if (rwnx_hw->cur_chanctx == csa->ch_idx) {
+ rwnx_radar_detection_enable_on_cur_channel(rwnx_hw);
+ rwnx_txq_vif_start(vif, RWNX_TXQ_STOP_CHAN, rwnx_hw);
+ } else
+ rwnx_txq_vif_stop(vif, RWNX_TXQ_STOP_CHAN, rwnx_hw);
+ spin_unlock_bh(&rwnx_hw->cb_lock);
+#if (LINUX_VERSION_CODE >= HIGH_KERNEL_VERSION)
+ cfg80211_ch_switch_notify(vif->ndev, &csa->chandef, 0, 0);
+#else
+ cfg80211_ch_switch_notify(vif->ndev, &csa->chandef);
+#endif
+
+ mutex_unlock(&vif->wdev.mtx);
+ __release(&vif->wdev.mtx);
+ }
+ rwnx_del_csa(vif);
+}
+#endif
+
+/**
+ * rwnx_external_auth_enable - Enable external authentication on a vif
+ *
+ * @vif: VIF on which external authentication must be enabled
+ *
+ * External authentication requires to start TXQ for unknown STA in
+ * order to send auth frame pusehd by user space.
+ * Note: It is assumed that fw is on the correct channel.
+ */
+void rwnx_external_auth_enable(struct rwnx_vif *vif)
+{
+ vif->sta.external_auth = true;
+ rwnx_txq_unk_vif_init(vif);
+ rwnx_txq_start(rwnx_txq_vif_get(vif, NX_UNK_TXQ_TYPE), 0);
+}
+
+/**
+ * rwnx_external_auth_disable - Disable external authentication on a vif
+ *
+ * @vif: VIF on which external authentication must be disabled
+ */
+void rwnx_external_auth_disable(struct rwnx_vif *vif)
+{
+ if (!vif->sta.external_auth)
+ return;
+
+ vif->sta.external_auth = false;
+ rwnx_txq_unk_vif_deinit(vif);
+}
+
+/**
+ * rwnx_update_mesh_power_mode -
+ *
+ * @vif: mesh VIF for which power mode is updated
+ *
+ * Does nothing if vif is not a mesh point interface.
+ * Since firmware doesn't support one power save mode per link select the
+ * most "active" power mode among all mesh links.
+ * Indeed as soon as we have to be active on one link we might as well be
+ * active on all links.
+ *
+ * If there is no link then the power mode for next peer is used;
+ */
+void rwnx_update_mesh_power_mode(struct rwnx_vif *vif)
+{
+ enum nl80211_mesh_power_mode mesh_pm;
+ struct rwnx_sta *sta;
+ struct mesh_config mesh_conf;
+ struct mesh_update_cfm cfm;
+ u32 mask;
+
+ if (RWNX_VIF_TYPE(vif) != NL80211_IFTYPE_MESH_POINT)
+ return;
+
+ if (list_empty(&vif->ap.sta_list)) {
+ mesh_pm = vif->ap.next_mesh_pm;
+ } else {
+ mesh_pm = NL80211_MESH_POWER_DEEP_SLEEP;
+ list_for_each_entry(sta, &vif->ap.sta_list, list) {
+ if (sta->valid && (sta->mesh_pm < mesh_pm)) {
+ mesh_pm = sta->mesh_pm;
+ }
+ }
+ }
+
+ if (mesh_pm == vif->ap.mesh_pm)
+ return;
+
+ mask = BIT(NL80211_MESHCONF_POWER_MODE - 1);
+ mesh_conf.power_mode = mesh_pm;
+ if (rwnx_send_mesh_update_req(vif->rwnx_hw, vif, mask, &mesh_conf, &cfm) ||
+ cfm.status)
+ return;
+
+ vif->ap.mesh_pm = mesh_pm;
+}
+
+#ifdef CONFIG_BR_SUPPORT
+void netdev_br_init(struct net_device *netdev)
+{
+ struct rwnx_vif *rwnx_vif = netdev_priv(netdev);
+
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 35))
+ rcu_read_lock();
+#endif
+
+ /* if(check_fwstate(pmlmepriv, WIFI_STATION_STATE|WIFI_ADHOC_STATE) == _TRUE) */
+ {
+ /* struct net_bridge *br = netdev->br_port->br; */ /* ->dev->dev_addr; */
+ #if (LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 35))
+ if (netdev->br_port)
+ #else
+ if (rcu_dereference(rwnx_vif->ndev->rx_handler_data))
+ #endif
+ {
+ struct net_device *br_netdev;
+
+ br_netdev = dev_get_by_name(&init_net, CONFIG_BR_SUPPORT_BRNAME);
+ if (br_netdev) {
+ memcpy(rwnx_vif->br_mac, br_netdev->dev_addr, ETH_ALEN);
+ dev_put(br_netdev);
+ AICWFDBG(LOGINFO, FUNC_NDEV_FMT" bind bridge dev "NDEV_FMT"("MAC_FMT")\n"
+ , FUNC_NDEV_ARG(netdev), NDEV_ARG(br_netdev), MAC_ARG(br_netdev->dev_addr));
+ } else {
+ AICWFDBG(LOGINFO, FUNC_NDEV_FMT" can't get bridge dev by name \"%s\"\n"
+ , FUNC_NDEV_ARG(netdev), CONFIG_BR_SUPPORT_BRNAME);
+ }
+ }
+
+ rwnx_vif->ethBrExtInfo.addPPPoETag = 1;
+ }
+
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 35))
+ rcu_read_unlock();
+#endif
+}
+#endif /* CONFIG_BR_SUPPORT */
+
+
+/*********************************************************************
+ * netdev callbacks
+ ********************************************************************/
+/**
+ * int (*ndo_open)(struct net_device *dev);
+ * This function is called when network device transistions to the up
+ * state.
+ *
+ * - Start FW if this is the first interface opened
+ * - Add interface at fw level
+ */
+static int rwnx_open(struct net_device *dev)
+{
+ struct rwnx_vif *rwnx_vif = netdev_priv(dev);
+ struct rwnx_hw *rwnx_hw = rwnx_vif->rwnx_hw;
+ struct mm_add_if_cfm add_if_cfm;
+ int error = 0;
+ u8 rwnx_rx_gain = 0x0E;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ // Check if it is the first opened VIF
+ if (rwnx_hw->vif_started == 0)
+ {
+ // Start the FW
+ if ((error = rwnx_send_start(rwnx_hw)))
+ return error;
+
+ if (rwnx_hw->usbdev->chipid == PRODUCT_ID_AIC8800DC || rwnx_hw->usbdev->chipid == PRODUCT_ID_AIC8800DW) {
+ error = rwnx_send_dbg_mem_mask_write_req(rwnx_hw, 0x4033b300, 0xFF, rwnx_rx_gain);
+ if(error){
+ return error;
+ }
+ }
+
+ /* Device is now started */
+ set_bit(RWNX_DEV_STARTED, &rwnx_hw->drv_flags);
+ atomic_set(&rwnx_vif->drv_conn_state, RWNX_DRV_STATUS_DISCONNECTED);
+ }
+
+ if (RWNX_VIF_TYPE(rwnx_vif) == NL80211_IFTYPE_AP_VLAN) {
+ /* For AP_vlan use same fw and drv indexes. We ensure that this index
+ will not be used by fw for another vif by taking index >= NX_VIRT_DEV_MAX */
+ add_if_cfm.inst_nbr = rwnx_vif->drv_vif_index;
+ netif_tx_stop_all_queues(dev);
+
+ /* Save the index retrieved from LMAC */
+ spin_lock_bh(&rwnx_hw->cb_lock);
+ rwnx_vif->vif_index = add_if_cfm.inst_nbr;
+ rwnx_vif->up = true;
+ rwnx_hw->vif_started++;
+ rwnx_hw->vif_table[add_if_cfm.inst_nbr] = rwnx_vif;
+ spin_unlock_bh(&rwnx_hw->cb_lock);
+ } else {
+ /* Forward the information to the LMAC,
+ * p2p value not used in FMAC configuration, iftype is sufficient */
+
+ if ((error = rwnx_send_add_if(rwnx_hw, rwnx_vif->wdev.address,
+ RWNX_VIF_TYPE(rwnx_vif), false, &add_if_cfm))) {
+ AICWFDBG(LOGERROR, "add if fail\n");
+ return error;
+ }
+
+ if (add_if_cfm.status != 0) {
+ RWNX_PRINT_CFM_ERR(add_if);
+ return -EIO;
+ }
+
+ /* Save the index retrieved from LMAC */
+ spin_lock_bh(&rwnx_hw->cb_lock);
+ rwnx_vif->vif_index = add_if_cfm.inst_nbr;
+ rwnx_vif->up = true;
+ rwnx_hw->vif_started++;
+ rwnx_hw->vif_table[add_if_cfm.inst_nbr] = rwnx_vif;
+ spin_unlock_bh(&rwnx_hw->cb_lock);
+#ifdef CONFIG_USE_P2P0
+ if(rwnx_vif->is_p2p_vif){
+ rwnx_hw->p2p_dev_vif = rwnx_vif;
+ rwnx_hw->is_p2p_alive = 1;
+ }
+#endif
+
+ }
+
+ if (RWNX_VIF_TYPE(rwnx_vif) == NL80211_IFTYPE_AP || RWNX_VIF_TYPE(rwnx_vif) == NL80211_IFTYPE_P2P_GO)
+ {
+ #ifdef CONFIG_COEX
+ rwnx_send_coex_req(rwnx_hw, 1, 0);
+ #endif
+ }
+
+ if (RWNX_VIF_TYPE(rwnx_vif) == NL80211_IFTYPE_MONITOR){
+ rwnx_hw->monitor_vif = rwnx_vif->vif_index;
+ if (rwnx_vif->ch_index != RWNX_CH_NOT_SET){
+ //Configure the monitor channel
+ error = rwnx_send_config_monitor_req(rwnx_hw, &rwnx_hw->chanctx_table[rwnx_vif->ch_index].chan_def, NULL);
+ }
+ #if defined(CONFIG_RWNX_MON_XMIT)
+ rwnx_txq_unk_vif_init(rwnx_vif);
+ #endif
+ #if defined(CONFIG_RWNX_MON_RXFILTER)
+ rwnx_send_set_filter(rwnx_hw, (FIF_BCN_PRBRESP_PROMISC | FIF_OTHER_BSS | FIF_PSPOLL | FIF_PROBE_REQ));
+ #endif
+ }
+
+ //netif_carrier_off(dev);
+ #if defined(CONFIG_RWNX_MON_XMIT)
+ netif_carrier_on(dev);
+ AICWFDBG(LOGINFO, "monitor xmit: netif_carrier_on\n");
+ #endif
+
+#ifdef CONFIG_BR_SUPPORT
+ netdev_br_init(dev);
+#endif /* CONFIG_BR_SUPPORT */
+
+ //netif_carrier_off(dev);
+ netif_start_queue(dev);
+
+ return error;
+}
+
+/**
+ * int (*ndo_stop)(struct net_device *dev);
+ * This function is called when network device transistions to the down
+ * state.
+ *
+ * - Remove interface at fw level
+ * - Reset FW if this is the last interface opened
+ */
+static int rwnx_close(struct net_device *dev)
+{
+ struct rwnx_vif *rwnx_vif = netdev_priv(dev);
+ struct rwnx_hw *rwnx_hw = rwnx_vif->rwnx_hw;
+ struct aicwf_bus *bus_if = NULL;
+ int ret = 0;
+#if defined(AICWF_USB_SUPPORT)
+ struct aic_usb_dev *usbdev = NULL;
+ bus_if = dev_get_drvdata(rwnx_hw->dev);
+ if (bus_if)
+ usbdev = bus_if->bus_priv.usb;
+#endif
+#if defined(AICWF_SDIO_SUPPORT)
+ struct aic_sdio_dev *sdiodev = NULL;
+ bus_if = dev_get_drvdata(rwnx_hw->dev);
+ if (bus_if)
+ sdiodev = bus_if->bus_priv.sdio;
+#endif
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+#if defined(AICWF_USB_SUPPORT) || defined(AICWF_SDIO_SUPPORT)
+ if (scanning){
+ scanning = false;
+ }
+
+ if(p2p_working){
+ p2p_working = false;
+ }
+#endif
+#if 0
+ netdev_info(dev, "CLOSE");
+#endif
+ AICWFDBG(LOGINFO, "%s %s Enter\n", __func__, dev->name);
+
+#ifdef CONFIG_USE_P2P0
+ if(rwnx_hw->p2p_dev_vif){
+ atomic_set(&rwnx_hw->p2p_alive_timer_count, P2P_ALIVE_TIME_MS);
+ rwnx_hw->is_p2p_alive = 0;
+ }
+#endif
+
+ rwnx_radar_cancel_cac(&rwnx_hw->radar);
+
+ /* Abort scan request on the vif */
+ if (rwnx_hw->scan_request &&
+ rwnx_hw->scan_request->wdev == &rwnx_vif->wdev) {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0)
+ struct cfg80211_scan_info info = {
+ .aborted = true,
+ };
+
+ cfg80211_scan_done(rwnx_hw->scan_request, &info);
+#else
+ cfg80211_scan_done(rwnx_hw->scan_request, true);
+#endif
+ rwnx_hw->scan_request = NULL;
+ ret = rwnx_send_scanu_cancel_req(rwnx_hw, NULL);
+ mdelay(35);//make sure firmware take affect
+ if (ret) {
+ AICWFDBG(LOGERROR, "scanu_cancel fail\n");
+ return ret;
+ }
+ }
+
+#if defined(AICWF_USB_SUPPORT)
+ if (usbdev != NULL) {
+ if (usbdev->bus_if->state != BUS_DOWN_ST && usbdev->state != USB_DOWN_ST)
+ AICWFDBG(LOGINFO, "state: %d %d \r\n", (int)usbdev->bus_if->state, (int)usbdev->state);
+
+ if(RWNX_VIF_TYPE(rwnx_vif) == NL80211_IFTYPE_STATION ||
+ RWNX_VIF_TYPE(rwnx_vif) == NL80211_IFTYPE_P2P_CLIENT){
+ if(atomic_read(&rwnx_vif->drv_conn_state) == (int)RWNX_DRV_STATUS_CONNECTING){
+ rwnx_send_sm_disconnect_req(rwnx_hw, rwnx_vif, 3);
+ atomic_set(&rwnx_vif->drv_conn_state, RWNX_DRV_STATUS_DISCONNECTED);
+ }
+ }
+#ifdef CONFIG_USE_P2P0
+ if(!rwnx_vif->is_p2p_vif || ( rwnx_vif->is_p2p_vif && rwnx_hw->is_p2p_alive)){
+#endif
+ rwnx_send_remove_if(rwnx_hw, rwnx_vif->vif_index, false);
+#ifdef CONFIG_USE_P2P0
+ }
+#endif
+
+ }
+#endif
+#if defined(AICWF_SDIO_SUPPORT)
+ if (sdiodev != NULL ) {
+ if (sdiodev->bus_if->state != BUS_DOWN_ST)
+ rwnx_send_remove_if(rwnx_hw, rwnx_vif->vif_index, false);
+ }
+#endif
+
+ if (rwnx_hw->roc_elem && (rwnx_hw->roc_elem->wdev == &rwnx_vif->wdev)) {
+ AICWFDBG(LOGINFO, "%s clear roc\n", __func__);
+ /* Initialize RoC element pointer to NULL, indicate that RoC can be started */
+ rwnx_hw->roc_elem = NULL;
+ }
+
+ /* Ensure that we won't process disconnect ind */
+ spin_lock_bh(&rwnx_hw->cb_lock);
+
+ rwnx_vif->up = false;
+ if (netif_carrier_ok(dev)) {
+ if (RWNX_VIF_TYPE(rwnx_vif) == NL80211_IFTYPE_STATION ||
+ RWNX_VIF_TYPE(rwnx_vif) == NL80211_IFTYPE_P2P_CLIENT) {
+ cfg80211_disconnected(dev, WLAN_REASON_DEAUTH_LEAVING,
+ NULL, 0, true, GFP_ATOMIC);
+ netif_tx_stop_all_queues(dev);
+ netif_carrier_off(dev);
+ } else if (RWNX_VIF_TYPE(rwnx_vif) == NL80211_IFTYPE_AP_VLAN) {
+ netif_carrier_off(dev);
+ } else {
+ netdev_warn(dev, "AP not stopped when disabling interface");
+ }
+#ifdef CONFIG_BR_SUPPORT
+ /* if (OPMODE & (WIFI_STATION_STATE | WIFI_ADHOC_STATE)) */
+ {
+ /* void nat25_db_cleanup(_adapter *priv); */
+ nat25_db_cleanup(rwnx_vif);
+ }
+#endif /* CONFIG_BR_SUPPORT */
+ }
+
+
+ rwnx_hw->vif_table[rwnx_vif->vif_index] = NULL;
+ spin_unlock_bh(&rwnx_hw->cb_lock);
+
+ rwnx_chanctx_unlink(rwnx_vif);
+
+ if (RWNX_VIF_TYPE(rwnx_vif) == NL80211_IFTYPE_MONITOR)
+ rwnx_hw->monitor_vif = RWNX_INVALID_VIF;
+
+ if (RWNX_VIF_TYPE(rwnx_vif) == NL80211_IFTYPE_AP || RWNX_VIF_TYPE(rwnx_vif) == NL80211_IFTYPE_P2P_GO)
+ {
+ #ifdef CONFIG_COEX
+ rwnx_send_coex_req(rwnx_hw, 0, 1);
+ #endif
+ }
+
+ rwnx_hw->vif_started--;
+ if (rwnx_hw->vif_started == 0) {
+ /* This also lets both ipc sides remain in sync before resetting */
+ #if 0
+ rwnx_ipc_tx_drain(rwnx_hw);
+ #else
+ #if defined(AICWF_USB_SUPPORT)
+ if (usbdev->bus_if->state != BUS_DOWN_ST && usbdev->state != USB_DOWN_ST) {
+ #else
+ if (sdiodev->bus_if->state != BUS_DOWN_ST) {
+ #endif
+ rwnx_send_reset(rwnx_hw);
+ #if defined(AICWF_USB_SUPPORT)
+ if (rwnx_hw->usbdev->chipid == PRODUCT_ID_AIC8801 ||
+ ((rwnx_hw->usbdev->chipid == PRODUCT_ID_AIC8800DC ||
+ rwnx_hw->usbdev->chipid == PRODUCT_ID_AIC8800DW ||
+ rwnx_hw->usbdev->chipid == PRODUCT_ID_AIC8800D81)&& testmode == 0)) {
+ #endif
+ // Set parameters to firmware
+ rwnx_send_me_config_req(rwnx_hw);
+ // Set channel parameters to firmware
+ rwnx_send_me_chan_config_req(rwnx_hw);
+ #if defined(AICWF_USB_SUPPORT)
+ }
+ #endif
+ }
+ #endif
+ clear_bit(RWNX_DEV_STARTED, &rwnx_hw->drv_flags);
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_RFTEST
+enum {
+ SET_TX,
+ SET_TXSTOP,
+ SET_TXTONE,
+ SET_RX,
+ GET_RX_RESULT,
+ SET_RXSTOP,
+ SET_RX_METER,
+ SET_POWER,
+ SET_XTAL_CAP,
+ SET_XTAL_CAP_FINE,
+ GET_EFUSE_BLOCK,
+ SET_FREQ_CAL,
+ SET_FREQ_CAL_FINE,
+ GET_FREQ_CAL,
+ SET_MAC_ADDR,
+ GET_MAC_ADDR,
+ SET_BT_MAC_ADDR,
+ GET_BT_MAC_ADDR,
+ SET_VENDOR_INFO,
+ GET_VENDOR_INFO,
+ RDWR_PWRMM,
+ RDWR_PWRIDX,
+ RDWR_PWRLVL = RDWR_PWRIDX,
+ RDWR_PWROFST,
+ RDWR_DRVIBIT,
+ RDWR_EFUSE_PWROFST,
+ RDWR_EFUSE_DRVIBIT,
+ SET_PAPR,
+ SET_CAL_XTAL,
+ GET_CAL_XTAL_RES,
+ SET_COB_CAL,
+ GET_COB_CAL_RES,
+ RDWR_EFUSE_USRDATA,
+ SET_NOTCH,
+ RDWR_PWROFSTFINE,
+ RDWR_EFUSE_PWROFSTFINE,
+ RDWR_EFUSE_SDIOCFG,
+
+ #ifdef CONFIG_USB_BT
+ BT_CMD_BASE = 0x100,
+ BT_RESET,
+ BT_TXDH,
+ BT_RXDH,
+ BT_STOP,
+ GET_BT_RX_RESULT,
+ #endif
+};
+
+typedef struct
+{
+ u8_l chan;
+ u8_l bw;
+ u8_l mode;
+ u8_l rate;
+ u16_l length;
+}cmd_rf_settx_t;
+
+typedef struct
+{
+ u8_l val;
+}cmd_rf_setfreq_t;
+
+typedef struct
+{
+ u8_l chan;
+ u8_l bw;
+}cmd_rf_rx_t;
+
+typedef struct
+{
+ u8_l block;
+}cmd_rf_getefuse_t;
+typedef struct
+{
+ u8_l dutid;
+ u8_l chip_num;
+ u8_l dis_xtal;
+}cmd_rf_setcobcal_t;
+typedef struct
+{
+ u16_l dut_rcv_golden_num;
+ u8_l golden_rcv_dut_num;
+ s8_l rssi_static;
+ s8_l snr_static;
+ s8_l dut_rssi_static;
+ u16_l reserved;
+}cob_result_ptr_t;
+
+typedef struct
+{
+ u8_l func;
+ u8_l cnt;
+ u8_l reserved[2];
+ u32_l usrdata[3]; // 3 words totally
+} cmd_ef_usrdata_t;
+
+#endif
+
+#define CMD_MAXARGS 30
+
+#if 0
+#define isblank(c) ((c) == ' ' || (c) == '\t')
+#define isascii(c) (((unsigned char)(c)) <= 0x7F)
+
+static int isdigit(unsigned char c)
+{
+ return ((c >= '0') && (c <='9'));
+}
+
+static int isxdigit(unsigned char c)
+{
+ if ((c >= '0') && (c <='9'))
+ return 1;
+ if ((c >= 'a') && (c <='f'))
+ return 1;
+ if ((c >= 'A') && (c <='F'))
+ return 1;
+ return 0;
+}
+
+static int islower(unsigned char c)
+{
+ return ((c >= 'a') && (c <='z'));
+}
+
+static unsigned char toupper(unsigned char c)
+{
+ if (islower(c))
+ c -= 'a'-'A';
+ return c;
+}
+#endif
+
+
+static int parse_line (char *line, char *argv[])
+{
+ int nargs = 0;
+
+ while (nargs < CMD_MAXARGS) {
+ /* skip any white space */
+ while ((*line == ' ') || (*line == '\t')) {
+ ++line;
+ }
+
+ if (*line == '\0') { /* end of line, no more args */
+ argv[nargs] = 0;
+ return (nargs);
+ }
+
+ /* Argument include space should be bracketed by quotation mark */
+ if (*line == '\"') {
+ /* Skip quotation mark */
+ line++;
+
+ /* Begin of argument string */
+ argv[nargs++] = line;
+
+ /* Until end of argument */
+ while(*line && (*line != '\"')) {
+ ++line;
+ }
+ } else {
+ argv[nargs++] = line; /* begin of argument string */
+
+ /* find end of string */
+ while(*line && (*line != ' ') && (*line != '\t')) {
+ ++line;
+ }
+ }
+
+ if (*line == '\0') { /* end of line, no more args */
+ argv[nargs] = 0;
+ return (nargs);
+ }
+
+ *line++ = '\0'; /* terminate current arg */
+ }
+
+ printk("** Too many args (max. %d) **\n", CMD_MAXARGS);
+
+ return (nargs);
+}
+
+unsigned int command_strtoul(const char *cp, char **endp, unsigned int base)
+{
+ unsigned int result = 0, value, is_neg=0;
+
+ if (*cp == '0') {
+ cp++;
+ if ((*cp == 'x') && isxdigit(cp[1])) {
+ base = 16;
+ cp++;
+ }
+ if (!base) {
+ base = 8;
+ }
+ }
+ if (!base) {
+ base = 10;
+ }
+ if (*cp == '-') {
+ is_neg = 1;
+ cp++;
+ }
+ while (isxdigit(*cp) && (value = isdigit(*cp) ? *cp - '0' : (islower(*cp) ? toupper(*cp) : *cp) - 'A' + 10) < base) {
+ result = result * base + value;
+ cp++;
+ }
+ if (is_neg)
+ result = (unsigned int)((int)result * (-1));
+
+ if (endp)
+ *endp = (char *)cp;
+ return result;
+}
+
+
+int handle_private_cmd(struct net_device *net, char *command, u32 cmd_len)
+{
+ int bytes_written = 0;
+ char* para = NULL;
+ char* cmd = NULL;
+ char *argv[CMD_MAXARGS + 1];
+ int argc;
+ #ifdef CONFIG_RFTEST
+ struct dbg_rftest_cmd_cfm cfm = {{0,}};
+ u8_l mac_addr[6];
+ cmd_rf_settx_t settx_param;
+ cmd_rf_rx_t setrx_param;
+ int freq;
+ cmd_rf_getefuse_t getefuse_param;
+ cmd_rf_setfreq_t cmd_setfreq;
+ cmd_rf_setcobcal_t setcob_cal;
+ cob_result_ptr_t *cob_result_ptr;
+ cmd_ef_usrdata_t cmd_ef_usrdata;
+ u8_l ana_pwr;
+ u8_l dig_pwr;
+ u8_l pwr;
+ u8_l papr;
+ u8_l xtal_cap;
+ u8_l xtal_cap_fine;
+ u8_l vendor_info;
+ #ifdef CONFIG_USB_BT
+ int bt_index;
+ u8_l dh_cmd_reset[4];
+ u8_l dh_cmd_txdh[18];
+ u8_l dh_cmd_rxdh[17];
+ u8_l dh_cmd_stop[5];
+ #endif
+ #endif
+ u8_l buf[2];
+ s8_l freq_ = 0;
+ u8_l func = 0;
+ u8_l state = 0;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ if ((argc = parse_line(command, argv)) == 0) {
+ return -1;
+ }
+
+ do {
+ #ifdef AICWF_SDIO_SUPPORT
+ struct rwnx_hw *p_rwnx_hw = g_rwnx_plat->sdiodev->rwnx_hw;
+ #endif
+ #ifdef AICWF_USB_SUPPORT
+ #ifdef CONFIG_RFTEST
+ struct rwnx_hw *p_rwnx_hw = g_rwnx_plat->usbdev->rwnx_hw;
+ #endif
+ #endif
+ #ifdef CONFIG_RFTEST
+ if (strcasecmp(argv[0], "GET_RX_RESULT") ==0) {
+ AICWFDBG(LOGINFO, "get_rx_result\n");
+ rwnx_send_rftest_req(p_rwnx_hw, GET_RX_RESULT, 0, NULL, &cfm);
+ memcpy(command, &cfm.rftest_result[0], 8);
+ bytes_written = 8;
+ } else if (strcasecmp(argv[0], "SET_TX") == 0) {
+ AICWFDBG(LOGINFO, "set_tx\n");
+ if (argc < 6) {
+ printk("wrong param\n");
+ bytes_written = -EINVAL;
+ break;
+ }
+ settx_param.chan = command_strtoul(argv[1], NULL, 10);
+ settx_param.bw = command_strtoul(argv[2], NULL, 10);
+ settx_param.mode = command_strtoul(argv[3], NULL, 10);
+ settx_param.rate = command_strtoul(argv[4], NULL, 10);
+ settx_param.length = command_strtoul(argv[5], NULL, 10);
+ AICWFDBG(LOGINFO, "txparam:%d,%d,%d,%d,%d\n", settx_param.chan, settx_param.bw,
+ settx_param.mode, settx_param.rate, settx_param.length);
+ rwnx_send_rftest_req(p_rwnx_hw, SET_TX, sizeof(cmd_rf_settx_t), (u8_l *)&settx_param, NULL);
+ } else if (strcasecmp(argv[0], "SET_TXSTOP") == 0) {
+ AICWFDBG(LOGINFO, "settx_stop\n");
+ rwnx_send_rftest_req(p_rwnx_hw, SET_TXSTOP, 0, NULL, NULL);
+ } else if (strcasecmp(argv[0], "SET_TXTONE") == 0) {
+ AICWFDBG(LOGINFO, "set_tx_tone,argc:%d\n",argc);
+ if ((argc == 2) || (argc == 3)) {
+ AICWFDBG(LOGINFO, "argv 1:%s\n",argv[1]);
+ //u8_l func = (u8_l)command_strtoul(argv[1], NULL, 16);
+ func = (u8_l)command_strtoul(argv[1], NULL, 16);
+ //s8_l freq;
+ if (argc == 3) {
+ AICWFDBG(LOGINFO, "argv 2:%s\n",argv[2]);
+ freq_ = (u8_l)command_strtoul(argv[2], NULL, 10);
+ } else {
+ freq_ = 0;
+ };
+ //u8_l buf[2] = {func, (u8_l)freq};
+ buf[0] = func;
+ buf[1] = (u8_l)freq_;
+ rwnx_send_rftest_req(p_rwnx_hw, SET_TXTONE, argc - 1, buf, NULL);
+ } else {
+ AICWFDBG(LOGINFO, "wrong args\n");
+ bytes_written = -EINVAL;
+ break;
+ }
+ } else if (strcasecmp(argv[0], "SET_RX") == 0) {
+ AICWFDBG(LOGINFO, "set_rx\n");
+ if (argc < 3) {
+ AICWFDBG(LOGERROR, "wrong param\n");
+ bytes_written = -EINVAL;
+ break;
+ }
+ setrx_param.chan = command_strtoul(argv[1], NULL, 10);
+ setrx_param.bw = command_strtoul(argv[2], NULL, 10);
+ rwnx_send_rftest_req(p_rwnx_hw, SET_RX, sizeof(cmd_rf_rx_t), (u8_l *)&setrx_param, NULL);
+ } else if (strcasecmp(argv[0], "SET_RXSTOP") == 0) {
+ AICWFDBG(LOGINFO, "set_rxstop\n");
+ rwnx_send_rftest_req(p_rwnx_hw, SET_RXSTOP, 0, NULL, NULL);
+ } else if (strcasecmp(argv[0], "SET_RX_METER") == 0) {
+ AICWFDBG(LOGINFO, "set_rx_meter\n");
+ freq = (int)command_strtoul(argv[1], NULL, 10);
+ rwnx_send_rftest_req(p_rwnx_hw, SET_RX_METER, sizeof(freq), (u8_l *)&freq, NULL);
+ } else if (strcasecmp(argv[0], "SET_FREQ_CAL") == 0) {
+ AICWFDBG(LOGINFO, "set_freq_cal\n");
+ if (argc < 2) {
+ AICWFDBG(LOGERROR, "wrong param\n");
+ bytes_written = -EINVAL;
+ break;
+ }
+ cmd_setfreq.val = command_strtoul(argv[1], NULL, 16);
+ AICWFDBG(LOGINFO, "param:%x\r\n", cmd_setfreq.val);
+ rwnx_send_rftest_req(p_rwnx_hw, SET_FREQ_CAL, sizeof(cmd_rf_setfreq_t), (u8_l *)&cmd_setfreq, &cfm);
+ memcpy(command, &cfm.rftest_result[0], 4);
+ bytes_written = 4;
+ } else if (strcasecmp(argv[0], "SET_FREQ_CAL_FINE") == 0) {
+ AICWFDBG(LOGINFO, "set_freq_cal_fine\n");
+ if (argc < 2) {
+ AICWFDBG(LOGERROR, "wrong param\n");
+ bytes_written = -EINVAL;
+ break;
+ }
+ cmd_setfreq.val = command_strtoul(argv[1], NULL, 16);
+ AICWFDBG(LOGINFO, "param:%x\r\n", cmd_setfreq.val);
+ rwnx_send_rftest_req(p_rwnx_hw, SET_FREQ_CAL_FINE, sizeof(cmd_rf_setfreq_t), (u8_l *)&cmd_setfreq, &cfm);
+ memcpy(command, &cfm.rftest_result[0], 4);
+ bytes_written = 4;
+ } else if (strcasecmp(argv[0], "GET_EFUSE_BLOCK") == 0) {
+ AICWFDBG(LOGINFO, "get_efuse_block\n");
+ if (argc < 2) {
+ AICWFDBG(LOGERROR, "wrong param\n");
+ bytes_written = -EINVAL;
+ break;
+ }
+ getefuse_param.block = command_strtoul(argv[1], NULL, 10);
+ rwnx_send_rftest_req(p_rwnx_hw, GET_EFUSE_BLOCK, sizeof(cmd_rf_getefuse_t), (u8_l *)&getefuse_param, &cfm);
+ AICWFDBG(LOGINFO, "get val=%x\r\n", cfm.rftest_result[0]);
+ memcpy(command, &cfm.rftest_result[0], 4);
+ bytes_written = 4;
+ } else if (strcasecmp(argv[0], "SET_POWER") == 0) {
+ AICWFDBG(LOGINFO, "set_power\n");
+ if (g_rwnx_plat->usbdev->chipid == PRODUCT_ID_AIC8801) {
+ ana_pwr = command_strtoul(argv[1], NULL, 16);
+ dig_pwr = command_strtoul(argv[2], NULL, 16);
+ pwr = (ana_pwr << 4 | dig_pwr);
+ if (ana_pwr > 0xf || dig_pwr > 0xf) {
+ AICWFDBG(LOGERROR, "invalid param\r\n");
+ bytes_written = -EINVAL;
+ break;
+ }
+ } else {
+ ana_pwr = command_strtoul(argv[1], NULL, 10);
+ pwr = ana_pwr;
+ if (ana_pwr > 0x1e) {
+ AICWFDBG(LOGERROR, "invalid param\r\n");
+ bytes_written = -EINVAL;
+ break;
+ }
+ }
+ AICWFDBG(LOGINFO, "pwr =%x\r\n", pwr);
+ rwnx_send_rftest_req(p_rwnx_hw, SET_POWER, sizeof(pwr), (u8_l *)&pwr, NULL);
+ } else if (strcasecmp(argv[0], "SET_PAPR") == 0) {
+ printk("set papr\n");
+ if (argc > 1) {
+ papr = command_strtoul(argv[1], NULL, 10);
+ printk("papr %d\r\n", papr);
+ rwnx_send_rftest_req(p_rwnx_hw, SET_PAPR, sizeof(papr), (u8_l *)&papr, NULL);
+ } else {
+ printk("wrong args\n");
+ bytes_written = -EINVAL;
+ break;
+ }
+ } else if (strcasecmp(argv[0], "SET_NOTCH") == 0) {
+ if (argc > 1) {
+ func = command_strtoul(argv[1], NULL, 10);
+ printk("set notch: %d\n", func);
+ rwnx_send_rftest_req(p_rwnx_hw, SET_NOTCH, sizeof(func), (u8_l *)&func, NULL);
+ } else {
+ printk("wrong args\n");
+ bytes_written = -EINVAL;
+ break;
+ }
+ } else if (strcasecmp(argv[0], "SET_XTAL_CAP")==0) {
+ AICWFDBG(LOGINFO, "set_xtal_cap\n");
+ if (argc < 2) {
+ AICWFDBG(LOGERROR, "wrong param\n");
+ bytes_written = -EINVAL;
+ break;
+ }
+ xtal_cap = command_strtoul(argv[1], NULL, 10);
+ AICWFDBG(LOGINFO, "xtal_cap =%x\r\n", xtal_cap);
+ rwnx_send_rftest_req(p_rwnx_hw, SET_XTAL_CAP, sizeof(xtal_cap), (u8_l *)&xtal_cap, &cfm);
+ memcpy(command, &cfm.rftest_result[0], 4);
+ bytes_written = 4;
+ } else if (strcasecmp(argv[0], "SET_XTAL_CAP_FINE")==0) {
+ AICWFDBG(LOGINFO, "set_xtal_cap_fine\n");
+ if (argc < 2) {
+ AICWFDBG(LOGERROR, "wrong param\n");
+ bytes_written = -EINVAL;
+ break;
+ }
+ xtal_cap_fine = command_strtoul(argv[1], NULL, 10);
+ AICWFDBG(LOGINFO, "xtal_cap_fine =%x\r\n", xtal_cap_fine);
+ rwnx_send_rftest_req(p_rwnx_hw, SET_XTAL_CAP_FINE, sizeof(xtal_cap_fine), (u8_l *)&xtal_cap_fine, &cfm);
+ memcpy(command, &cfm.rftest_result[0], 4);
+ bytes_written = 4;
+ } else if (strcasecmp(argv[0], "SET_MAC_ADDR")==0) {
+ AICWFDBG(LOGINFO, "set_mac_addr\n");
+ if (argc < 7) {
+ AICWFDBG(LOGERROR, "wrong param\n");
+ bytes_written = -EINVAL;
+ break;
+ }
+ mac_addr[5] = command_strtoul(argv[1], NULL, 16);
+ mac_addr[4] = command_strtoul(argv[2], NULL, 16);
+ mac_addr[3] = command_strtoul(argv[3], NULL, 16);
+ mac_addr[2] = command_strtoul(argv[4], NULL, 16);
+ mac_addr[1] = command_strtoul(argv[5], NULL, 16);
+ mac_addr[0] = command_strtoul(argv[6], NULL, 16);
+ AICWFDBG(LOGINFO, "set macaddr:%x,%x,%x,%x,%x,%x\n", mac_addr[5], mac_addr[4], mac_addr[3], mac_addr[2], mac_addr[1], mac_addr[0]);
+ rwnx_send_rftest_req(p_rwnx_hw, SET_MAC_ADDR, sizeof(mac_addr), (u8_l *)&mac_addr, NULL);
+ } else if (strcasecmp(argv[0], "GET_MAC_ADDR")==0) {
+ u32_l addr0, addr1;
+ AICWFDBG(LOGINFO, "get mac addr\n");
+ rwnx_send_rftest_req(p_rwnx_hw, GET_MAC_ADDR, 0, NULL, &cfm);
+ memcpy(command, &cfm.rftest_result[0], 8);
+ bytes_written = 8;
+ addr0 = cfm.rftest_result[0];
+ if ((g_rwnx_plat->usbdev->chipid == PRODUCT_ID_AIC8800DC) ||
+ (g_rwnx_plat->usbdev->chipid == PRODUCT_ID_AIC8800DW)) {
+ int rem_cnt = (cfm.rftest_result[1] >> 16) & 0x00FF;
+ addr1 = cfm.rftest_result[1] & 0x0000FFFF;
+ AICWFDBG(LOGINFO, "0x%x,0x%x (remain:%x)\n", addr0, addr1, rem_cnt);
+ } else {
+ addr1 = cfm.rftest_result[1];
+ AICWFDBG(LOGINFO, "0x%x,0x%x\n", addr0, addr1);
+ }
+ } else if (strcasecmp(argv[0], "SET_BT_MAC_ADDR") == 0) {
+ AICWFDBG(LOGINFO, "set_bt_mac_addr\n");
+ if (argc < 7) {
+ AICWFDBG(LOGERROR, "wrong param\n");
+ bytes_written = -EINVAL;
+ break;
+ }
+ mac_addr[5] = command_strtoul(argv[1], NULL, 16);
+ mac_addr[4] = command_strtoul(argv[2], NULL, 16);
+ mac_addr[3] = command_strtoul(argv[3], NULL, 16);
+ mac_addr[2] = command_strtoul(argv[4], NULL, 16);
+ mac_addr[1] = command_strtoul(argv[5], NULL, 16);
+ mac_addr[0] = command_strtoul(argv[6], NULL, 16);
+ AICWFDBG(LOGINFO, "set bt macaddr:%x,%x,%x,%x,%x,%x\n", mac_addr[5], mac_addr[4], mac_addr[3], mac_addr[2], mac_addr[1], mac_addr[0]);
+ rwnx_send_rftest_req(p_rwnx_hw, SET_BT_MAC_ADDR, sizeof(mac_addr), (u8_l *)&mac_addr, NULL);
+ } else if (strcasecmp(argv[0], "GET_BT_MAC_ADDR")==0) {
+ u32_l addr0, addr1;
+ AICWFDBG(LOGINFO, "get bt mac addr\n");
+ rwnx_send_rftest_req(p_rwnx_hw, GET_BT_MAC_ADDR, 0, NULL, &cfm);
+ memcpy(command, &cfm.rftest_result[0], 8);
+ bytes_written = 8;
+ addr0 = cfm.rftest_result[0];
+ if ((g_rwnx_plat->usbdev->chipid == PRODUCT_ID_AIC8800DC) ||
+ (g_rwnx_plat->usbdev->chipid == PRODUCT_ID_AIC8800DW)) {
+ int rem_cnt = (cfm.rftest_result[1] >> 16) & 0x00FF;
+ addr1 = cfm.rftest_result[1] & 0x0000FFFF;
+ AICWFDBG(LOGINFO, "0x%x,0x%x (remain:%x)\n", addr0, addr1, rem_cnt);
+ } else {
+ addr1 = cfm.rftest_result[1];
+ AICWFDBG(LOGINFO, "0x%x,0x%x\n", addr0, addr1);
+ }
+ } else if (strcasecmp(argv[0], "SET_VENDOR_INFO")==0) {
+ vendor_info = command_strtoul(argv[1], NULL, 16);
+ AICWFDBG(LOGINFO, "set vendor info:%x\n", vendor_info);
+ rwnx_send_rftest_req(p_rwnx_hw, SET_VENDOR_INFO, 1, &vendor_info, &cfm);
+ if ((g_rwnx_plat->usbdev->chipid == PRODUCT_ID_AIC8800DC) ||
+ (g_rwnx_plat->usbdev->chipid == PRODUCT_ID_AIC8800DW)) {
+ memcpy(command, &cfm.rftest_result[0], 2);
+ bytes_written = 2;
+ } else {
+ memcpy(command, &cfm.rftest_result[0], 1);
+ bytes_written = 1;
+ }
+ AICWFDBG(LOGINFO, "0x%x\n", cfm.rftest_result[0]);
+ } else if (strcasecmp(argv[0], "GET_VENDOR_INFO")==0) {
+ AICWFDBG(LOGINFO, "get vendor info\n");
+ rwnx_send_rftest_req(p_rwnx_hw, GET_VENDOR_INFO, 0, NULL, &cfm);
+ if ((g_rwnx_plat->usbdev->chipid == PRODUCT_ID_AIC8800DC) ||
+ (g_rwnx_plat->usbdev->chipid == PRODUCT_ID_AIC8800DW)) {
+ memcpy(command, &cfm.rftest_result[0], 2);
+ bytes_written = 2;
+ } else {
+ memcpy(command, &cfm.rftest_result[0], 1);
+ bytes_written = 1;
+ }
+ AICWFDBG(LOGINFO, "0x%x\n", cfm.rftest_result[0]);
+ } else if (strcasecmp(argv[0], "GET_FREQ_CAL") == 0) {
+ unsigned int val;
+ AICWFDBG(LOGINFO, "get freq cal\n");
+ rwnx_send_rftest_req(p_rwnx_hw, GET_FREQ_CAL, 0, NULL, &cfm);
+ memcpy(command, &cfm.rftest_result[0], 4);
+ bytes_written = 4;
+ val = cfm.rftest_result[0];
+ if ((g_rwnx_plat->usbdev->chipid == PRODUCT_ID_AIC8800DC) ||
+ (g_rwnx_plat->usbdev->chipid == PRODUCT_ID_AIC8800DW)) {
+ AICWFDBG(LOGINFO, "cap=0x%x (remain:%x), cap_fine=%x (remain:%x)\n",
+ val & 0xff, (val >> 8) & 0xff, (val >> 16) & 0xff, (val >> 24) & 0xff);
+ } else {
+ AICWFDBG(LOGINFO, "cap=0x%x, cap_fine=0x%x\n", val & 0xff, (val >> 8) & 0xff);
+ }
+ } else if (strcasecmp(argv[0], "RDWR_PWRMM") == 0) {
+ AICWFDBG(LOGINFO, "read/write txpwr manul mode\n");
+ if (argc <= 1) { // read cur
+ rwnx_send_rftest_req(p_rwnx_hw, RDWR_PWRMM, 0, NULL, &cfm);
+ } else { // write
+ u8_l pwrmm = (u8_l)command_strtoul(argv[1], NULL, 16);
+ pwrmm = (pwrmm) ? 1 : 0;
+ AICWFDBG(LOGINFO, "set pwrmm = %x\r\n", pwrmm);
+ rwnx_send_rftest_req(p_rwnx_hw, RDWR_PWRMM, sizeof(pwrmm), (u8_l *)&pwrmm, &cfm);
+ }
+ memcpy(command, &cfm.rftest_result[0], 4);
+ bytes_written = 4;
+ } else if (strcasecmp(argv[0], "RDWR_PWRIDX") == 0) {
+ u8_l func = 0;
+ #ifdef AICWF_USB_SUPPORT
+ if (g_rwnx_plat->usbdev->chipid != PRODUCT_ID_AIC8801) {
+ AICWFDBG(LOGERROR, "unsupported cmd\n");
+ bytes_written = -EINVAL;
+ break;
+ }
+ #endif
+ AICWFDBG(LOGINFO, "read/write txpwr index\n");
+ if (argc > 1) {
+ func = (u8_l)command_strtoul(argv[1], NULL, 16);
+ }
+ if (func == 0) { // read cur
+ rwnx_send_rftest_req(p_rwnx_hw, RDWR_PWRIDX, 0, NULL, &cfm);
+ } else if (func <= 2) { // write 2.4g/5g pwr idx
+ if (argc > 3) {
+ u8_l type = (u8_l)command_strtoul(argv[2], NULL, 16);
+ u8_l pwridx = (u8_l)command_strtoul(argv[3], NULL, 10);
+ u8_l buf[3] = {func, type, pwridx};
+ AICWFDBG(LOGINFO, "set pwridx:[%x][%x]=%x\r\n", func, type, pwridx);
+ rwnx_send_rftest_req(p_rwnx_hw, RDWR_PWRIDX, sizeof(buf), buf, &cfm);
+ } else {
+ AICWFDBG(LOGERROR, "wrong args\n");
+ bytes_written = -EINVAL;
+ break;
+ }
+ } else {
+ AICWFDBG(LOGERROR, "wrong func: %x\n", func);
+ bytes_written = -EINVAL;
+ break;
+ }
+ memcpy(command, &cfm.rftest_result[0], 9);
+ bytes_written = 9;
+ } else if (strcasecmp(argv[0], "RDWR_PWRLVL") == 0) {
+ u8_l func = 0;
+ #ifdef AICWF_USB_SUPPORT
+ if ((g_rwnx_plat->usbdev->chipid != PRODUCT_ID_AIC8800DC)
+ && (g_rwnx_plat->usbdev->chipid != PRODUCT_ID_AIC8800DW)
+ && (g_rwnx_plat->usbdev->chipid != PRODUCT_ID_AIC8800D81)) {
+ AICWFDBG(LOGERROR, "unsupported cmd\n");
+ bytes_written = -EINVAL;
+ break;
+ }
+ #endif
+ printk("read/write txpwr level\n");
+ if (argc > 1) {
+ func = (u8_l)command_strtoul(argv[1], NULL, 16);
+ }
+ if (func == 0) { // read cur
+ rwnx_send_rftest_req(p_rwnx_hw, RDWR_PWRLVL, 0, NULL, &cfm);
+ } else if (func <= 2) { // write 2.4g/5g pwr lvl
+ if (argc > 4) {
+ u8_l grp = (u8_l)command_strtoul(argv[2], NULL, 16);
+ u8_l idx, size;
+ u8_l buf[14] = {func, grp,};
+ if (argc > 12) { // set all grp
+ printk("set pwrlvl %s:\n"
+ " [%x] =", (func == 1) ? "2.4g" : "5g", grp);
+ if (grp == 1) { // TXPWR_LVL_GRP_11N_11AC
+ size = 10;
+ } else {
+ size = 12;
+ }
+ for (idx = 0; idx < size; idx++) {
+ s8_l pwrlvl = (s8_l)command_strtoul(argv[3 + idx], NULL, 10);
+ buf[2 + idx] = (u8_l)pwrlvl;
+ if (idx && !(idx & 0x3)) {
+ printk(" ");
+ }
+ printk(" %2d", pwrlvl);
+ }
+ printk("\n");
+ size += 2;
+ } else { // set grp[idx]
+ u8_l idx = (u8_l)command_strtoul(argv[3], NULL, 10);
+ s8_l pwrlvl = (s8_l)command_strtoul(argv[4], NULL, 10);
+ buf[2] = idx;
+ buf[3] = (u8_l)pwrlvl;
+ size = 4;
+ printk("set pwrlvl %s:\n"
+ " [%x][%d] = %d\n", (func == 1) ? "2.4g" : "5g", grp, idx, pwrlvl);
+ }
+ rwnx_send_rftest_req(p_rwnx_hw, RDWR_PWRLVL, size, buf, &cfm);
+ } else {
+ printk("wrong args\n");
+ }
+ } else {
+ printk("wrong func: %x\n", func);
+ }
+ if(g_rwnx_plat->usbdev->chipid == PRODUCT_ID_AIC8800D81){
+ memcpy(command, &cfm.rftest_result[0], 6 * 12);
+ bytes_written = 6 * 12;
+ } else {
+ memcpy(command, &cfm.rftest_result[0], 3 * 12);
+ bytes_written = 3 * 12;
+ }
+ } else if (strcasecmp(argv[0], "RDWR_PWROFST") == 0) {
+ u8_l func = 0;
+ AICWFDBG(LOGINFO, "read/write txpwr offset\n");
+ if (argc > 1) {
+ func = (u8_l)command_strtoul(argv[1], NULL, 16);
+ }
+ if (func == 0) { // read cur
+ rwnx_send_rftest_req(p_rwnx_hw, RDWR_PWROFST, 0, NULL, &cfm);
+ } else if (func <= 2) { // write 2.4g/5g pwr ofst
+ if (argc > 3) {
+ u8_l chgrp = (u8_l)command_strtoul(argv[2], NULL, 16);
+ s8_l pwrofst = (u8_l)command_strtoul(argv[3], NULL, 10);
+ u8_l buf[3] = {func, chgrp, (u8_l)pwrofst};
+ AICWFDBG(LOGINFO, "set pwrofst:[%x][%x]=%d\r\n", func, chgrp, pwrofst);
+ rwnx_send_rftest_req(p_rwnx_hw, RDWR_PWROFST, sizeof(buf), buf, &cfm);
+ } else {
+ AICWFDBG(LOGERROR, "wrong args\n");
+ bytes_written = -EINVAL;
+ break;
+ }
+ } else {
+ AICWFDBG(LOGERROR, "wrong func: %x\n", func);
+ bytes_written = -EINVAL;
+ break;
+ }
+ memcpy(command, &cfm.rftest_result[0], 7);
+ bytes_written = 7;
+ } else if (strcasecmp(argv[0], "RDWR_PWROFSTFINE") == 0) {
+ u8_l func = 0;
+ AICWFDBG(LOGINFO, "read/write txpwr offset fine\n");
+ if (argc > 1) {
+ func = (u8_l)command_strtoul(argv[1], NULL, 16);
+ }
+ if (func == 0) { // read cur
+ rwnx_send_rftest_req(p_rwnx_hw, RDWR_PWROFSTFINE, 0, NULL, &cfm);
+ } else if (func <= 2) { // write 2.4g/5g pwr ofst
+ if (argc > 3) {
+ u8_l chgrp = (u8_l)command_strtoul(argv[2], NULL, 16);
+ s8_l pwrofst = (u8_l)command_strtoul(argv[3], NULL, 10);
+ u8_l buf[3] = {func, chgrp, (u8_l)pwrofst};
+ AICWFDBG(LOGINFO, "set pwrofstfine:[%x][%x]=%d\r\n", func, chgrp, pwrofst);
+ rwnx_send_rftest_req(p_rwnx_hw, RDWR_PWROFSTFINE, sizeof(buf), buf, &cfm);
+ } else {
+ AICWFDBG(LOGERROR, "wrong args\n");
+ bytes_written = -EINVAL;
+ break;
+ }
+ } else {
+ AICWFDBG(LOGERROR, "wrong func: %x\n", func);
+ bytes_written = -EINVAL;
+ break;
+ }
+ memcpy(command, &cfm.rftest_result[0], 7);
+ bytes_written = 7;
+ } else if (strcasecmp(argv[0], "RDWR_DRVIBIT") == 0) {
+ u8_l func = 0;
+ AICWFDBG(LOGINFO, "read/write pa drv_ibit\n");
+ if (argc > 1) {
+ func = (u8_l)command_strtoul(argv[1], NULL, 16);
+ }
+ if (func == 0) { // read cur
+ rwnx_send_rftest_req(p_rwnx_hw, RDWR_DRVIBIT, 0, NULL, &cfm);
+ } else if (func == 1) { // write 2.4g pa drv_ibit
+ if (argc > 2) {
+ u8_l ibit = (u8_l)command_strtoul(argv[2], NULL, 16);
+ u8_l buf[2] = {func, ibit};
+ AICWFDBG(LOGINFO, "set drvibit:[%x]=%x\r\n", func, ibit);
+ rwnx_send_rftest_req(p_rwnx_hw, RDWR_DRVIBIT, sizeof(buf), buf, &cfm);
+ } else {
+ AICWFDBG(LOGERROR, "wrong args\n");
+ bytes_written = -EINVAL;
+ break;
+ }
+ } else {
+ AICWFDBG(LOGERROR, "wrong func: %x\n", func);
+ bytes_written = -EINVAL;
+ break;
+ }
+ memcpy(command, &cfm.rftest_result[0], 16);
+ bytes_written = 16;
+ } else if (strcasecmp(argv[0], "RDWR_EFUSE_PWROFST") == 0) {
+ u8_l func = 0;
+ AICWFDBG(LOGINFO, "read/write txpwr offset into efuse\n");
+ if (argc > 1) {
+ func = (u8_l)command_strtoul(argv[1], NULL, 16);
+ }
+ if (func == 0) { // read cur
+ rwnx_send_rftest_req(p_rwnx_hw, RDWR_EFUSE_PWROFST, 0, NULL, &cfm);
+ } else if (func <= 2) { // write 2.4g/5g pwr ofst
+ if (argc > 3) {
+ u8_l chgrp = (u8_l)command_strtoul(argv[2], NULL, 16);
+ s8_l pwrofst = (u8_l)command_strtoul(argv[3], NULL, 10);
+ u8_l buf[3] = {func, chgrp, (u8_l)pwrofst};
+ AICWFDBG(LOGINFO, "set efuse pwrofst:[%x][%x]=%d\r\n", func, chgrp, pwrofst);
+ rwnx_send_rftest_req(p_rwnx_hw, RDWR_EFUSE_PWROFST, sizeof(buf), buf, &cfm);
+ } else {
+ AICWFDBG(LOGERROR, "wrong args\n");
+ bytes_written = -EINVAL;
+ break;
+ }
+ } else {
+ AICWFDBG(LOGERROR, "wrong func: %x\n", func);
+ bytes_written = -EINVAL;
+ break;
+ }
+ if ((g_rwnx_plat->usbdev->chipid == PRODUCT_ID_AIC8800DC) ||
+ (g_rwnx_plat->usbdev->chipid == PRODUCT_ID_AIC8800DW)) { // 6 = 3 (2.4g) * 2
+ memcpy(command, &cfm.rftest_result[0], 6);
+ bytes_written = 6;
+ } else { // 7 = 3(2.4g) + 4(5g)
+ memcpy(command, &cfm.rftest_result[0], 7);
+ bytes_written = 7;
+ }
+ } else if (strcasecmp(argv[0], "RDWR_EFUSE_DRVIBIT") == 0) {
+ u8_l func = 0;
+ AICWFDBG(LOGINFO, "read/write pa drv_ibit into efuse\n");
+ if (argc > 1) {
+ func = (u8_l)command_strtoul(argv[1], NULL, 16);
+ }
+ if (func == 0) { // read cur
+ rwnx_send_rftest_req(p_rwnx_hw, RDWR_EFUSE_DRVIBIT, 0, NULL, &cfm);
+ } else if (func == 1) { // write 2.4g pa drv_ibit
+ if (argc > 2) {
+ u8_l ibit = (u8_l)command_strtoul(argv[2], NULL, 16);
+ u8_l buf[2] = {func, ibit};
+ AICWFDBG(LOGINFO, "set efuse drvibit:[%x]=%x\r\n", func, ibit);
+ rwnx_send_rftest_req(p_rwnx_hw, RDWR_EFUSE_DRVIBIT, sizeof(buf), buf, &cfm);
+ } else {
+ AICWFDBG(LOGERROR, "wrong args\n");
+ bytes_written = -EINVAL;
+ break;
+ }
+ } else {
+ AICWFDBG(LOGERROR, "wrong func: %x\n", func);
+ bytes_written = -EINVAL;
+ break;
+ }
+ memcpy(command, &cfm.rftest_result[0], 4);
+ bytes_written = 4;
+ } else if (strcasecmp(argv[0], "RDWR_EFUSE_PWROFSTFINE") == 0) {
+ u8_l func = 0;
+ AICWFDBG(LOGINFO, "read/write txpwr offset fine into efuse\n");
+ if (argc > 1) {
+ func = (u8_l)command_strtoul(argv[1], NULL, 16);
+ }
+ if (func == 0) { // read cur
+ rwnx_send_rftest_req(p_rwnx_hw, RDWR_EFUSE_PWROFSTFINE, 0, NULL, &cfm);
+ } else if (func <= 2) { // write 2.4g/5g pwr ofst
+ if (argc > 3) {
+ u8_l chgrp = (u8_l)command_strtoul(argv[2], NULL, 16);
+ s8_l pwrofst = (u8_l)command_strtoul(argv[3], NULL, 10);
+ u8_l buf[3] = {func, chgrp, (u8_l)pwrofst};
+ AICWFDBG(LOGINFO, "set efuse pwrofstfine:[%x][%x]=%d\r\n", func, chgrp, pwrofst);
+ rwnx_send_rftest_req(p_rwnx_hw, RDWR_EFUSE_PWROFSTFINE, sizeof(buf), buf, &cfm);
+ } else {
+ AICWFDBG(LOGERROR, "wrong args\n");
+ bytes_written = -EINVAL;
+ break;
+ }
+ } else {
+ AICWFDBG(LOGERROR, "wrong func: %x\n", func);
+ bytes_written = -EINVAL;
+ break;
+ }
+ if ((g_rwnx_plat->usbdev->chipid == PRODUCT_ID_AIC8800DC) ||
+ (g_rwnx_plat->usbdev->chipid == PRODUCT_ID_AIC8800DW)) { // 6 = 3 (2.4g) * 2
+ memcpy(command, &cfm.rftest_result[0], 6);
+ bytes_written = 6;
+ } else { // 7 = 3(2.4g) + 4(5g)
+ memcpy(command, &cfm.rftest_result[0], 7);
+ bytes_written = 7;
+ }
+ } else if (strcasecmp(argv[0], "RDWR_EFUSE_DRVIBIT") == 0) {
+ u8_l func = 0;
+ AICWFDBG(LOGINFO, "read/write pa drv_ibit into efuse\n");
+ if (argc > 1) {
+ func = (u8_l)command_strtoul(argv[1], NULL, 16);
+ }
+ if (func == 0) { // read cur
+ rwnx_send_rftest_req(p_rwnx_hw, RDWR_EFUSE_DRVIBIT, 0, NULL, &cfm);
+ } else if (func == 1) { // write 2.4g pa drv_ibit
+ if (argc > 2) {
+ u8_l ibit = (u8_l)command_strtoul(argv[2], NULL, 16);
+ u8_l buf[2] = {func, ibit};
+ AICWFDBG(LOGINFO, "set efuse drvibit:[%x]=%x\r\n", func, ibit);
+ rwnx_send_rftest_req(p_rwnx_hw, RDWR_EFUSE_DRVIBIT, sizeof(buf), buf, &cfm);
+ } else {
+ AICWFDBG(LOGERROR, "wrong args\n");
+ bytes_written = -EINVAL;
+ break;
+ }
+ } else {
+ AICWFDBG(LOGERROR, "wrong func: %x\n", func);
+ bytes_written = -EINVAL;
+ break;
+ }
+ memcpy(command, &cfm.rftest_result[0], 4);
+ bytes_written = 4;
+ } else if (strcasecmp(argv[0], "SET_CAL_XTAL") == 0) {
+ AICWFDBG(LOGINFO, "set_cal_xtal\n");
+ rwnx_send_rftest_req(p_rwnx_hw, SET_CAL_XTAL, 0, NULL, NULL);
+ } else if (strcasecmp(argv[0], "GET_CAL_XTAL_RES") == 0) {
+ AICWFDBG(LOGINFO, "get_cal_xtal_res\n");
+ rwnx_send_rftest_req(p_rwnx_hw, GET_CAL_XTAL_RES, 0, NULL, &cfm);
+ memcpy(command, &cfm.rftest_result[0], 4);
+ bytes_written = 4;
+ AICWFDBG(LOGINFO, "cap=0x%x, cap_fine=0x%x\n", cfm.rftest_result[0] & 0x0000ffff, (cfm.rftest_result[0] >> 16) & 0x0000ffff);
+ } else if (strcasecmp(argv[0], "SET_COB_CAL") == 0) {
+ AICWFDBG(LOGINFO, "set_cob_cal\n");
+ if (argc < 3) {
+ AICWFDBG(LOGERROR, "wrong param\n");
+ bytes_written = -EINVAL;
+ break;
+ }
+ setcob_cal.dutid = command_strtoul(argv[1], NULL, 10);
+ setcob_cal.chip_num = command_strtoul(argv[2], NULL, 10);
+ setcob_cal.dis_xtal = command_strtoul(argv[3], NULL, 10);
+ rwnx_send_rftest_req(p_rwnx_hw, SET_COB_CAL, sizeof(cmd_rf_setcobcal_t), (u8_l *)&setcob_cal, NULL);
+ } else if (strcasecmp(argv[0], "GET_COB_CAL_RES")==0) {
+ AICWFDBG(LOGINFO, "get_cob_cal_res\n");
+ rwnx_send_rftest_req(p_rwnx_hw, GET_COB_CAL_RES, 0, NULL, &cfm);
+ state = (cfm.rftest_result[0] >> 16) & 0x000000ff;
+ if (!state){
+ AICWFDBG(LOGINFO, "cap= 0x%x, cap_fine= 0x%x, freq_ofst= %d Hz\n",
+ cfm.rftest_result[0] & 0x000000ff, (cfm.rftest_result[0] >> 8) & 0x000000ff, cfm.rftest_result[1]);
+ cob_result_ptr = (cob_result_ptr_t *) & (cfm.rftest_result[2]);
+ AICWFDBG(LOGINFO, "golden_rcv_dut= %d , tx_rssi= %d dBm, snr = %d dB\ndut_rcv_godlden= %d , rx_rssi= %d dBm",
+ cob_result_ptr->golden_rcv_dut_num, cob_result_ptr->rssi_static, cob_result_ptr->snr_static,
+ cob_result_ptr->dut_rcv_golden_num, cob_result_ptr->dut_rssi_static);
+ memcpy(command, &cfm.rftest_result, 16);
+ bytes_written = 16;
+ } else {
+ AICWFDBG(LOGERROR, "cob not idle\n");
+ bytes_written = -EINVAL;
+ break;
+ }
+ } else if (strcasecmp(argv[0], "DO_COB_TEST") == 0) {
+ AICWFDBG(LOGINFO, "do_cob_test\n");
+ setcob_cal.dutid = 1;
+ setcob_cal.chip_num = 1;
+ setcob_cal.dis_xtal = 0;
+ if (argc > 1 ) {
+ setcob_cal.dis_xtal = command_strtoul(argv[1], NULL, 10);
+ }
+ rwnx_send_rftest_req(p_rwnx_hw, SET_COB_CAL, sizeof(cmd_rf_setcobcal_t), (u8_l *)&setcob_cal, NULL);
+ msleep(2000);
+ rwnx_send_rftest_req(p_rwnx_hw, GET_COB_CAL_RES, 0, NULL, &cfm);
+ state = (cfm.rftest_result[0] >> 16) & 0x000000ff;
+ if (!state){
+ AICWFDBG(LOGINFO, "cap= 0x%x, cap_fine= 0x%x, freq_ofst= %d Hz\n",
+ cfm.rftest_result[0] & 0x000000ff, (cfm.rftest_result[0] >> 8) & 0x000000ff, cfm.rftest_result[1]);
+ cob_result_ptr = (cob_result_ptr_t *) & (cfm.rftest_result[2]);
+ AICWFDBG(LOGINFO, "golden_rcv_dut= %d , tx_rssi= %d dBm, snr = %d dB\ndut_rcv_godlden= %d , rx_rssi= %d dBm",
+ cob_result_ptr->golden_rcv_dut_num, cob_result_ptr->rssi_static, cob_result_ptr->snr_static,
+ cob_result_ptr->dut_rcv_golden_num, cob_result_ptr->dut_rssi_static);
+ memcpy(command, &cfm.rftest_result, 16);
+ bytes_written = 16;
+ } else {
+ AICWFDBG(LOGERROR, "cob not idle\n");
+ bytes_written = -EINVAL;
+ break;
+ }
+ } else if (strcasecmp(argv[0], "RDWR_EFUSE_USRDATA") == 0) {
+ AICWFDBG(LOGINFO, "read/write efuse usrdata\n");
+ if (argc <= 1) { // read all
+ cmd_ef_usrdata.func = 0;
+ cmd_ef_usrdata.cnt = 3;
+ } else if (argc >= 2) { // read/write
+ cmd_ef_usrdata.func = (u8_l)command_strtoul(argv[1], NULL, 10);
+ cmd_ef_usrdata.cnt = (u8_l)command_strtoul(argv[2], NULL, 10);
+ if (cmd_ef_usrdata.func == 1) {
+ int idx;
+ for (idx = 0; idx < cmd_ef_usrdata.cnt; idx++) {
+ cmd_ef_usrdata.usrdata[idx] = (u32_l)command_strtoul(argv[3 + idx], NULL, 16);
+ }
+ }
+ } else {
+ AICWFDBG(LOGERROR, "wrong argc: %x\n", argc);
+ bytes_written = -EINVAL;
+ break;
+ }
+ rwnx_send_rftest_req(p_rwnx_hw, RDWR_EFUSE_USRDATA, sizeof(cmd_ef_usrdata), (u8_l *)&cmd_ef_usrdata, &cfm);
+ memcpy(command, &cfm.rftest_result[0], 12);
+ bytes_written = 12;
+ } else if (strcasecmp(argv[0], "RDWR_EFUSE_SDIOCFG") == 0) {
+ u8_l func = 0;
+ AICWFDBG(LOGINFO, "read/write sdiocfg_bit into efuse\n");
+ if (argc > 1) {
+ func = (u8_l)command_strtoul(argv[1], NULL, 16);
+ }
+ if (func == 0) { // read cur
+ rwnx_send_rftest_req(p_rwnx_hw, RDWR_EFUSE_SDIOCFG, 0, NULL, &cfm);
+ } else if (func == 1) { // write sdiocfg
+ if (argc > 2) {
+ u8_l ibit = (u8_l)command_strtoul(argv[2], NULL, 16);
+ u8_l buf[2] = {func, ibit};
+ AICWFDBG(LOGINFO, "set efuse sdiocfg:[%x]=%x\r\n", func, ibit);
+ rwnx_send_rftest_req(p_rwnx_hw, RDWR_EFUSE_SDIOCFG, sizeof(buf), buf, &cfm);
+ } else {
+ AICWFDBG(LOGERROR, "wrong args\n");
+ bytes_written = -EINVAL;
+ break;
+ }
+ } else {
+ AICWFDBG(LOGERROR, "wrong func: %x\n", func);
+ bytes_written = -EINVAL;
+ break;
+ }
+ memcpy(command, &cfm.rftest_result[0], 4);
+ bytes_written = 4;
+ }
+ #ifdef CONFIG_USB_BT
+ else if (strcasecmp(argv[0], "BT_RESET") == 0) {
+ if (argc == 5) {
+ AICWFDBG(LOGINFO, "btrf reset\n");
+ for (bt_index = 0; bt_index < 4; bt_index++) {
+ dh_cmd_reset[bt_index] = command_strtoul(argv[bt_index+1], NULL, 16);
+ AICWFDBG(LOGINFO, "0x%x ",dh_cmd_reset[bt_index]);
+ }
+ AICWFDBG(LOGINFO, "\n");
+ } else {
+ AICWFDBG(LOGERROR, "wrong param\n");
+ bytes_written = -EINVAL;
+ break;
+ }
+ rwnx_send_rftest_req(p_rwnx_hw, BT_RESET, sizeof(dh_cmd_reset), (u8_l *)&dh_cmd_reset, NULL);
+ } else if (strcasecmp(argv[0], "BT_TXDH") == 0) {
+ if (argc == 19) {
+ AICWFDBG(LOGINFO, "btrf txdh\n");
+ for (bt_index = 0; bt_index < 18; bt_index++) {
+ dh_cmd_txdh[bt_index] = command_strtoul(argv[bt_index+1], NULL, 16);
+ AICWFDBG(LOGINFO, "0x%x ", dh_cmd_txdh[bt_index]);
+ }
+ AICWFDBG(LOGINFO, "\n");
+ } else {
+ AICWFDBG(LOGERROR, "wrong param\n");
+ bytes_written = -EINVAL;
+ break;
+ }
+ rwnx_send_rftest_req(p_rwnx_hw, BT_TXDH, sizeof(dh_cmd_txdh), (u8_l *)&dh_cmd_txdh, NULL);
+ } else if (strcasecmp(argv[0], "BT_RXDH") == 0) {
+ if (argc == 18) {
+ AICWFDBG(LOGINFO, "btrf rxdh\n");
+ for (bt_index = 0; bt_index < 17; bt_index++) {
+ dh_cmd_rxdh[bt_index] = command_strtoul(argv[bt_index+1], NULL, 16);
+ AICWFDBG(LOGINFO, "0x%x ", dh_cmd_rxdh[bt_index]);
+ }
+ AICWFDBG(LOGINFO, "\n");
+ } else {
+ AICWFDBG(LOGERROR, "wrong param\n");
+ bytes_written = -EINVAL;
+ break;
+ }
+ rwnx_send_rftest_req(p_rwnx_hw, BT_RXDH, sizeof(dh_cmd_rxdh), (u8_l *)&dh_cmd_rxdh, NULL);
+ } else if (strcasecmp(argv[0], "BT_STOP") == 0) {
+ if (argc == 6) {
+ AICWFDBG(LOGINFO, "btrf stop\n");
+ for (bt_index = 0; bt_index < 5; bt_index++) {
+ dh_cmd_stop[bt_index] = command_strtoul(argv[bt_index+1], NULL, 16);
+ AICWFDBG(LOGINFO, "0x%x ", dh_cmd_stop[bt_index]);
+ }
+ AICWFDBG(LOGINFO, "\n");
+ } else {
+ AICWFDBG(LOGERROR, "wrong param\n");
+ bytes_written = -EINVAL;
+ break;
+ }
+ rwnx_send_rftest_req(p_rwnx_hw, BT_STOP, sizeof(dh_cmd_stop), (u8_l *)&dh_cmd_stop, NULL);
+ } else if (strcasecmp(argv[0], "GET_BT_RX_RESULT") ==0) {
+ AICWFDBG(LOGINFO, "get_bt_rx_result\n");
+ rwnx_send_rftest_req(p_rwnx_hw, GET_BT_RX_RESULT, 0, NULL, &cfm);
+ memcpy(command, &cfm.rftest_result[0], 12);
+ bytes_written = 12;
+ }
+ #endif
+ else {
+ AICWFDBG(LOGERROR, "wrong cmd:%s in %s\n", cmd, __func__);
+ bytes_written = -EINVAL;
+ break;
+ }
+ #endif
+ } while(0);
+ kfree(cmd);
+ kfree(para);
+ return bytes_written;
+}
+
+//Android private command
+
+#define RWNX_COUNTRY_CODE_LEN 2
+#define CMD_SET_COUNTRY "COUNTRY"
+#define CMD_SET_VENDOR_EX_IE "SET_VENDOR_EX_IE"
+#define CMD_SET_AP_WPS_P2P_IE "SET_AP_WPS_P2P_IE"
+#define CMD_SET_TESTMODE "SET_TESTMODE"
+
+
+struct ieee80211_regdomain *getRegdomainFromRwnxDB(struct wiphy *wiphy, char *alpha2);
+struct ieee80211_regdomain *getRegdomainFromRwnxDBIndex(struct wiphy *wiphy, int index);
+extern int reg_regdb_size;
+
+#ifdef CONFIG_SET_VENDOR_EXTENSION_IE
+extern u8_l vendor_extension_data[256];
+extern u8_l vendor_extension_len;
+
+void set_vendor_extension_ie(char *command){
+
+ char databyte[3]={0x00, 0x00, 0x00};
+ int skip = strlen(CMD_SET_VENDOR_EX_IE) + 1;
+ int command_index = skip;
+ int data_index = 0;
+
+ memset(vendor_extension_data, 0, 256);
+ vendor_extension_len = 0;
+ memcpy(databyte, command + command_index, 2);
+ vendor_extension_len = command_strtoul(databyte, NULL, 16);
+ AICWFDBG(LOGINFO, "%s len:%d \r\n", __func__, vendor_extension_len);
+
+ //parser command and save data in vendor_extension_data
+ for(data_index = 0;data_index < vendor_extension_len; data_index++){
+ command_index = command_index + 3;
+ memcpy(databyte, command + command_index, 2);
+ vendor_extension_data[data_index] = command_strtoul(databyte, NULL, 16);
+ }
+
+}
+#endif//CONFIG_SET_VENDOR_EXTENSION_IE
+
+int android_priv_cmd(struct net_device *net, struct ifreq *ifr, int cmd)
+{
+#define PRIVATE_COMMAND_MAX_LEN 8192
+#define PRIVATE_COMMAND_DEF_LEN 4096
+
+ struct rwnx_vif *vif = netdev_priv(net);
+ int ret = 0;
+ char *command = NULL;
+ int bytes_written = 0;
+ android_wifi_priv_cmd priv_cmd;
+ int buf_size = 0;
+ int skip = 0;
+ char *country = NULL;
+ struct ieee80211_regdomain *regdomain;
+ //int index = 0;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ ///todo: add our lock
+ //net_os_wake_lock(net);
+
+
+/* if (!capable(CAP_NET_ADMIN)) {
+ ret = -EPERM;
+ goto exit;
+ }*/
+ if (!ifr->ifr_data) {
+ ret = -EINVAL;
+ goto exit;
+ }
+
+#ifdef CONFIG_COMPAT
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 6, 0))
+ if (in_compat_syscall())
+#else
+ if (is_compat_task())
+#endif
+ {
+ compat_android_wifi_priv_cmd compat_priv_cmd;
+ if (copy_from_user(&compat_priv_cmd, ifr->ifr_data, sizeof(compat_android_wifi_priv_cmd))) {
+ ret = -EFAULT;
+ goto exit;
+ }
+ priv_cmd.buf = compat_ptr(compat_priv_cmd.buf);
+ priv_cmd.used_len = compat_priv_cmd.used_len;
+ priv_cmd.total_len = compat_priv_cmd.total_len;
+ } else
+#endif /* CONFIG_COMPAT */
+ {
+ if (copy_from_user(&priv_cmd, ifr->ifr_data, sizeof(android_wifi_priv_cmd))) {
+ ret = -EFAULT;
+ goto exit;
+ }
+ }
+ if ((priv_cmd.total_len > PRIVATE_COMMAND_MAX_LEN) || (priv_cmd.total_len < 0)) {
+ AICWFDBG(LOGERROR, "%s: buf length invalid:%d\n", __FUNCTION__, priv_cmd.total_len);
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ buf_size = max(priv_cmd.total_len, PRIVATE_COMMAND_DEF_LEN);
+ command = kmalloc((buf_size + 1), GFP_KERNEL);
+
+ if (!command)
+ {
+ AICWFDBG(LOGERROR, "%s: failed to allocate memory\n", __FUNCTION__);
+ ret = -ENOMEM;
+ goto exit;
+ }
+ if (copy_from_user(command, priv_cmd.buf, priv_cmd.total_len)) {
+ ret = -EFAULT;
+ goto exit;
+ }
+ command[priv_cmd.total_len] = '\0';
+
+ /* outputs */
+ AICWFDBG(LOGINFO, "%s: Android private cmd \"%s\" on %s\n", __FUNCTION__, command, ifr->ifr_name);
+ AICWFDBG(LOGINFO, "cmd = %d\n", cmd);
+ AICWFDBG(LOGINFO, "buf_size=%d\n", buf_size);
+
+
+#if 1//Handle Android command
+ if(!strncasecmp(command, CMD_SET_COUNTRY, strlen(CMD_SET_COUNTRY))) {
+ skip = strlen(CMD_SET_COUNTRY) + 1;
+ country = command + skip;
+ if (!country || strlen(country) < RWNX_COUNTRY_CODE_LEN) {
+ AICWFDBG(LOGERROR, "%s: invalid country code\n", __func__);
+ ret = -EINVAL;
+ goto exit;
+ }
+#if 0
+ for(index = 0; index < reg_regdb_size; index++){
+ regdomain = getRegdomainFromRwnxDBIndex(vif->rwnx_hw->wiphy, index);
+ if((ret = regulatory_set_wiphy_regd(vif->rwnx_hw->wiphy, regdomain))){
+ AICWFDBG(LOGERROR, "regulatory_set_wiphy_regd fail \r\n");
+ }else{
+ AICWFDBG(LOGINFO, "regulatory_set_wiphy_regd ok \r\n");
+ }
+ }
+#endif
+ AICWFDBG(LOGINFO, "%s country code:%c%c\n", __func__, toupper(country[0]), toupper(country[1]));
+ regdomain = getRegdomainFromRwnxDB(vif->rwnx_hw->wiphy, country);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)
+ if((ret = regulatory_set_wiphy_regd(vif->rwnx_hw->wiphy, regdomain))){
+ AICWFDBG(LOGERROR, "regulatory_set_wiphy_regd fail \r\n");
+ }
+#else
+ wiphy_apply_custom_regulatory(vif->rwnx_hw->wiphy, regdomain);
+#endif
+ }
+#ifdef CONFIG_SET_VENDOR_EXTENSION_IE
+ else if(!strncasecmp(command, CMD_SET_VENDOR_EX_IE, strlen(CMD_SET_VENDOR_EX_IE))){
+ set_vendor_extension_ie(command);
+ }
+#endif//CONFIG_SET_VENDOR_EXTENSION_IE
+ else if(!strncasecmp(command, CMD_SET_AP_WPS_P2P_IE, strlen(CMD_SET_AP_WPS_P2P_IE))){
+ ret = 0;
+ goto exit;
+ }else if(!strncasecmp(command, CMD_SET_TESTMODE, strlen(CMD_SET_TESTMODE))){
+ if(g_rwnx_plat && g_rwnx_plat->usbdev->rwnx_hw){
+ if (g_rwnx_plat->usbdev->chipid == PRODUCT_ID_AIC8800DW ||
+ (g_rwnx_plat->usbdev->chipid == PRODUCT_ID_AIC8800DC)){
+ set_testmode(!testmode);
+ rwnx_send_reboot(g_rwnx_plat->usbdev->rwnx_hw);
+ }
+ }
+ ret = 0;
+ goto exit;
+ }
+#endif//Handle Android command
+
+
+ bytes_written = handle_private_cmd(net, command, priv_cmd.total_len);
+ if (bytes_written >= 0) {
+ if ((bytes_written == 0) && (priv_cmd.total_len > 0)) {
+ command[0] = '\0';
+ }
+ if (bytes_written >= priv_cmd.total_len) {
+ AICWFDBG(LOGINFO, "%s: err. bytes_written:%d >= buf_size:%d \n",
+ __FUNCTION__, bytes_written, buf_size);
+ goto exit;
+ }
+ bytes_written++;
+ priv_cmd.used_len = bytes_written;
+ if (copy_to_user(priv_cmd.buf, command, bytes_written)) {
+ AICWFDBG(LOGERROR, "%s: failed to copy data to user buffer\n", __FUNCTION__);
+ ret = -EFAULT;
+ }
+ }
+ else {
+ /* Propagate the error */
+ ret = bytes_written;
+ }
+
+exit:
+ ///todo: add our unlock
+ //net_os_wake_unlock(net);
+ kfree(command);
+ return ret;
+}
+
+#define IOCTL_HOSTAPD (SIOCIWFIRSTPRIV+28)
+#define IOCTL_WPAS (SIOCIWFIRSTPRIV+30)
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0)
+static int rwnx_do_ioctl(struct net_device *net, struct ifreq *req, void __user *data, int cmd)
+#else
+static int rwnx_do_ioctl(struct net_device *net, struct ifreq *req, int cmd)
+#endif
+{
+ int ret = 0;
+ ///TODO: add ioctl command handler later
+ switch(cmd)
+ {
+ case IOCTL_HOSTAPD:
+ AICWFDBG(LOGINFO, "IOCTL_HOSTAPD\n");
+ break;
+ case IOCTL_WPAS:
+ AICWFDBG(LOGINFO, "IOCTL_WPAS\n");
+ break;
+ case SIOCDEVPRIVATE:
+ AICWFDBG(LOGINFO, "IOCTL SIOCDEVPRIVATE\n");
+ break;
+ case (SIOCDEVPRIVATE+1):
+ AICWFDBG(LOGINFO, "IOCTL PRIVATE\n");
+ android_priv_cmd(net, req, cmd);
+ break;
+ default:
+ ret = -EOPNOTSUPP;
+ }
+ return ret;
+}
+
+/**
+ * struct net_device_stats* (*ndo_get_stats)(struct net_device *dev);
+ * Called when a user wants to get the network device usage
+ * statistics. Drivers must do one of the following:
+ * 1. Define @ndo_get_stats64 to fill in a zero-initialised
+ * rtnl_link_stats64 structure passed by the caller.
+ * 2. Define @ndo_get_stats to update a net_device_stats structure
+ * (which should normally be dev->stats) and return a pointer to
+ * it. The structure may be changed asynchronously only if each
+ * field is written atomically.
+ * 3. Update dev->stats asynchronously and atomically, and define
+ * neither operation.
+ */
+static struct net_device_stats *rwnx_get_stats(struct net_device *dev)
+{
+ struct rwnx_vif *vif = netdev_priv(dev);
+
+ return &vif->net_stats;
+}
+
+/**
+ * u16 (*ndo_select_queue)(struct net_device *dev, struct sk_buff *skb,
+ * struct net_device *sb_dev);
+ * Called to decide which queue to when device supports multiple
+ * transmit queues.
+ */
+u16 rwnx_select_queue(struct net_device *dev, struct sk_buff *skb,
+ struct net_device *sb_dev)
+{
+ struct rwnx_vif *rwnx_vif = netdev_priv(dev);
+ return rwnx_select_txq(rwnx_vif, skb);
+}
+
+/**
+ * int (*ndo_set_mac_address)(struct net_device *dev, void *addr);
+ * This function is called when the Media Access Control address
+ * needs to be changed. If this interface is not defined, the
+ * mac address can not be changed.
+ */
+static int rwnx_set_mac_address(struct net_device *dev, void *addr)
+{
+ struct sockaddr *sa = addr;
+ int ret;
+ struct rwnx_vif *rwnx_vif = netdev_priv(dev);
+ printk("%s enter \r\n", __func__);
+
+ ret = eth_mac_addr(dev, sa);
+ memcpy(rwnx_vif->wdev.address, dev->dev_addr, 6);
+
+ return ret;
+}
+
+static const struct net_device_ops rwnx_netdev_ops = {
+ .ndo_open = rwnx_open,
+ .ndo_stop = rwnx_close,
+ #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0)
+ .ndo_siocdevprivate = rwnx_do_ioctl,
+ #else
+ .ndo_do_ioctl = rwnx_do_ioctl,
+ #endif
+ .ndo_start_xmit = rwnx_start_xmit,
+ .ndo_get_stats = rwnx_get_stats,
+#ifndef CONFIG_ONE_TXQ
+ .ndo_select_queue = rwnx_select_queue,
+#endif
+#ifdef CONFIG_SUPPORT_REALTIME_CHANGE_MAC
+ .ndo_set_mac_address = rwnx_set_mac_address
+#endif
+// .ndo_set_features = rwnx_set_features,
+// .ndo_set_rx_mode = rwnx_set_multicast_list,
+};
+
+static const struct net_device_ops rwnx_netdev_monitor_ops = {
+ .ndo_open = rwnx_open,
+ .ndo_stop = rwnx_close,
+ #ifdef CONFIG_RWNX_MON_XMIT
+ .ndo_start_xmit = rwnx_start_monitor_if_xmit,
+ .ndo_select_queue = rwnx_select_queue,
+ #endif
+ .ndo_get_stats = rwnx_get_stats,
+ .ndo_set_mac_address = rwnx_set_mac_address,
+};
+
+static void rwnx_netdev_setup(struct net_device *dev)
+{
+ ether_setup(dev);
+ dev->priv_flags &= ~IFF_TX_SKB_SHARING;
+ dev->netdev_ops = &rwnx_netdev_ops;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0)
+ dev->destructor = free_netdev;
+#else
+ dev->needs_free_netdev = true;
+#endif
+ dev->watchdog_timeo = RWNX_TX_LIFETIME_MS;
+
+ dev->needed_headroom = sizeof(struct rwnx_txhdr) + RWNX_SWTXHDR_ALIGN_SZ - 14;
+#ifdef CONFIG_RWNX_AMSDUS_TX
+ dev->needed_headroom = max(dev->needed_headroom,
+ (unsigned short)(sizeof(struct rwnx_amsdu_txhdr)
+ + sizeof(struct ethhdr) + 4
+ + sizeof(rfc1042_header) + 2));
+#endif /* CONFIG_RWNX_AMSDUS_TX */
+
+ dev->hw_features = 0;
+}
+
+#ifndef CONFIG_USE_WIRELESS_EXT
+#ifdef CONFIG_WIRELESS_EXT
+ #include <net/iw_handler.h>
+ struct iw_handler_def aic_handlers_def;
+#endif
+#endif
+
+
+/*********************************************************************
+ * Cfg80211 callbacks (and helper)
+ *********************************************************************/
+static struct rwnx_vif *rwnx_interface_add(struct rwnx_hw *rwnx_hw,
+ const char *name,
+ unsigned char name_assign_type,
+ enum nl80211_iftype type,
+ struct vif_params *params)
+{
+ struct net_device *ndev;
+ struct rwnx_vif *vif;
+ int min_idx, max_idx;
+ int vif_idx = -1;
+ int i;
+ int nx_nb_ndev_txq = NX_NB_NDEV_TXQ;
+
+
+ if((g_rwnx_plat->usbdev->chipid == PRODUCT_ID_AIC8801) ||
+ ((g_rwnx_plat->usbdev->chipid == PRODUCT_ID_AIC8800DC ||
+ g_rwnx_plat->usbdev->chipid == PRODUCT_ID_AIC8800DW) && chip_id < 3)){
+ nx_nb_ndev_txq = NX_NB_NDEV_TXQ_FOR_OLD_IC;
+ }
+
+ AICWFDBG(LOGINFO, "rwnx_interface_add: %s, %d, %d\r\n", name, type, NL80211_IFTYPE_P2P_DEVICE);
+ // Look for an available VIF
+ if (type == NL80211_IFTYPE_AP_VLAN) {
+ min_idx = NX_VIRT_DEV_MAX;
+ max_idx = NX_VIRT_DEV_MAX + NX_REMOTE_STA_MAX;
+ } else {
+ min_idx = 0;
+ max_idx = NX_VIRT_DEV_MAX;
+ }
+
+ for (i = min_idx; i < max_idx; i++) {
+ if ((rwnx_hw->avail_idx_map) & BIT(i)) {
+ vif_idx = i;
+ break;
+ }
+ }
+ if (vif_idx < 0)
+ return NULL;
+
+ #ifndef CONFIG_RWNX_MON_DATA
+ list_for_each_entry(vif, &rwnx_hw->vifs, list) {
+ // Check if monitor interface already exists or type is monitor
+ if ((RWNX_VIF_TYPE(vif) == NL80211_IFTYPE_MONITOR) ||
+ (type == NL80211_IFTYPE_MONITOR)) {
+ wiphy_err(rwnx_hw->wiphy,
+ "Monitor+Data interface support (MON_DATA) disabled\n");
+ return NULL;
+ }
+ }
+ #endif
+
+#ifndef CONFIG_ONE_TXQ
+ ndev = alloc_netdev_mqs(sizeof(*vif), name, name_assign_type,
+ rwnx_netdev_setup, nx_nb_ndev_txq, 1);
+#else
+ ndev = alloc_netdev_mqs(sizeof(*vif), name, name_assign_type,
+ rwnx_netdev_setup, 1, 1);
+#endif
+
+ if (!ndev)
+ return NULL;
+
+ vif = netdev_priv(ndev);
+ vif->key_has_add = 0;
+ ndev->ieee80211_ptr = &vif->wdev;
+ vif->wdev.wiphy = rwnx_hw->wiphy;
+ vif->rwnx_hw = rwnx_hw;
+ vif->ndev = ndev;
+ vif->drv_vif_index = vif_idx;
+ SET_NETDEV_DEV(ndev, wiphy_dev(vif->wdev.wiphy));
+ vif->wdev.netdev = ndev;
+ vif->wdev.iftype = type;
+ vif->up = false;
+ vif->ch_index = RWNX_CH_NOT_SET;
+ memset(&vif->net_stats, 0, sizeof(vif->net_stats));
+ vif->is_p2p_vif = 0;
+#ifdef CONFIG_BR_SUPPORT
+ spin_lock_init(&vif->br_ext_lock);
+#endif /* CONFIG_BR_SUPPORT */
+
+ switch (type) {
+ case NL80211_IFTYPE_STATION:
+ vif->sta.ap = NULL;
+ vif->sta.tdls_sta = NULL;
+ vif->sta.external_auth = false;
+ break;
+ case NL80211_IFTYPE_P2P_CLIENT:
+ vif->sta.ap = NULL;
+ vif->sta.tdls_sta = NULL;
+ vif->sta.external_auth = false;
+ vif->is_p2p_vif = 1;
+ break;
+ case NL80211_IFTYPE_MESH_POINT:
+ INIT_LIST_HEAD(&vif->ap.mpath_list);
+ INIT_LIST_HEAD(&vif->ap.proxy_list);
+ vif->ap.create_path = false;
+ vif->ap.generation = 0;
+ vif->ap.mesh_pm = NL80211_MESH_POWER_ACTIVE;
+ vif->ap.next_mesh_pm = NL80211_MESH_POWER_ACTIVE;
+ // no break
+ case NL80211_IFTYPE_AP:
+ INIT_LIST_HEAD(&vif->ap.sta_list);
+ memset(&vif->ap.bcn, 0, sizeof(vif->ap.bcn));
+ break;
+ case NL80211_IFTYPE_P2P_GO:
+ INIT_LIST_HEAD(&vif->ap.sta_list);
+ memset(&vif->ap.bcn, 0, sizeof(vif->ap.bcn));
+ vif->is_p2p_vif = 1;
+ break;
+ case NL80211_IFTYPE_AP_VLAN:
+ {
+ struct rwnx_vif *master_vif;
+ bool found = false;
+ list_for_each_entry(master_vif, &rwnx_hw->vifs, list) {
+ if ((RWNX_VIF_TYPE(master_vif) == NL80211_IFTYPE_AP) &&
+ !(!memcmp(master_vif->ndev->dev_addr, params->macaddr,
+ ETH_ALEN))) {
+ found=true;
+ break;
+ }
+ }
+
+ if (!found)
+ goto err;
+
+ vif->ap_vlan.master = master_vif;
+ vif->ap_vlan.sta_4a = NULL;
+ break;
+ }
+ case NL80211_IFTYPE_MONITOR:
+ ndev->type = ARPHRD_IEEE80211_RADIOTAP;
+ ndev->netdev_ops = &rwnx_netdev_monitor_ops;
+ break;
+ default:
+ break;
+ }
+
+ if (type == NL80211_IFTYPE_AP_VLAN) {
+ memcpy(ndev->dev_addr, params->macaddr, ETH_ALEN);
+ memcpy(vif->wdev.address, params->macaddr, ETH_ALEN);
+ }
+ else {
+#if 1
+ unsigned char mac_addr[6];
+ memcpy(mac_addr, rwnx_hw->wiphy->perm_addr, ETH_ALEN);
+ memcpy(mac_addr, rwnx_hw->wiphy->perm_addr, ETH_ALEN);
+ mac_addr[5] ^= vif_idx;
+// memcpy(ndev->dev_addr, mac_addr, ETH_ALEN);
+ eth_hw_addr_set(ndev, mac_addr);
+ memcpy(vif->wdev.address, ndev->dev_addr, ETH_ALEN);
+#else
+ memcpy(ndev->dev_addr, rwnx_hw->wiphy->perm_addr, ETH_ALEN);
+ ndev->dev_addr[5] ^= vif_idx;
+ memcpy(vif->wdev.address, ndev->dev_addr, ETH_ALEN);
+#endif
+ }
+
+ AICWFDBG(LOGINFO, "interface add:%x %x %x %x %x %x\n", vif->wdev.address[0], vif->wdev.address[1],
+ vif->wdev.address[2], vif->wdev.address[3], vif->wdev.address[4], vif->wdev.address[5]);
+
+ if (params) {
+ vif->use_4addr = params->use_4addr;
+ ndev->ieee80211_ptr->use_4addr = params->use_4addr;
+ } else
+ vif->use_4addr = false;
+
+#ifdef CONFIG_USE_WIRELESS_EXT
+ aicwf_set_wireless_ext(ndev, rwnx_hw);
+#else
+#ifdef CONFIG_WIRELESS_EXT
+ memset(&aic_handlers_def, 0,sizeof(struct iw_handler_def));
+ ndev->wireless_handlers = (struct iw_handler_def *)&aic_handlers_def;
+#endif
+#endif
+
+
+ if (register_netdevice(ndev))
+ goto err;
+
+ spin_lock_bh(&rwnx_hw->cb_lock);
+ list_add_tail(&vif->list, &rwnx_hw->vifs);
+ spin_unlock_bh(&rwnx_hw->cb_lock);
+ rwnx_hw->avail_idx_map &= ~BIT(vif_idx);
+
+ return vif;
+
+err:
+ free_netdev(ndev);
+ return NULL;
+}
+
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0)
+void aicwf_p2p_alive_timeout(ulong data)
+#else
+void aicwf_p2p_alive_timeout(struct timer_list *t)
+#endif
+{
+ struct rwnx_hw *rwnx_hw;
+ struct rwnx_vif *rwnx_vif;
+ struct rwnx_vif *rwnx_vif1, *tmp;
+ u8_l p2p = 0;
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0)
+ rwnx_vif = (struct rwnx_vif *)data;
+ rwnx_hw = rwnx_vif->rwnx_hw;
+ #else
+ rwnx_hw = from_timer(rwnx_hw, t, p2p_alive_timer);
+ rwnx_vif = rwnx_hw->p2p_dev_vif;
+ #endif
+
+ //printk("%s enter %d \r\n", __func__, atomic_read(&rwnx_hw->p2p_alive_timer_count));
+
+#if 1 //AIDEN workaround
+ if(atomic_read(&rwnx_hw->p2p_alive_timer_count) > 2){
+ p2p_working = 0;
+ }
+#endif
+
+ list_for_each_entry_safe(rwnx_vif1, tmp, &rwnx_hw->vifs, list) {
+ if ((rwnx_hw->avail_idx_map & BIT(rwnx_vif1->drv_vif_index)) == 0) {
+ switch(RWNX_VIF_TYPE(rwnx_vif1)) {
+ case NL80211_IFTYPE_P2P_CLIENT:
+ case NL80211_IFTYPE_P2P_GO:
+ rwnx_hw->is_p2p_alive = 1;
+ p2p = 1;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ if (p2p){
+ atomic_set(&rwnx_hw->p2p_alive_timer_count, 0);
+ }else{
+ atomic_inc(&rwnx_hw->p2p_alive_timer_count);
+ }
+
+ if (atomic_read(&rwnx_hw->p2p_alive_timer_count) < P2P_ALIVE_TIME_COUNT) {
+ mod_timer(&rwnx_hw->p2p_alive_timer,
+ jiffies + msecs_to_jiffies(P2P_ALIVE_TIME_MS));
+ return;
+ } else
+ atomic_set(&rwnx_hw->p2p_alive_timer_count, 0);
+
+ rwnx_hw->is_p2p_alive = 0;
+ rwnx_send_remove_if(rwnx_hw, rwnx_vif->vif_index, true);
+
+ /* Ensure that we won't process disconnect ind */
+ spin_lock_bh(&rwnx_hw->cb_lock);
+
+ rwnx_vif->up = false;
+ rwnx_hw->vif_table[rwnx_vif->vif_index] = NULL;
+ rwnx_hw->vif_started--;
+ spin_unlock_bh(&rwnx_hw->cb_lock);
+}
+
+
+/*********************************************************************
+ * Cfg80211 callbacks (and helper)
+ *********************************************************************/
+static struct wireless_dev *rwnx_virtual_interface_add(struct rwnx_hw *rwnx_hw,
+ const char *name,
+ unsigned char name_assign_type,
+ enum nl80211_iftype type,
+ struct vif_params *params)
+{
+ struct wireless_dev *wdev = NULL;
+ struct rwnx_vif *vif;
+ int min_idx, max_idx;
+ int vif_idx = -1;
+ int i;
+
+ AICWFDBG(LOGINFO, "rwnx_virtual_interface_add: %d, %s\n", type, name);
+
+ if (type == NL80211_IFTYPE_AP_VLAN) {
+ min_idx = NX_VIRT_DEV_MAX;
+ max_idx = NX_VIRT_DEV_MAX + NX_REMOTE_STA_MAX;
+ } else {
+ min_idx = 0;
+ max_idx = NX_VIRT_DEV_MAX;
+ }
+
+ for (i = min_idx; i < max_idx; i++) {
+ if ((rwnx_hw->avail_idx_map) & BIT(i)) {
+ vif_idx = i;
+ break;
+ }
+ }
+
+ if (vif_idx < 0) {
+ AICWFDBG(LOGERROR, "virtual_interface_add %s fail\n", name);
+ return NULL;
+ }
+
+ vif = kzalloc(sizeof(struct rwnx_vif), GFP_KERNEL);
+ if (unlikely(!vif)) {
+ AICWFDBG(LOGERROR, "Could not allocate wireless device\n");
+ return NULL;
+ }
+ wdev = &vif->wdev;
+ wdev->wiphy = rwnx_hw->wiphy;
+ wdev->iftype = type;
+
+ AICWFDBG(LOGINFO, "rwnx_virtual_interface_add, ifname=%s, wdev=%p, vif_idx=%d\n", name, wdev, vif_idx);
+
+ #ifndef CONFIG_USE_P2P0
+ vif->is_p2p_vif = 1;
+ vif->rwnx_hw = rwnx_hw;
+ vif->vif_index = vif_idx;
+ vif->wdev.wiphy = rwnx_hw->wiphy;
+ vif->drv_vif_index = vif_idx;
+ vif->up = false;
+ vif->ch_index = RWNX_CH_NOT_SET;
+ memset(&vif->net_stats, 0, sizeof(vif->net_stats));
+ vif->use_4addr = false;
+
+ spin_lock_bh(&rwnx_hw->cb_lock);
+ list_add_tail(&vif->list, &rwnx_hw->vifs);
+ spin_unlock_bh(&rwnx_hw->cb_lock);
+
+ if (rwnx_hw->is_p2p_alive == 0) {
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0)
+ init_timer(&rwnx_hw->p2p_alive_timer);
+ rwnx_hw->p2p_alive_timer.data = (unsigned long)vif;
+ rwnx_hw->p2p_alive_timer.function = aicwf_p2p_alive_timeout;
+ #else
+ timer_setup(&rwnx_hw->p2p_alive_timer, aicwf_p2p_alive_timeout, 0);
+ #endif
+ rwnx_hw->is_p2p_alive = 0;
+ rwnx_hw->is_p2p_connected = 0;
+ rwnx_hw->p2p_dev_vif = vif;
+ atomic_set(&rwnx_hw->p2p_alive_timer_count, 0);
+ }
+ #endif
+ rwnx_hw->avail_idx_map &= ~BIT(vif_idx);
+
+ memcpy(vif->wdev.address, rwnx_hw->wiphy->perm_addr, ETH_ALEN);
+ vif->wdev.address[5] ^= vif_idx;
+ AICWFDBG(LOGERROR, "p2p dev addr=%x %x %x %x %x %x\n", vif->wdev.address[0], vif->wdev.address[1], \
+ vif->wdev.address[2], vif->wdev.address[3], vif->wdev.address[4], vif->wdev.address[5]);
+
+ return wdev;
+}
+
+/*
+ * @brief Retrieve the rwnx_sta object allocated for a given MAC address
+ * and a given role.
+ */
+
+static struct rwnx_sta *rwnx_retrieve_sta(struct rwnx_hw *rwnx_hw,
+ struct rwnx_vif *rwnx_vif, u8 *addr,
+ __le16 fc, bool ap)
+{
+ if (ap) {
+ /* only deauth, disassoc and action are bufferable MMPDUs */
+ bool bufferable = ieee80211_is_deauth(fc) ||
+ ieee80211_is_disassoc(fc) ||
+ ieee80211_is_action(fc);
+
+ /* Check if the packet is bufferable or not */
+ if (bufferable)
+ {
+ /* Check if address is a broadcast or a multicast address */
+ if (is_broadcast_ether_addr(addr) || is_multicast_ether_addr(addr)) {
+ /* Returned STA pointer */
+ struct rwnx_sta *rwnx_sta = &rwnx_hw->sta_table[rwnx_vif->ap.bcmc_index];
+
+ if (rwnx_sta->valid)
+ return rwnx_sta;
+ } else {
+ /* Returned STA pointer */
+ struct rwnx_sta *rwnx_sta;
+
+ /* Go through list of STAs linked with the provided VIF */
+ spin_lock_bh(&rwnx_vif->rwnx_hw->cb_lock);
+ list_for_each_entry(rwnx_sta, &rwnx_vif->ap.sta_list, list) {
+ AICWFDBG(LOGDEBUG, "%s mac_addr:%x %x %x %x %x %x addr:%x %x %x %x %x %x \r\n", __func__,
+ rwnx_sta->mac_addr[0],rwnx_sta->mac_addr[1],rwnx_sta->mac_addr[2],
+ rwnx_sta->mac_addr[3],rwnx_sta->mac_addr[4],rwnx_sta->mac_addr[5],
+ addr[0],addr[1],addr[2],addr[3],addr[4],addr[5]);
+ if (rwnx_sta->valid &&
+ ether_addr_equal(rwnx_sta->mac_addr, addr)) {
+ /* Return the found STA */
+ spin_unlock_bh(&rwnx_vif->rwnx_hw->cb_lock);
+ return rwnx_sta;
+ }
+ }
+ spin_unlock_bh(&rwnx_vif->rwnx_hw->cb_lock);
+ }
+ }
+ } else {
+ return rwnx_vif->sta.ap;
+ }
+
+ return NULL;
+}
+
+/**
+ * @add_virtual_intf: create a new virtual interface with the given name,
+ * must set the struct wireless_dev's iftype. Beware: You must create
+ * the new netdev in the wiphy's network namespace! Returns the struct
+ * wireless_dev, or an ERR_PTR. For P2P device wdevs, the driver must
+ * also set the address member in the wdev.
+ */
+static struct wireless_dev *rwnx_cfg80211_add_iface(struct wiphy *wiphy,
+ const char *name,
+ unsigned char name_assign_type,
+ enum nl80211_iftype type,
+ struct vif_params *params)
+{
+ struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy);
+ struct wireless_dev *wdev;
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0))
+ unsigned char name_assign_type = NET_NAME_UNKNOWN;
+#endif
+
+ if (type != NL80211_IFTYPE_P2P_DEVICE) {
+ struct rwnx_vif *vif= rwnx_interface_add(rwnx_hw, name, name_assign_type, type, params);
+ if (!vif)
+ return ERR_PTR(-EINVAL);
+ return &vif->wdev;
+
+ } else {
+ wdev = rwnx_virtual_interface_add(rwnx_hw, name, name_assign_type, type, params);
+ if (!wdev)
+ return ERR_PTR(-EINVAL);
+ return wdev;
+ }
+}
+
+/**
+ * @del_virtual_intf: remove the virtual interface
+ */
+static int rwnx_cfg80211_del_iface(struct wiphy *wiphy, struct wireless_dev *wdev)
+{
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0))
+ struct net_device *dev = wdev->netdev;
+ struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy);
+ struct rwnx_vif *rwnx_vif = container_of(wdev, struct rwnx_vif, wdev);
+#else
+ struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy);
+ struct rwnx_vif *rwnx_vif = netdev_priv(dev);
+#endif
+ // printk("del_iface: %p\n",wdev);
+
+ AICWFDBG(LOGINFO, "del_iface: %p, %x\n",wdev, wdev->address[5]);
+
+ if (!dev || !rwnx_vif->ndev) {
+#if 0
+ if (rwnx_vif == rwnx_hw->p2p_dev_vif) {
+ if (timer_pending(&rwnx_hw->p2p_alive_timer)) {
+ del_timer_sync(&rwnx_hw->p2p_alive_timer);
+ }
+ }
+#endif
+ cfg80211_unregister_wdev(wdev);
+ spin_lock_bh(&rwnx_hw->cb_lock);
+ list_del(&rwnx_vif->list);
+ spin_unlock_bh(&rwnx_hw->cb_lock);
+ rwnx_hw->avail_idx_map |= BIT(rwnx_vif->drv_vif_index);
+ rwnx_vif->ndev = NULL;
+ kfree(rwnx_vif);
+ return 0;
+ }
+#if 0
+ netdev_info(dev, "Remove Interface");
+#endif
+
+ AICWFDBG(LOGINFO, "%s Remove Interface \r\n", dev->name);
+ if (dev->reg_state == NETREG_REGISTERED) {
+ /* Will call rwnx_close if interface is UP */
+ unregister_netdevice(dev);
+ }
+
+ spin_lock_bh(&rwnx_hw->cb_lock);
+ list_del(&rwnx_vif->list);
+ spin_unlock_bh(&rwnx_hw->cb_lock);
+ rwnx_hw->avail_idx_map |= BIT(rwnx_vif->drv_vif_index);
+ rwnx_vif->ndev = NULL;
+
+ /* Clear the priv in adapter */
+ dev->ieee80211_ptr = NULL;
+
+ return 0;
+}
+
+/**
+ * @change_virtual_intf: change type/configuration of virtual interface,
+ * keep the struct wireless_dev's iftype updated.
+ */
+static int rwnx_cfg80211_change_iface(struct wiphy *wiphy,
+ struct net_device *dev,
+ enum nl80211_iftype type,
+ struct vif_params *params)
+{
+#ifndef CONFIG_RWNX_MON_DATA
+ struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy);
+#endif
+ struct rwnx_vif *vif = netdev_priv(dev);
+ struct mm_add_if_cfm add_if_cfm;
+ bool_l p2p = false;
+ int ret;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+ AICWFDBG(LOGINFO, "change_if: %d to %d, %d, %d", vif->wdev.iftype, type, NL80211_IFTYPE_P2P_CLIENT, NL80211_IFTYPE_STATION);
+
+#ifdef CONFIG_COEX
+ if (type == NL80211_IFTYPE_AP || type == NL80211_IFTYPE_P2P_GO)
+ rwnx_send_coex_req(vif->rwnx_hw, 1, 0);
+ if (RWNX_VIF_TYPE(vif) == NL80211_IFTYPE_AP || RWNX_VIF_TYPE(vif) == NL80211_IFTYPE_P2P_GO)
+ rwnx_send_coex_req(vif->rwnx_hw, 0, 1);
+#endif
+#ifndef CONFIG_RWNX_MON_DATA
+ if ((type == NL80211_IFTYPE_MONITOR) &&
+ (RWNX_VIF_TYPE(vif) != NL80211_IFTYPE_MONITOR)) {
+ struct rwnx_vif *vif_el;
+ list_for_each_entry(vif_el, &rwnx_hw->vifs, list) {
+ // Check if data interface already exists
+ if ((vif_el != vif) &&
+ (RWNX_VIF_TYPE(vif) != NL80211_IFTYPE_MONITOR)) {
+ wiphy_err(rwnx_hw->wiphy,
+ "Monitor+Data interface support (MON_DATA) disabled\n");
+ return -EIO;
+ }
+ }
+ }
+#endif
+
+ // Reset to default case (i.e. not monitor)
+ dev->type = ARPHRD_ETHER;
+ dev->netdev_ops = &rwnx_netdev_ops;
+
+ switch (type) {
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_P2P_CLIENT:
+ vif->sta.ap = NULL;
+ vif->sta.tdls_sta = NULL;
+ vif->sta.external_auth = false;
+ break;
+ case NL80211_IFTYPE_MESH_POINT:
+ INIT_LIST_HEAD(&vif->ap.mpath_list);
+ INIT_LIST_HEAD(&vif->ap.proxy_list);
+ vif->ap.create_path = false;
+ vif->ap.generation = 0;
+ // no break
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_P2P_GO:
+ INIT_LIST_HEAD(&vif->ap.sta_list);
+ memset(&vif->ap.bcn, 0, sizeof(vif->ap.bcn));
+ break;
+ case NL80211_IFTYPE_AP_VLAN:
+ return -EPERM;
+ case NL80211_IFTYPE_MONITOR:
+ dev->type = ARPHRD_IEEE80211_RADIOTAP;
+ dev->netdev_ops = &rwnx_netdev_monitor_ops;
+ break;
+ default:
+ break;
+ }
+
+ vif->wdev.iftype = type;
+ if (params->use_4addr != -1)
+ vif->use_4addr = params->use_4addr;
+ if (type == NL80211_IFTYPE_P2P_CLIENT || type == NL80211_IFTYPE_P2P_GO){
+ p2p = true;
+ }
+ if (vif->up) {
+ /* Abort scan request on the vif */
+ if (vif->rwnx_hw->scan_request &&
+ vif->rwnx_hw->scan_request->wdev == &vif->wdev) {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0)
+ struct cfg80211_scan_info info = {
+ .aborted = true,
+ };
+ cfg80211_scan_done(vif->rwnx_hw->scan_request, &info);
+#else
+ cfg80211_scan_done(vif->rwnx_hw->scan_request, true);
+#endif
+ if ((ret = rwnx_send_scanu_cancel_req(vif->rwnx_hw, NULL))) {
+ AICWFDBG(LOGERROR, "scanu_cancel fail\n");
+ return ret;
+ }
+ vif->rwnx_hw->scan_request = NULL;
+ }
+ if ((ret = rwnx_send_remove_if(vif->rwnx_hw, vif->vif_index, false))) {
+ AICWFDBG(LOGERROR, "remove_if fail\n");
+ return ret;
+ }
+ vif->rwnx_hw->vif_table[vif->vif_index] = NULL;
+ AICWFDBG(LOGINFO, "change_if from %d \n", vif->vif_index);
+ if ((ret = rwnx_send_add_if(vif->rwnx_hw, vif->wdev.address,
+ RWNX_VIF_TYPE(vif), p2p, &add_if_cfm))) {
+ AICWFDBG(LOGERROR, "add if fail\n");
+ return ret;
+ }
+ if (add_if_cfm.status != 0) {
+ AICWFDBG(LOGERROR, "add if status fail\n");
+ return -EIO;
+ }
+
+ AICWFDBG(LOGINFO, "change_if to %d \n", add_if_cfm.inst_nbr);
+ /* Save the index retrieved from LMAC */
+ spin_lock_bh(&vif->rwnx_hw->cb_lock);
+ vif->vif_index = add_if_cfm.inst_nbr;
+ vif->rwnx_hw->vif_table[add_if_cfm.inst_nbr] = vif;
+ spin_unlock_bh(&vif->rwnx_hw->cb_lock);
+ }
+
+ if (type == NL80211_IFTYPE_MONITOR) {
+ vif->rwnx_hw->monitor_vif = vif->vif_index;
+ #if defined(CONFIG_RWNX_MON_XMIT)
+ rwnx_txq_unk_vif_init(vif);
+ #endif
+ #if defined(CONFIG_RWNX_MON_RXFILTER)
+ rwnx_send_set_filter(vif->rwnx_hw, (FIF_BCN_PRBRESP_PROMISC | FIF_OTHER_BSS | FIF_PSPOLL | FIF_PROBE_REQ));
+ #endif
+ } else {
+ vif->rwnx_hw->monitor_vif = RWNX_INVALID_VIF;
+ }
+
+ return 0;
+}
+
+static int rwnx_cfgp2p_start_p2p_device(struct wiphy *wiphy, struct wireless_dev *wdev)
+{
+ int ret = 0;
+
+ //do nothing
+ AICWFDBG(LOGINFO, "P2P interface started\n");
+
+ return ret;
+}
+
+static void rwnx_cfgp2p_stop_p2p_device(struct wiphy *wiphy, struct wireless_dev *wdev)
+{
+#if 0
+ int ret = 0;
+ struct bcm_cfg80211 *cfg = wiphy_priv(wiphy);
+
+ if (!cfg)
+ return;
+
+ CFGP2P_DBG(("Enter\n"));
+
+ ret = wl_cfg80211_scan_stop(cfg, wdev);
+ if (unlikely(ret < 0)) {
+ CFGP2P_ERR(("P2P scan stop failed, ret=%d\n", ret));
+ }
+
+ if (!cfg->p2p)
+ return;
+
+ /* Cancel any on-going listen */
+ wl_cfgp2p_cancel_listen(cfg, bcmcfg_to_prmry_ndev(cfg), wdev, TRUE);
+
+ ret = wl_cfgp2p_disable_discovery(cfg);
+ if (unlikely(ret < 0)) {
+ CFGP2P_ERR(("P2P disable discovery failed, ret=%d\n", ret));
+ }
+
+ p2p_on(cfg) = false;
+#endif
+ int ret = 0;
+ struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy);
+ struct rwnx_vif *rwnx_vif = container_of(wdev, struct rwnx_vif, wdev);
+ /* Abort scan request on the vif */
+ if (rwnx_hw->scan_request &&
+ rwnx_hw->scan_request->wdev == &rwnx_vif->wdev) {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0)
+ struct cfg80211_scan_info info = {
+ .aborted = true,
+ };
+
+ cfg80211_scan_done(rwnx_hw->scan_request, &info);
+#else
+ cfg80211_scan_done(rwnx_hw->scan_request, true);
+#endif
+ rwnx_hw->scan_request = NULL;
+ ret = rwnx_send_scanu_cancel_req(rwnx_hw, NULL);
+ if (ret){
+ AICWFDBG(LOGERROR, "scanu_cancel fail\n");
+ }
+ }
+
+ if (rwnx_vif == rwnx_hw->p2p_dev_vif) {
+ rwnx_hw->is_p2p_alive = 0;
+ if (timer_pending(&rwnx_hw->p2p_alive_timer)) {
+ del_timer_sync(&rwnx_hw->p2p_alive_timer);
+ }
+ if (rwnx_vif->up) {
+ rwnx_send_remove_if(rwnx_hw, rwnx_vif->vif_index, true);
+ /* Ensure that we won't process disconnect ind */
+ spin_lock_bh(&rwnx_hw->cb_lock);
+ rwnx_vif->up = false;
+ rwnx_hw->vif_table[rwnx_vif->vif_index] = NULL;
+ rwnx_hw->vif_started--;
+ spin_unlock_bh(&rwnx_hw->cb_lock);
+ }
+ }
+
+ AICWFDBG(LOGINFO, "Exit. P2P interface stopped\n");
+
+ return;
+}
+
+int rwnx_send_check_p2p(struct cfg80211_scan_request *param){
+ int index = (u8)min_t(int, SCAN_SSID_MAX, param->n_ssids);
+ int i = 0;
+
+ for(i = 0;i < index;i++){
+ if (!memcmp("DIRECT-", param->ssids[i].ssid,
+ (sizeof("DIRECT-") - 1))) {
+ //printk("AIDEN rwnx_send_check_p2p!!\r\n");
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/**
+ * @scan: Request to do a scan. If returning zero, the scan request is given
+ * the driver, and will be valid until passed to cfg80211_scan_done().
+ * For scan results, call cfg80211_inform_bss(); you can call this outside
+ * the scan/scan_done bracket too.
+ */
+static int rwnx_cfg80211_scan(struct wiphy *wiphy,
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0)
+ struct net_device *dev,
+ #endif
+ struct cfg80211_scan_request *request)
+{
+ struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)
+ struct rwnx_vif *rwnx_vif = container_of(request->wdev, struct rwnx_vif,
+ wdev);
+#else
+ struct rwnx_vif* rwnx_vif = netdev_priv(request->dev);
+#endif
+ int error;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ if(testmode){
+ AICWFDBG(LOGERROR, "%s in testmode return busy\r\n", __func__);
+ return -EBUSY;
+ }
+
+ if((int)atomic_read(&rwnx_vif->drv_conn_state) == (int)RWNX_DRV_STATUS_CONNECTING){
+ AICWFDBG(LOGERROR, "%s in connecting return busy\r\n", __func__);
+ return -EBUSY;
+ }
+
+#ifndef CONFIG_STA_SCAN_WHEN_P2P_WORKING
+ if (p2p_working && RWNX_VIF_TYPE(rwnx_vif) != NL80211_IFTYPE_P2P_CLIENT &&
+ !rwnx_send_check_p2p(request)) {
+ AICWFDBG(LOGINFO, "p2p is working, scan abort\n");
+ return -EBUSY;
+ }
+#endif
+
+ if (scanning) {
+ AICWFDBG(LOGERROR, "%s is scanning, abort\n", __func__);
+ #if 0//AIDEN test
+ error = rwnx_send_scanu_cancel_req(rwnx_hw, NULL);
+ if (error)
+ return error;
+ msleep(150);
+ #endif
+ return -EBUSY;
+ }
+
+ if((RWNX_VIF_TYPE(rwnx_vif) == NL80211_IFTYPE_STATION ||RWNX_VIF_TYPE(rwnx_vif) == NL80211_IFTYPE_P2P_CLIENT) && rwnx_vif->sta.external_auth){
+ AICWFDBG(LOGINFO, "scan about: external auth\r\n");
+ return -EBUSY;
+ }
+
+ rwnx_hw->scan_request = request;
+ if ((error = rwnx_send_scanu_req(rwnx_hw, rwnx_vif, request)))
+ return error;
+
+ return 0;
+}
+
+bool key_flag = false;
+/**
+ * @add_key: add a key with the given parameters. @mac_addr will be %NULL
+ * when adding a group key.
+ */
+static int rwnx_cfg80211_add_key(struct wiphy *wiphy, struct net_device *netdev,
+#if (LINUX_VERSION_CODE >= HIGH_KERNEL_VERSION)
+ int link_id,
+#endif
+ u8 key_index, bool pairwise, const u8 *mac_addr,
+ struct key_params *params)
+{
+ struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy);
+ struct rwnx_vif *vif = netdev_priv(netdev);
+ int i, error = 0;
+ struct mm_key_add_cfm key_add_cfm;
+ u8_l cipher = 0;
+ struct rwnx_sta *sta = NULL;
+ struct rwnx_key *rwnx_key;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ if (mac_addr) {
+ sta = rwnx_get_sta(rwnx_hw, mac_addr);
+ if (!sta)
+ return -EINVAL;
+ rwnx_key = &sta->key;
+ if (vif->wdev.iftype == NL80211_IFTYPE_STATION || vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT)
+ vif->sta.paired_cipher_type = params->cipher;
+ }
+ else {
+ rwnx_key = &vif->key[key_index];
+ vif->key_has_add = 1;
+ if (vif->wdev.iftype == NL80211_IFTYPE_STATION || vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT)
+ vif->sta.group_cipher_type = params->cipher;
+ }
+
+ /* Retrieve the cipher suite selector */
+ switch (params->cipher) {
+ case WLAN_CIPHER_SUITE_WEP40:
+ cipher = MAC_CIPHER_WEP40;
+ break;
+ case WLAN_CIPHER_SUITE_WEP104:
+ cipher = MAC_CIPHER_WEP104;
+ break;
+ case WLAN_CIPHER_SUITE_TKIP:
+ cipher = MAC_CIPHER_TKIP;
+ break;
+ case WLAN_CIPHER_SUITE_CCMP:
+ cipher = MAC_CIPHER_CCMP;
+ break;
+ case WLAN_CIPHER_SUITE_AES_CMAC:
+ cipher = MAC_CIPHER_BIP_CMAC_128;
+ break;
+ case WLAN_CIPHER_SUITE_SMS4:
+ {
+ // Need to reverse key order
+ u8 tmp, *key = (u8 *)params->key;
+ cipher = MAC_CIPHER_WPI_SMS4;
+ for (i = 0; i < WPI_SUBKEY_LEN/2; i++) {
+ tmp = key[i];
+ key[i] = key[WPI_SUBKEY_LEN - 1 - i];
+ key[WPI_SUBKEY_LEN - 1 - i] = tmp;
+ }
+ for (i = 0; i < WPI_SUBKEY_LEN/2; i++) {
+ tmp = key[i + WPI_SUBKEY_LEN];
+ key[i + WPI_SUBKEY_LEN] = key[WPI_KEY_LEN - 1 - i];
+ key[WPI_KEY_LEN - 1 - i] = tmp;
+ }
+ break;
+ }
+ default:
+ return -EINVAL;
+ }
+
+ key_flag = false;
+ if ((error = rwnx_send_key_add(rwnx_hw, vif->vif_index,
+ (sta ? sta->sta_idx : 0xFF), pairwise,
+ (u8 *)params->key, params->key_len,
+ key_index, cipher, &key_add_cfm)))
+ return error;
+
+ if (key_add_cfm.status != 0) {
+ RWNX_PRINT_CFM_ERR(key_add);
+ return -EIO;
+ }
+
+ /* Save the index retrieved from LMAC */
+ rwnx_key->hw_idx = key_add_cfm.hw_key_idx;
+
+ return 0;
+}
+
+/**
+ * @get_key: get information about the key with the given parameters.
+ * @mac_addr will be %NULL when requesting information for a group
+ * key. All pointers given to the @callback function need not be valid
+ * after it returns. This function should return an error if it is
+ * not possible to retrieve the key, -ENOENT if it doesn't exist.
+ *
+ */
+static int rwnx_cfg80211_get_key(struct wiphy *wiphy, struct net_device *netdev,
+#if (LINUX_VERSION_CODE >= HIGH_KERNEL_VERSION)
+ int link_id,
+#endif
+
+ u8 key_index, bool pairwise, const u8 *mac_addr,
+ void *cookie,
+ void (*callback)(void *cookie, struct key_params*))
+{
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ return -1;
+}
+
+
+/**
+ * @del_key: remove a key given the @mac_addr (%NULL for a group key)
+ * and @key_index, return -ENOENT if the key doesn't exist.
+ */
+static int rwnx_cfg80211_del_key(struct wiphy *wiphy, struct net_device *netdev,
+#if (LINUX_VERSION_CODE >= HIGH_KERNEL_VERSION)
+ int link_id,
+#endif
+
+ u8 key_index, bool pairwise, const u8 *mac_addr)
+{
+ struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy);
+ struct rwnx_vif *vif = netdev_priv(netdev);
+ int error;
+ struct rwnx_sta *sta = NULL;
+ struct rwnx_key *rwnx_key;
+ if (!key_flag && vif->wdev.iftype == NL80211_IFTYPE_STATION)
+ return 0;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+ if (mac_addr) {
+ sta = rwnx_get_sta(rwnx_hw, mac_addr);
+ if (!sta)
+ return -EINVAL;
+ rwnx_key = &sta->key;
+ if (vif->wdev.iftype == NL80211_IFTYPE_STATION || vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT)
+ vif->sta.paired_cipher_type = 0xff;
+ }
+ else {
+ rwnx_key = &vif->key[key_index];
+ vif->key_has_add = 0;
+ if (vif->wdev.iftype == NL80211_IFTYPE_STATION || vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT)
+ vif->sta.group_cipher_type = 0xff;
+ }
+
+ error = rwnx_send_key_del(rwnx_hw, rwnx_key->hw_idx);
+
+ rwnx_key->hw_idx = 0;
+ return error;
+}
+
+/**
+ * @set_default_key: set the default key on an interface
+ */
+static int rwnx_cfg80211_set_default_key(struct wiphy *wiphy,
+ struct net_device *netdev,
+#if (LINUX_VERSION_CODE >= HIGH_KERNEL_VERSION)
+ int link_id,
+#endif
+ u8 key_index, bool unicast, bool multicast)
+{
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ return 0;
+}
+
+/**
+ * @set_default_mgmt_key: set the default management frame key on an interface
+ */
+static int rwnx_cfg80211_set_default_mgmt_key(struct wiphy *wiphy,
+ struct net_device *netdev,
+#if (LINUX_VERSION_CODE >= HIGH_KERNEL_VERSION)
+ int link_id,
+#endif
+ u8 key_index)
+{
+ return 0;
+}
+
+/**
+ * @connect: Connect to the ESS with the specified parameters. When connected,
+ * call cfg80211_connect_result() with status code %WLAN_STATUS_SUCCESS.
+ * If the connection fails for some reason, call cfg80211_connect_result()
+ * with the status from the AP.
+ * (invoked with the wireless_dev mutex held)
+ */
+
+static int rwnx_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
+ struct cfg80211_connect_params *sme)
+{
+ struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy);
+ struct rwnx_vif *rwnx_vif = netdev_priv(dev);
+ struct sm_connect_cfm sm_connect_cfm;
+ int error = 0;
+ int is_wep = ((sme->crypto.cipher_group == WLAN_CIPHER_SUITE_WEP40) ||
+ (sme->crypto.cipher_group == WLAN_CIPHER_SUITE_WEP104) ||
+ (sme->crypto.ciphers_pairwise[0] == WLAN_CIPHER_SUITE_WEP40) ||
+ (sme->crypto.ciphers_pairwise[0] == WLAN_CIPHER_SUITE_WEP104));
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+#if 1
+#if 0
+ if((int)atomic_read(&rwnx_vif->drv_conn_state) == (int)RWNX_DRV_STATUS_CONNECTED){
+ AICWFDBG(LOGERROR, "%s driver was connected return it \r\n", __func__);
+ return -EALREADY;
+ }
+#endif
+ if((int)atomic_read(&rwnx_vif->drv_conn_state) == (int)RWNX_DRV_STATUS_DISCONNECTING){
+ AICWFDBG(LOGERROR, "%s driver is disconnecting return it \r\n", __func__);
+ return -EALREADY;
+ }
+#endif
+
+ atomic_set(&rwnx_vif->drv_conn_state, (int)RWNX_DRV_STATUS_CONNECTING);
+
+ if(is_wep) {
+ if(sme->auth_type == NL80211_AUTHTYPE_AUTOMATIC) {
+ if(rwnx_vif->wep_enabled && rwnx_vif->wep_auth_err) {
+ if(rwnx_vif->last_auth_type == NL80211_AUTHTYPE_SHARED_KEY)
+ sme->auth_type = NL80211_AUTHTYPE_OPEN_SYSTEM;
+ else
+ sme->auth_type = NL80211_AUTHTYPE_SHARED_KEY;
+ } else {
+ if((rwnx_vif->wep_enabled && !rwnx_vif->wep_auth_err))
+ sme->auth_type = rwnx_vif->last_auth_type;
+ else
+ sme->auth_type = NL80211_AUTHTYPE_SHARED_KEY;
+ }
+ AICWFDBG(LOGINFO, "auto: use sme->auth_type = %d\r\n", sme->auth_type);
+ } else {
+ if (rwnx_vif->wep_enabled && rwnx_vif->wep_auth_err && (sme->auth_type == rwnx_vif->last_auth_type)) {
+ if(sme->auth_type == NL80211_AUTHTYPE_SHARED_KEY) {
+ sme->auth_type = NL80211_AUTHTYPE_OPEN_SYSTEM;
+ AICWFDBG(LOGINFO, "start connect, auth_type changed, shared --> open\n");
+ } else if(sme->auth_type == NL80211_AUTHTYPE_OPEN_SYSTEM) {
+ sme->auth_type = NL80211_AUTHTYPE_SHARED_KEY;
+ AICWFDBG(LOGINFO, "start connect, auth_type changed, open --> shared\n");
+ }
+ }
+ }
+ }
+
+ /* For SHARED-KEY authentication, must install key first */
+ if (sme->auth_type == NL80211_AUTHTYPE_SHARED_KEY && sme->key)
+ {
+ struct key_params key_params;
+ key_params.key = (u8*)sme->key;
+ key_params.seq = NULL;
+ key_params.key_len = sme->key_len;
+ key_params.seq_len = 0;
+ key_params.cipher = sme->crypto.cipher_group;
+ rwnx_cfg80211_add_key(wiphy, dev,
+#if (LINUX_VERSION_CODE >= HIGH_KERNEL_VERSION)
+ 0,
+#endif
+ sme->key_idx, false, NULL, &key_params);
+ }
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0) || defined(CONFIG_WPA3_FOR_OLD_KERNEL)
+ else if ((sme->auth_type == NL80211_AUTHTYPE_SAE) &&
+ !(sme->flags & CONNECT_REQ_EXTERNAL_AUTH_SUPPORT)) {
+ netdev_err(dev, "Doesn't support SAE without external authentication\n");
+ return -EINVAL;
+ }
+#endif
+
+ if (rwnx_vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT) {
+ rwnx_hw->is_p2p_connected = 1;
+ }
+
+ if (rwnx_vif->wdev.iftype == NL80211_IFTYPE_STATION || rwnx_vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT) {
+ rwnx_vif->sta.paired_cipher_type = 0xff;
+ rwnx_vif->sta.group_cipher_type = 0xff;
+ }
+
+
+ /* Forward the information to the LMAC */
+ if ((error = rwnx_send_sm_connect_req(rwnx_hw, rwnx_vif, sme, &sm_connect_cfm)))
+ return error;
+
+ // Check the status
+ switch (sm_connect_cfm.status)
+ {
+ case CO_OK:
+ error = 0;
+ break;
+ case CO_BUSY:
+ error = -EINPROGRESS;
+ break;
+ case CO_OP_IN_PROGRESS:
+ error = -EALREADY;
+ break;
+ default:
+ error = -EIO;
+ break;
+ }
+
+ return error;
+}
+
+/**
+ * @disconnect: Disconnect from the BSS/ESS.
+ * (invoked with the wireless_dev mutex held)
+ */
+static int rwnx_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *dev,
+ u16 reason_code)
+{
+ struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy);
+ struct rwnx_vif *rwnx_vif = netdev_priv(dev);
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+ AICWFDBG(LOGINFO, "%s drv_vif_index:%d disconnect reason:%d \r\n",
+ __func__, rwnx_vif->drv_vif_index, reason_code);
+
+#if 0
+ while(atomic_read(&rwnx_vif->drv_conn_state) == RWNX_DRV_STATUS_CONNECTING){
+ AICWFDBG(LOGERROR, "%s driver connecting waiting 100ms \r\n", __func__);
+ msleep(100);
+ retry--;
+ if(retry == 0){
+ break;
+ }
+ if(rwnx_hw->usbdev->state == USB_DOWN_ST){
+ break;
+ }
+ }
+ if(atomic_read(&rwnx_vif->drv_conn_state) == RWNX_DRV_STATUS_CONNECTED){
+
+ atomic_set(&rwnx_vif->drv_conn_state, RWNX_DRV_STATUS_DISCONNECTING);
+ }
+#endif
+ if(atomic_read(&rwnx_vif->drv_conn_state) == RWNX_DRV_STATUS_CONNECTING){
+ AICWFDBG(LOGINFO, "%s call cfg80211_connect_result reason:%d \r\n",
+ __func__, reason_code);
+ msleep(500);
+ }
+
+ if(atomic_read(&rwnx_vif->drv_conn_state) == RWNX_DRV_STATUS_CONNECTED){
+ atomic_set(&rwnx_vif->drv_conn_state, RWNX_DRV_STATUS_DISCONNECTING);
+
+ #ifdef CONFIG_USE_WIRELESS_EXT
+ memset(rwnx_hw->wext_essid, 0, 32);
+ #endif
+ key_flag = true;
+ return(rwnx_send_sm_disconnect_req(rwnx_hw, rwnx_vif, reason_code));
+ }else{
+ cfg80211_connect_result(dev, NULL, NULL, 0, NULL, 0,
+ reason_code?reason_code:WLAN_STATUS_UNSPECIFIED_FAILURE, GFP_ATOMIC);
+ atomic_set(&rwnx_vif->drv_conn_state, RWNX_DRV_STATUS_DISCONNECTED);
+ return 0;
+ }
+
+}
+
+#ifdef CONFIG_SCHED_SCAN
+
+static int rwnx_cfg80211_sched_scan_stop(struct wiphy *wiphy,
+ struct net_device *ndev
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0)
+ ,u64 reqid)
+#else
+ )
+#endif
+{
+
+ struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy);
+ //struct rwnx_vif *rwnx_vif = netdev_priv(dev);
+ AICWFDBG(LOGINFO, "%s enter wiphy:%p\r\n", __func__, wiphy);
+
+ if(rwnx_hw->scan_request){
+ AICWFDBG(LOGINFO, "%s rwnx_send_scanu_cancel_req\r\n", __func__);
+ return rwnx_send_scanu_cancel_req(rwnx_hw, NULL);
+ }else{
+ return 0;
+ }
+}
+
+
+static int rwnx_cfg80211_sched_scan_start(struct wiphy *wiphy,
+ struct net_device *dev,
+ struct cfg80211_sched_scan_request *request)
+
+{
+ struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy);
+ struct rwnx_vif *rwnx_vif = netdev_priv(dev);
+ struct cfg80211_scan_request *scan_request = NULL;
+
+ int ret = 0;
+ int index = 0;
+
+ AICWFDBG(LOGINFO, "%s enter wiphy:%p\r\n", __func__, wiphy);
+
+ if(rwnx_hw->is_sched_scan || scanning){
+ AICWFDBG(LOGERROR, "%s is_sched_scanning and scanning, busy", __func__);
+ return -EBUSY;
+ }
+
+ scan_request = (struct cfg80211_scan_request *)kmalloc(sizeof(struct cfg80211_scan_request), GFP_KERNEL);
+
+ scan_request->ssids = request->ssids;
+ scan_request->n_channels = request->n_channels;
+ scan_request->n_ssids = request->n_match_sets;
+ scan_request->no_cck = false;
+ scan_request->ie = request->ie;
+ scan_request->ie_len = request->ie_len;
+ scan_request->flags = request->flags;
+ scan_request->wiphy = wiphy;
+ scan_request->scan_start = request->scan_start;
+ memcpy(scan_request->mac_addr, request->mac_addr, ETH_ALEN);
+ memcpy(scan_request->mac_addr_mask, request->mac_addr_mask, ETH_ALEN);
+ rwnx_hw->sched_scan_req = request;
+ scan_request->wdev = &rwnx_vif->wdev;
+ AICWFDBG(LOGDEBUG, "%s scan_request->n_channels:%d \r\n", __func__, scan_request->n_channels);
+ AICWFDBG(LOGDEBUG, "%s scan_request->n_ssids:%d \r\n", __func__, scan_request->n_ssids);
+
+ for(index = 0; index < scan_request->n_ssids; index++){
+ memset(scan_request->ssids[index].ssid, 0, IEEE80211_MAX_SSID_LEN);
+
+ memcpy(scan_request->ssids[index].ssid,
+ request->match_sets[index].ssid.ssid,
+ IEEE80211_MAX_SSID_LEN);
+
+ scan_request->ssids[index].ssid_len = request->match_sets[index].ssid.ssid_len;
+
+ AICWFDBG(LOGDEBUG, "%s request ssid:%s len:%d \r\n", __func__,
+ scan_request->ssids[index].ssid, scan_request->ssids[index].ssid_len);
+ }
+
+ for(index = 0;index < scan_request->n_channels; index++){
+ scan_request->channels[index] = request->channels[index];
+
+ AICWFDBG(LOGDEBUG, "%s scan_request->channels[%d]:%d \r\n", __func__, index,
+ scan_request->channels[index]->center_freq);
+
+ if(scan_request->channels[index] == NULL){
+ AICWFDBG(LOGERROR, "%s ERROR!!! channels is NULL", __func__);
+ continue;
+ }
+ }
+
+ rwnx_hw->is_sched_scan = true;
+
+ if(scanning){
+ AICWFDBG(LOGERROR, "%s scanning, about it", __func__);
+ kfree(scan_request);
+ return -EBUSY;
+ }else{
+ ret = rwnx_cfg80211_scan(wiphy, scan_request);
+ }
+
+ return ret;
+}
+#endif //CONFIG_SCHED_SCAN
+
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0) || defined(CONFIG_WPA3_FOR_OLD_KERNEL)
+/**
+ * @external_auth: indicates result of offloaded authentication processing from
+ * user space
+ */
+static int rwnx_cfg80211_external_auth(struct wiphy *wiphy, struct net_device *dev,
+ struct cfg80211_external_auth_params *params)
+{
+ struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy);
+ struct rwnx_vif *rwnx_vif = netdev_priv(dev);
+
+ //printk("%s Enter \r\n");
+ if (!rwnx_vif->sta.external_auth)
+ return -EINVAL;
+
+ rwnx_external_auth_disable(rwnx_vif);
+ return rwnx_send_sm_external_auth_required_rsp(rwnx_hw, rwnx_vif,
+ params->status);
+}
+#endif
+
+/**
+ * @add_station: Add a new station.
+ */
+static int rwnx_cfg80211_add_station(struct wiphy *wiphy,
+ struct net_device *dev,
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 16, 0))
+ u8 *mac,
+#else
+ const u8 *mac,
+#endif
+ struct station_parameters *params)
+{
+ struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy);
+ struct rwnx_vif *rwnx_vif = netdev_priv(dev);
+ struct me_sta_add_cfm me_sta_add_cfm;
+ int error = 0;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ WARN_ON(RWNX_VIF_TYPE(rwnx_vif) == NL80211_IFTYPE_AP_VLAN);
+
+ /* Do not add TDLS station */
+ if (params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))
+ return 0;
+
+ /* Indicate we are in a STA addition process - This will allow handling
+ * potential PS mode change indications correctly
+ */
+ rwnx_hw->adding_sta = true;
+
+ /* Forward the information to the LMAC */
+ if ((error = rwnx_send_me_sta_add(rwnx_hw, params, mac, rwnx_vif->vif_index,
+ &me_sta_add_cfm)))
+ return error;
+
+ // Check the status
+ switch (me_sta_add_cfm.status)
+ {
+ case CO_OK:
+ {
+ struct rwnx_sta *sta = &rwnx_hw->sta_table[me_sta_add_cfm.sta_idx];
+ int tid;
+ sta->aid = params->aid;
+
+ sta->sta_idx = me_sta_add_cfm.sta_idx;
+ sta->ch_idx = rwnx_vif->ch_index;
+ sta->vif_idx = rwnx_vif->vif_index;
+ sta->vlan_idx = sta->vif_idx;
+ sta->qos = (params->sta_flags_set & BIT(NL80211_STA_FLAG_WME)) != 0;
+#if LINUX_VERSION_CODE >= HIGH_KERNEL_VERSION
+ sta->ht = params->link_sta_params.ht_capa ? 1 : 0;
+ sta->vht = params->link_sta_params.vht_capa ? 1 : 0;
+#else
+ sta->ht = params->ht_capa ? 1 : 0;
+ sta->vht = params->vht_capa ? 1 : 0;
+#endif
+
+ sta->acm = 0;
+ sta->key.hw_idx = 0;
+
+ if (params->local_pm != NL80211_MESH_POWER_UNKNOWN)
+ sta->mesh_pm = params->local_pm;
+ else
+ sta->mesh_pm = rwnx_vif->ap.next_mesh_pm;
+ rwnx_update_mesh_power_mode(rwnx_vif);
+
+ for (tid = 0; tid < NX_NB_TXQ_PER_STA; tid++) {
+ int uapsd_bit = rwnx_hwq2uapsd[rwnx_tid2hwq[tid]];
+ if (params->uapsd_queues & uapsd_bit)
+ sta->uapsd_tids |= 1 << tid;
+ else
+ sta->uapsd_tids &= ~(1 << tid);
+ }
+ memcpy(sta->mac_addr, mac, ETH_ALEN);
+#ifdef CONFIG_DEBUG_FS
+ rwnx_dbgfs_register_rc_stat(rwnx_hw, sta);
+#endif
+ /* Ensure that we won't process PS change or channel switch ind*/
+ spin_lock_bh(&rwnx_hw->cb_lock);
+ rwnx_txq_sta_init(rwnx_hw, sta, rwnx_txq_vif_get_status(rwnx_vif));
+ list_add_tail(&sta->list, &rwnx_vif->ap.sta_list);
+ sta->valid = true;
+ rwnx_ps_bh_enable(rwnx_hw, sta, sta->ps.active || me_sta_add_cfm.pm_state);
+ spin_unlock_bh(&rwnx_hw->cb_lock);
+
+ error = 0;
+ if(atomic_read(&rwnx_hw->sta_flowctrl[sta->sta_idx].tx_pending_cnt) > 0)
+ AICWFDBG(LOGDEBUG, "sta idx %d fc error %d.\n", sta->sta_idx, atomic_read(&rwnx_hw->sta_flowctrl[sta->sta_idx].tx_pending_cnt));
+
+ if (rwnx_vif->wdev.iftype == NL80211_IFTYPE_AP || rwnx_vif->wdev.iftype == NL80211_IFTYPE_P2P_GO) {
+ struct station_info sinfo;
+ memset(&sinfo, 0, sizeof(struct station_info));
+ sinfo.assoc_req_ies = NULL;
+ sinfo.assoc_req_ies_len = 0;
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0)
+ sinfo.filled |= STATION_INFO_ASSOC_REQ_IES;
+ #endif
+ cfg80211_new_sta(rwnx_vif->ndev, sta->mac_addr, &sinfo, GFP_KERNEL);
+ }
+#ifdef CONFIG_RWNX_BFMER
+ if (rwnx_hw->mod_params->bfmer)
+ rwnx_send_bfmer_enable(rwnx_hw, sta, params->vht_capa);
+
+ rwnx_mu_group_sta_init(sta, params->vht_capa);
+#endif /* CONFIG_RWNX_BFMER */
+
+ #define PRINT_STA_FLAG(f) \
+ (params->sta_flags_set & BIT(NL80211_STA_FLAG_##f) ? "["#f"]" : "")
+
+ netdev_info(dev, "Add sta %d (%pM) flags=%s%s%s%s%s%s%s",
+ sta->sta_idx, mac,
+ PRINT_STA_FLAG(AUTHORIZED),
+ PRINT_STA_FLAG(SHORT_PREAMBLE),
+ PRINT_STA_FLAG(WME),
+ PRINT_STA_FLAG(MFP),
+ PRINT_STA_FLAG(AUTHENTICATED),
+ PRINT_STA_FLAG(TDLS_PEER),
+ PRINT_STA_FLAG(ASSOCIATED));
+ #undef PRINT_STA_FLAG
+ break;
+ }
+ default:
+ error = -EBUSY;
+ break;
+ }
+
+ rwnx_hw->adding_sta = false;
+
+ return error;
+}
+
+/**
+ * @del_station: Remove a station
+ */
+static int rwnx_cfg80211_del_station_compat(struct wiphy *wiphy,
+ struct net_device *dev,
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 16, 0))
+ u8 *mac
+#elif (LINUX_VERSION_CODE < KERNEL_VERSION(3, 19, 0))
+ const u8 *mac
+#else
+ struct station_del_parameters *params
+#endif
+)
+{
+ struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy);
+ struct rwnx_vif *rwnx_vif = netdev_priv(dev);
+ struct rwnx_sta *cur, *tmp;
+ int error = 0, found = 0;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)
+ const u8 *mac = NULL;
+#endif
+#ifdef AICWF_RX_REORDER
+ struct reord_ctrl_info *reord_info, *reord_tmp;
+ u8 *macaddr;
+ struct aicwf_rx_priv *rx_priv;
+#endif
+
+ //RWNX_DBG(RWNX_FN_ENTRY_STR);
+ AICWFDBG(LOGDEBUG ,"%s: %pM\n", __func__, mac);
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)
+ if (params)
+ mac = params->mac;
+#endif
+
+ do {
+ spin_lock_bh(&rwnx_hw->cb_lock);
+ if(list_empty(&rwnx_vif->ap.sta_list)) {
+ spin_unlock_bh(&rwnx_hw->cb_lock);
+ break;
+ }
+
+ list_for_each_entry_safe(cur, tmp, &rwnx_vif->ap.sta_list, list) {
+ if ((!mac) || (!memcmp(cur->mac_addr, mac, ETH_ALEN))) {
+ found = 1;
+ break;
+ }
+ }
+
+ if(found) {
+ cur->ps.active = false;
+ cur->valid = false;
+ list_del(&cur->list);
+ }
+ spin_unlock_bh(&rwnx_hw->cb_lock);
+
+ if(found) {
+ netdev_info(dev, "Del sta %d (%pM)", cur->sta_idx, cur->mac_addr);
+ if (cur->vif_idx != cur->vlan_idx) {
+ struct rwnx_vif *vlan_vif;
+ vlan_vif = rwnx_hw->vif_table[cur->vlan_idx];
+ if (vlan_vif->up) {
+ if ((RWNX_VIF_TYPE(vlan_vif) == NL80211_IFTYPE_AP_VLAN) &&
+ (vlan_vif->use_4addr)) {
+ vlan_vif->ap_vlan.sta_4a = NULL;
+ } else {
+ WARN(1, "Deleting sta belonging to VLAN other than AP_VLAN 4A");
+ }
+ }
+ }
+ if (rwnx_vif->wdev.iftype == NL80211_IFTYPE_AP || rwnx_vif->wdev.iftype == NL80211_IFTYPE_P2P_GO) {
+ cfg80211_del_sta(rwnx_vif->ndev, cur->mac_addr, GFP_KERNEL);
+ }
+
+#ifdef AICWF_RX_REORDER
+#ifdef AICWF_SDIO_SUPPORT
+ rx_priv = rwnx_hw->sdiodev->rx_priv;
+#else
+ rx_priv = rwnx_hw->usbdev->rx_priv;
+#endif
+ if ((rwnx_vif->wdev.iftype == NL80211_IFTYPE_STATION) || (rwnx_vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT)) {
+ BUG();//should be other function
+ }
+ else if ((rwnx_vif->wdev.iftype == NL80211_IFTYPE_AP) || (rwnx_vif->wdev.iftype == NL80211_IFTYPE_P2P_GO)){
+ macaddr = cur->mac_addr;
+ AICWFDBG(LOGINFO, "deinit:macaddr:%x,%x,%x,%x,%x,%x\r\n", macaddr[0],macaddr[1],macaddr[2], \
+ macaddr[3],macaddr[4],macaddr[5]);
+ list_for_each_entry_safe(reord_info, reord_tmp,
+ &rx_priv->stas_reord_list, list) {
+ AICWFDBG(LOGINFO, "reord_mac:%x,%x,%x,%x,%x,%x\r\n", reord_info->mac_addr[0],reord_info->mac_addr[1],reord_info->mac_addr[2], \
+ reord_info->mac_addr[3],reord_info->mac_addr[4],reord_info->mac_addr[5]);
+ if (!memcmp(reord_info->mac_addr, macaddr, 6)) {
+ reord_deinit_sta(rx_priv, reord_info);
+ break;
+ }
+ }
+ }
+#endif
+
+ rwnx_txq_sta_deinit(rwnx_hw, cur);
+ error = rwnx_send_me_sta_del(rwnx_hw, cur->sta_idx, false);
+ if ((error != 0) && (error != -EPIPE))
+ return error;
+
+#ifdef CONFIG_RWNX_BFMER
+ // Disable Beamformer if supported
+ rwnx_bfmer_report_del(rwnx_hw, cur);
+ rwnx_mu_group_sta_del(rwnx_hw, cur);
+#endif /* CONFIG_RWNX_BFMER */
+
+#ifdef CONFIG_DEBUG_FS
+ rwnx_dbgfs_unregister_rc_stat(rwnx_hw, cur);
+#endif
+ }
+
+ if(mac)
+ break;
+ } while (1);
+
+ rwnx_update_mesh_power_mode(rwnx_vif);
+
+ if(!found && mac != NULL)
+ return -ENOENT;
+ else
+ return 0;
+}
+
+
+void apm_staloss_work_process(struct work_struct *work)
+{
+ struct rwnx_hw *rwnx_hw = container_of(work, struct rwnx_hw, apmStalossWork);
+ struct rwnx_sta *cur, *tmp;
+ int error = 0;
+
+#ifdef AICWF_RX_REORDER
+ struct reord_ctrl_info *reord_info, *reord_tmp;
+ u8 *macaddr;
+ struct aicwf_rx_priv *rx_priv;
+#endif
+ struct rwnx_vif *rwnx_vif;
+ bool_l found = false;
+ const u8 *mac = rwnx_hw->sta_mac_addr;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ // Look for VIF entry
+ list_for_each_entry(rwnx_vif, &rwnx_hw->vifs, list) {
+ if (rwnx_vif->vif_index == rwnx_hw->apm_vif_idx) {
+ found = true;
+ break;
+ }
+ }
+
+ AICWFDBG(LOGINFO, "apm vif idx=%d, found=%d, mac addr=%pM\n", rwnx_hw->apm_vif_idx, found, mac);
+ if (!found || !rwnx_vif || (RWNX_VIF_TYPE(rwnx_vif) != NL80211_IFTYPE_AP && RWNX_VIF_TYPE(rwnx_vif) != NL80211_IFTYPE_P2P_GO))
+ {
+ return;
+ }
+
+ found = false;
+ spin_lock_bh(&rwnx_hw->cb_lock);
+ list_for_each_entry_safe(cur, tmp, &rwnx_vif->ap.sta_list, list) {
+ if ((mac) && (!memcmp(cur->mac_addr, mac, ETH_ALEN))) {
+ found = true;
+ break;
+ }
+ }
+ if(found) {
+ cur->ps.active = false;
+ cur->valid = false;
+ list_del(&cur->list);
+ }
+ spin_unlock_bh(&rwnx_hw->cb_lock);
+
+ if(found) {
+ netdev_info(rwnx_vif->ndev, "Del sta %d (%pM)", cur->sta_idx, cur->mac_addr);
+ if (cur->vif_idx != cur->vlan_idx) {
+ struct rwnx_vif *vlan_vif;
+ vlan_vif = rwnx_hw->vif_table[cur->vlan_idx];
+ if (vlan_vif->up) {
+ if ((RWNX_VIF_TYPE(vlan_vif) == NL80211_IFTYPE_AP_VLAN) &&
+ (vlan_vif->use_4addr)) {
+ vlan_vif->ap_vlan.sta_4a = NULL;
+ } else {
+ WARN(1, "Deleting sta belonging to VLAN other than AP_VLAN 4A");
+ }
+ }
+ }
+ // if (rwnx_vif->wdev.iftype == NL80211_IFTYPE_AP || rwnx_vif->wdev.iftype == NL80211_IFTYPE_P2P_GO) {
+ // cfg80211_del_sta(rwnx_vif->ndev, cur->mac_addr, GFP_KERNEL);
+ // }
+
+#ifdef AICWF_RX_REORDER
+#ifdef AICWF_SDIO_SUPPORT
+ rx_priv = rwnx_hw->sdiodev->rx_priv;
+#else
+ rx_priv = rwnx_hw->usbdev->rx_priv;
+#endif
+ if ((rwnx_vif->wdev.iftype == NL80211_IFTYPE_STATION) || (rwnx_vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT)) {
+ BUG();//should be other function
+ } else if ((rwnx_vif->wdev.iftype == NL80211_IFTYPE_AP) || (rwnx_vif->wdev.iftype == NL80211_IFTYPE_P2P_GO)) {
+ macaddr = cur->mac_addr;
+ AICWFDBG(LOGINFO, "deinit:macaddr:%x,%x,%x,%x,%x,%x\r\n", macaddr[0], macaddr[1], macaddr[2], \
+ macaddr[3], macaddr[4], macaddr[5]);
+ //spin_lock_bh(&rx_priv->stas_reord_lock);
+ list_for_each_entry_safe(reord_info, reord_tmp,
+ &rx_priv->stas_reord_list, list) {
+ AICWFDBG(LOGINFO, "reord_mac:%x,%x,%x,%x,%x,%x\r\n", reord_info->mac_addr[0], reord_info->mac_addr[1], reord_info->mac_addr[2], \
+ reord_info->mac_addr[3], reord_info->mac_addr[4], reord_info->mac_addr[5]);
+ if (!memcmp(reord_info->mac_addr, macaddr, 6)) {
+ reord_deinit_sta(rx_priv, reord_info);
+ break;
+ }
+ }
+ //spin_unlock_bh(&rx_priv->stas_reord_lock);
+ }
+#endif
+
+ rwnx_txq_sta_deinit(rwnx_hw, cur);
+ error = rwnx_send_me_sta_del(rwnx_hw, cur->sta_idx, false);
+ if ((error != 0) && (error != -EPIPE))
+ return;
+
+#ifdef CONFIG_RWNX_BFMER
+ // Disable Beamformer if supported
+ rwnx_bfmer_report_del(rwnx_hw, cur);
+ rwnx_mu_group_sta_del(rwnx_hw, cur);
+#endif /* CONFIG_RWNX_BFMER */
+
+
+#ifdef CONFIG_DEBUG_FS
+ rwnx_dbgfs_unregister_rc_stat(rwnx_hw, cur);
+#endif
+ }else {
+ printk("sta not found: %pM\n", mac);
+ return;
+ }
+
+ rwnx_update_mesh_power_mode(rwnx_vif);
+}
+
+
+void apm_probe_sta_work_process(struct work_struct *work)
+{
+ struct apm_probe_sta *probe_sta = container_of(work, struct apm_probe_sta, apmprobestaWork);
+ struct rwnx_vif *rwnx_vif = container_of(probe_sta, struct rwnx_vif, sta_probe);
+ bool found = false;
+ struct rwnx_sta *cur, *tmp;
+
+ u8 *mac = rwnx_vif->sta_probe.sta_mac_addr;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ spin_lock_bh(&rwnx_vif->rwnx_hw->cb_lock);
+ list_for_each_entry_safe(cur, tmp, &rwnx_vif->ap.sta_list, list) {
+ if (!memcmp(cur->mac_addr, mac, ETH_ALEN)) {
+ found = true;
+ break;
+ }
+ }
+ spin_unlock_bh(&rwnx_vif->rwnx_hw->cb_lock);
+
+ printk("sta %pM found = %d\n", mac, found);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0)
+ if(found)
+ cfg80211_probe_status(rwnx_vif->ndev, mac, (u64)rwnx_vif->sta_probe.probe_id, 1, 0, false, GFP_ATOMIC);
+ else
+ cfg80211_probe_status(rwnx_vif->ndev, mac, (u64)rwnx_vif->sta_probe.probe_id, 0, 0, false, GFP_ATOMIC);
+#else
+ if(found)
+ cfg80211_probe_status(rwnx_vif->ndev, mac, (u64)rwnx_vif->sta_probe.probe_id, 1, GFP_ATOMIC);
+ else
+ cfg80211_probe_status(rwnx_vif->ndev, mac, (u64)rwnx_vif->sta_probe.probe_id, 0, GFP_ATOMIC);
+#endif
+ rwnx_vif->sta_probe.probe_id ++;
+}
+
+/**
+ * @change_station: Modify a given station. Note that flags changes are not much
+ * validated in cfg80211, in particular the auth/assoc/authorized flags
+ * might come to the driver in invalid combinations -- make sure to check
+ * them, also against the existing state! Drivers must call
+ * cfg80211_check_station_change() to validate the information.
+ */
+static int rwnx_cfg80211_change_station(struct wiphy *wiphy, struct net_device *dev,
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 16, 0))
+ u8 *mac,
+#else
+ const u8 *mac,
+#endif
+ struct station_parameters *params)
+{
+ struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy);
+ struct rwnx_vif *vif = netdev_priv(dev);
+ struct rwnx_sta *sta;
+
+ sta = rwnx_get_sta(rwnx_hw, mac);
+ if (!sta)
+ {
+ /* Add the TDLS station */
+ if (params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))
+ {
+ struct rwnx_vif *rwnx_vif = netdev_priv(dev);
+ struct me_sta_add_cfm me_sta_add_cfm;
+ int error = 0;
+
+ /* Indicate we are in a STA addition process - This will allow handling
+ * potential PS mode change indications correctly
+ */
+ rwnx_hw->adding_sta = true;
+
+ /* Forward the information to the LMAC */
+ if ((error = rwnx_send_me_sta_add(rwnx_hw, params, mac, rwnx_vif->vif_index,
+ &me_sta_add_cfm)))
+ return error;
+
+ // Check the status
+ switch (me_sta_add_cfm.status)
+ {
+ case CO_OK:
+ {
+ int tid;
+ sta = &rwnx_hw->sta_table[me_sta_add_cfm.sta_idx];
+ sta->aid = params->aid;
+ sta->sta_idx = me_sta_add_cfm.sta_idx;
+ sta->ch_idx = rwnx_vif->ch_index;
+ sta->vif_idx = rwnx_vif->vif_index;
+ sta->vlan_idx = sta->vif_idx;
+ sta->qos = (params->sta_flags_set & BIT(NL80211_STA_FLAG_WME)) != 0;
+#if LINUX_VERSION_CODE >= HIGH_KERNEL_VERSION
+ sta->ht = params->link_sta_params.ht_capa ? 1 : 0;
+ sta->vht = params->link_sta_params.vht_capa ? 1 : 0;
+#else
+ sta->ht = params->ht_capa ? 1 : 0;
+ sta->vht = params->vht_capa ? 1 : 0;
+#endif
+ sta->acm = 0;
+ for (tid = 0; tid < NX_NB_TXQ_PER_STA; tid++) {
+ int uapsd_bit = rwnx_hwq2uapsd[rwnx_tid2hwq[tid]];
+ if (params->uapsd_queues & uapsd_bit)
+ sta->uapsd_tids |= 1 << tid;
+ else
+ sta->uapsd_tids &= ~(1 << tid);
+ }
+ memcpy(sta->mac_addr, mac, ETH_ALEN);
+#ifdef CONFIG_DEBUG_FS
+ rwnx_dbgfs_register_rc_stat(rwnx_hw, sta);
+#endif
+ /* Ensure that we won't process PS change or channel switch ind*/
+ spin_lock_bh(&rwnx_hw->cb_lock);
+ rwnx_txq_sta_init(rwnx_hw, sta, rwnx_txq_vif_get_status(rwnx_vif));
+ if (rwnx_vif->tdls_status == TDLS_SETUP_RSP_TX) {
+ rwnx_vif->tdls_status = TDLS_LINK_ACTIVE;
+ sta->tdls.initiator = true;
+ sta->tdls.active = true;
+ }
+ /* Set TDLS channel switch capability */
+ if ((params->ext_capab[3] & WLAN_EXT_CAPA4_TDLS_CHAN_SWITCH) &&
+ !rwnx_vif->tdls_chsw_prohibited)
+ sta->tdls.chsw_allowed = true;
+ rwnx_vif->sta.tdls_sta = sta;
+ sta->valid = true;
+ spin_unlock_bh(&rwnx_hw->cb_lock);
+#ifdef CONFIG_RWNX_BFMER
+ if (rwnx_hw->mod_params->bfmer)
+ rwnx_send_bfmer_enable(rwnx_hw, sta, params->vht_capa);
+
+ rwnx_mu_group_sta_init(sta, NULL);
+#endif /* CONFIG_RWNX_BFMER */
+
+ #define PRINT_STA_FLAG(f) \
+ (params->sta_flags_set & BIT(NL80211_STA_FLAG_##f) ? "["#f"]" : "")
+
+ netdev_info(dev, "Add %s TDLS sta %d (%pM) flags=%s%s%s%s%s%s%s",
+ sta->tdls.initiator ? "initiator" : "responder",
+ sta->sta_idx, mac,
+ PRINT_STA_FLAG(AUTHORIZED),
+ PRINT_STA_FLAG(SHORT_PREAMBLE),
+ PRINT_STA_FLAG(WME),
+ PRINT_STA_FLAG(MFP),
+ PRINT_STA_FLAG(AUTHENTICATED),
+ PRINT_STA_FLAG(TDLS_PEER),
+ PRINT_STA_FLAG(ASSOCIATED));
+ #undef PRINT_STA_FLAG
+
+ break;
+ }
+ default:
+ error = -EBUSY;
+ break;
+ }
+
+ rwnx_hw->adding_sta = false;
+ } else {
+ return -EINVAL;
+ }
+ }
+
+ if (params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED))
+ rwnx_send_me_set_control_port_req(rwnx_hw,
+ (params->sta_flags_set & BIT(NL80211_STA_FLAG_AUTHORIZED)) != 0,
+ sta->sta_idx);
+
+ if (RWNX_VIF_TYPE(vif) == NL80211_IFTYPE_MESH_POINT) {
+ if (params->sta_modify_mask & STATION_PARAM_APPLY_PLINK_STATE) {
+ if (params->plink_state < NUM_NL80211_PLINK_STATES) {
+ rwnx_send_mesh_peer_update_ntf(rwnx_hw, vif, sta->sta_idx, params->plink_state);
+ }
+ }
+
+ if (params->local_pm != NL80211_MESH_POWER_UNKNOWN) {
+ sta->mesh_pm = params->local_pm;
+ rwnx_update_mesh_power_mode(vif);
+ }
+ }
+
+ if (params->vlan) {
+ uint8_t vlan_idx;
+
+ vif = netdev_priv(params->vlan);
+ vlan_idx = vif->vif_index;
+
+ if (sta->vlan_idx != vlan_idx) {
+ struct rwnx_vif *old_vif;
+ old_vif = rwnx_hw->vif_table[sta->vlan_idx];
+ rwnx_txq_sta_switch_vif(sta, old_vif, vif);
+ sta->vlan_idx = vlan_idx;
+
+ if ((RWNX_VIF_TYPE(vif) == NL80211_IFTYPE_AP_VLAN) &&
+ (vif->use_4addr)) {
+ WARN((vif->ap_vlan.sta_4a),
+ "4A AP_VLAN interface with more than one sta");
+ vif->ap_vlan.sta_4a = sta;
+ }
+
+ if ((RWNX_VIF_TYPE(old_vif) == NL80211_IFTYPE_AP_VLAN) &&
+ (old_vif->use_4addr)) {
+ old_vif->ap_vlan.sta_4a = NULL;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * @start_ap: Start acting in AP mode defined by the parameters.
+ */
+static int rwnx_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *dev,
+ struct cfg80211_ap_settings *settings)
+{
+ struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy);
+ struct rwnx_vif *rwnx_vif = netdev_priv(dev);
+ struct apm_start_cfm apm_start_cfm;
+ struct rwnx_ipc_elem_var elem;
+ struct rwnx_sta *sta;
+ int error = 0;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ INIT_WORK(&rwnx_vif->sta_probe.apmprobestaWork, apm_probe_sta_work_process);
+ rwnx_vif->sta_probe.apmprobesta_wq = create_singlethread_workqueue("apmprobe_wq");
+ if (!rwnx_vif->sta_probe.apmprobesta_wq) {
+ txrx_err("insufficient memory to create apmprobe_wq.\n");
+ return -ENOBUFS;
+ }
+ if (rwnx_vif->wdev.iftype == NL80211_IFTYPE_P2P_GO)
+ rwnx_hw->is_p2p_connected = 1;
+ /* Forward the information to the LMAC */
+ if ((error = rwnx_send_apm_start_req(rwnx_hw, rwnx_vif, settings,
+ &apm_start_cfm, &elem)))
+ goto end;
+
+ // Check the status
+ switch (apm_start_cfm.status)
+ {
+ case CO_OK:
+ {
+ u8 txq_status = 0;
+ rwnx_vif->ap.bcmc_index = apm_start_cfm.bcmc_idx;
+ rwnx_vif->ap.flags = 0;
+ #if (defined CONFIG_HE_FOR_OLD_KERNEL) || (defined CONFIG_VHT_FOR_OLD_KERNEL)
+ rwnx_vif->ap.aic_index = 0;
+ #endif
+ sta = &rwnx_hw->sta_table[apm_start_cfm.bcmc_idx];
+ sta->valid = true;
+ sta->aid = 0;
+ sta->sta_idx = apm_start_cfm.bcmc_idx;
+ sta->ch_idx = apm_start_cfm.ch_idx;
+ sta->vif_idx = rwnx_vif->vif_index;
+ sta->qos = false;
+ sta->acm = 0;
+ sta->ps.active = false;
+ rwnx_mu_group_sta_init(sta, NULL);
+ spin_lock_bh(&rwnx_hw->cb_lock);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 9, 0)
+ rwnx_chanctx_link(rwnx_vif, apm_start_cfm.ch_idx,
+ NULL);
+#else
+ rwnx_chanctx_link(rwnx_vif, apm_start_cfm.ch_idx,
+ &settings->chandef);
+#endif
+ if (rwnx_hw->cur_chanctx != apm_start_cfm.ch_idx) {
+ txq_status = RWNX_TXQ_STOP_CHAN;
+ }
+ rwnx_txq_vif_init(rwnx_hw, rwnx_vif, txq_status);
+ spin_unlock_bh(&rwnx_hw->cb_lock);
+
+ netif_tx_start_all_queues(dev);
+ netif_carrier_on(dev);
+ error = 0;
+ /* If the AP channel is already the active, we probably skip radar
+ activation on MM_CHANNEL_SWITCH_IND (unless another vif use this
+ ctxt). In anycase retest if radar detection must be activated
+ */
+ if (txq_status == 0) {
+ rwnx_radar_detection_enable_on_cur_channel(rwnx_hw);
+ }
+ break;
+ }
+ case CO_BUSY:
+ error = -EINPROGRESS;
+ break;
+ case CO_OP_IN_PROGRESS:
+ error = -EALREADY;
+ break;
+ default:
+ error = -EIO;
+ break;
+ }
+
+ if (error) {
+ netdev_info(dev, "Failed to start AP (%d)", error);
+ } else {
+ netdev_info(dev, "AP started: ch=%d, bcmc_idx=%d channel=%d bw=%d",
+ rwnx_vif->ch_index, rwnx_vif->ap.bcmc_index,
+ ((settings->chandef).chan)->center_freq,
+ ((settings->chandef).width));
+ }
+
+ end:
+ //rwnx_ipc_elem_var_deallocs(rwnx_hw, &elem);
+
+ return error;
+}
+
+
+/**
+ * @change_beacon: Change the beacon parameters for an access point mode
+ * interface. This should reject the call when AP mode wasn't started.
+ */
+static int rwnx_cfg80211_change_beacon(struct wiphy *wiphy, struct net_device *dev,
+ struct cfg80211_beacon_data *info)
+{
+ struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy);
+ struct rwnx_vif *vif = netdev_priv(dev);
+ struct rwnx_bcn *bcn = &vif->ap.bcn;
+ struct rwnx_ipc_elem_var elem;
+ u8 *buf;
+ int error = 0;
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ //elem init
+ elem.dma_addr = 0;
+
+ // Build the beacon
+ buf = rwnx_build_bcn(bcn, info);
+ if (!buf)
+ return -ENOMEM;
+
+ rwnx_send_bcn(rwnx_hw, buf, vif->vif_index, bcn->len);
+
+#if 0
+ // Sync buffer for FW
+ if ((error = rwnx_ipc_elem_var_allocs(rwnx_hw, &elem, bcn->len, DMA_TO_DEVICE,
+ buf, NULL, NULL)))
+ return error;
+#endif
+ // Forward the information to the LMAC
+ error = rwnx_send_bcn_change(rwnx_hw, vif->vif_index, elem.dma_addr,
+ bcn->len, bcn->head_len, bcn->tim_len, NULL);
+
+#if 0
+ rwnx_ipc_elem_var_deallocs(rwnx_hw, &elem);
+#else
+
+
+#endif
+ return error;
+}
+
+/**
+ * * @stop_ap: Stop being an AP, including stopping beaconing.
+ */
+#if (LINUX_VERSION_CODE >= HIGH_KERNEL_VERSION)
+static int rwnx_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *dev, unsigned int link_id)
+#else
+static int rwnx_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *dev)
+#endif
+
+{
+ struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy);
+ struct rwnx_vif *rwnx_vif = netdev_priv(dev);
+ struct rwnx_sta *sta;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ netif_tx_stop_all_queues(dev);
+ netif_carrier_off(dev);
+
+ if (rwnx_vif->wdev.iftype == NL80211_IFTYPE_P2P_GO)
+ rwnx_hw->is_p2p_connected = 0;
+ rwnx_radar_cancel_cac(&rwnx_hw->radar);
+ rwnx_send_apm_stop_req(rwnx_hw, rwnx_vif);
+ spin_lock_bh(&rwnx_hw->cb_lock);
+ rwnx_chanctx_unlink(rwnx_vif);
+ spin_unlock_bh(&rwnx_hw->cb_lock);
+
+ /* delete any remaining STA*/
+ while (!list_empty(&rwnx_vif->ap.sta_list)) {
+ rwnx_cfg80211_del_station_compat(wiphy, dev, NULL);
+ }
+
+ /* delete BC/MC STA */
+ sta = &rwnx_hw->sta_table[rwnx_vif->ap.bcmc_index];
+ rwnx_txq_vif_deinit(rwnx_hw, rwnx_vif);
+ rwnx_del_bcn(&rwnx_vif->ap.bcn);
+ rwnx_del_csa(rwnx_vif);
+
+ flush_workqueue(rwnx_vif->sta_probe.apmprobesta_wq);
+ destroy_workqueue(rwnx_vif->sta_probe.apmprobesta_wq);
+
+ netdev_info(dev, "AP Stopped");
+
+ return 0;
+}
+
+/**
+ * @set_monitor_channel: Set the monitor mode channel for the device. If other
+ * interfaces are active this callback should reject the configuration.
+ * If no interfaces are active or the device is down, the channel should
+ * be stored for when a monitor interface becomes active.
+ *
+ * Also called internaly with chandef set to NULL simply to retrieve the channel
+ * configured at firmware level.
+ */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 8, 0)
+static inline bool
+cfg80211_chandef_identical(const struct cfg80211_chan_def *chandef1,
+ const struct cfg80211_chan_def *chandef2)
+{
+ return (chandef1->chan == chandef2->chan &&
+ chandef1->width == chandef2->width &&
+ chandef1->center_freq1 == chandef2->center_freq1 &&
+ chandef1->center_freq2 == chandef2->center_freq2);
+}
+#endif
+
+static int rwnx_cfg80211_set_monitor_channel(struct wiphy *wiphy,
+ struct cfg80211_chan_def *chandef)
+{
+ struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy);
+ struct rwnx_vif *rwnx_vif;
+ struct me_config_monitor_cfm cfm;
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ if (rwnx_hw->monitor_vif == RWNX_INVALID_VIF)
+ return -EINVAL;
+
+ rwnx_vif = rwnx_hw->vif_table[rwnx_hw->monitor_vif];
+
+ // Do nothing if monitor interface is already configured with the requested channel
+ if (rwnx_chanctx_valid(rwnx_hw, rwnx_vif->ch_index)) {
+ struct rwnx_chanctx *ctxt;
+ ctxt = &rwnx_vif->rwnx_hw->chanctx_table[rwnx_vif->ch_index];
+ if (chandef && cfg80211_chandef_identical(&ctxt->chan_def, chandef))
+ return 0;
+ }
+
+ // Always send command to firmware. It allows to retrieve channel context index
+ // and its configuration.
+ if (rwnx_send_config_monitor_req(rwnx_hw, chandef, &cfm))
+ return -EIO;
+
+ // Always re-set channel context info
+ rwnx_chanctx_unlink(rwnx_vif);
+
+
+
+ // If there is also a STA interface not yet connected then monitor interface
+ // will only have a channel context after the connection of the STA interface.
+ if (cfm.chan_index != RWNX_CH_NOT_SET)
+ {
+ struct cfg80211_chan_def mon_chandef;
+
+ if (rwnx_hw->vif_started > 1) {
+ // In this case we just want to update the channel context index not
+ // the channel configuration
+ rwnx_chanctx_link(rwnx_vif, cfm.chan_index, NULL);
+ return -EBUSY;
+ }
+
+ mon_chandef.chan = ieee80211_get_channel(wiphy, cfm.chan.prim20_freq);
+ mon_chandef.center_freq1 = cfm.chan.center1_freq;
+ mon_chandef.center_freq2 = cfm.chan.center2_freq;
+ mon_chandef.width = chnl2bw[cfm.chan.type];
+ rwnx_chanctx_link(rwnx_vif, cfm.chan_index, &mon_chandef);
+ }
+
+ return 0;
+}
+
+/**
+ * @probe_client: probe an associated client, must return a cookie that it
+ * later passes to cfg80211_probe_status().
+ */
+int rwnx_cfg80211_probe_client(struct wiphy *wiphy, struct net_device *dev,
+ const u8 *peer, u64 *cookie)
+{
+ //struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy);
+ struct rwnx_vif *vif = netdev_priv(dev);
+ struct rwnx_sta *sta = NULL;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ if((RWNX_VIF_TYPE(vif) != NL80211_IFTYPE_AP) && (RWNX_VIF_TYPE(vif) != NL80211_IFTYPE_P2P_GO) &&
+ (RWNX_VIF_TYPE(vif) != NL80211_IFTYPE_AP_VLAN))
+ return -EINVAL;
+
+ spin_lock_bh(&vif->rwnx_hw->cb_lock);
+ list_for_each_entry(sta, &vif->ap.sta_list, list){
+ if (sta->valid && ether_addr_equal(sta->mac_addr, peer))
+ break;
+ }
+ spin_unlock_bh(&vif->rwnx_hw->cb_lock);
+
+ if (!sta)
+ return -ENOENT;
+
+
+ memcpy(vif->sta_probe.sta_mac_addr, peer, 6);
+ queue_work(vif->sta_probe.apmprobesta_wq, &vif->sta_probe.apmprobestaWork);
+
+ *cookie = vif->sta_probe.probe_id;
+
+ return 0;
+}
+
+/**
+ * @mgmt_frame_register: Notify driver that a management frame type was
+ * registered. Note that this callback may not sleep, and cannot run
+ * concurrently with itself.
+ */
+void rwnx_cfg80211_mgmt_frame_register(struct wiphy *wiphy,
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,6,0))
+ struct net_device *dev,
+#else
+ struct wireless_dev *wdev,
+#endif
+ u16 frame_type, bool reg)
+{
+}
+
+/**
+ * @set_wiphy_params: Notify that wiphy parameters have changed;
+ * @changed bitfield (see &enum wiphy_params_flags) describes which values
+ * have changed. The actual parameter values are available in
+ * struct wiphy. If returning an error, no value should be changed.
+ */
+static int rwnx_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
+{
+ return 0;
+}
+
+
+/**
+ * @set_tx_power: set the transmit power according to the parameters,
+ * the power passed is in mBm, to get dBm use MBM_TO_DBM(). The
+ * wdev may be %NULL if power was set for the wiphy, and will
+ * always be %NULL unless the driver supports per-vif TX power
+ * (as advertised by the nl80211 feature flag.)
+ */
+static int rwnx_cfg80211_set_tx_power(struct wiphy *wiphy,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
+ struct wireless_dev *wdev,
+#endif
+ enum nl80211_tx_power_setting type, int mbm)
+{
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 8, 0)
+ struct wireless_dev *wdev = NULL;
+ #endif
+ struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy);
+ struct rwnx_vif *vif;
+ s8 pwr;
+ int res = 0;
+
+ if (type == NL80211_TX_POWER_AUTOMATIC) {
+ pwr = 0x7f;
+ } else {
+ pwr = MBM_TO_DBM(mbm);
+ }
+
+ if (wdev) {
+ vif = container_of(wdev, struct rwnx_vif, wdev);
+ res = rwnx_send_set_power(rwnx_hw, vif->vif_index, pwr, NULL);
+ } else {
+ list_for_each_entry(vif, &rwnx_hw->vifs, list) {
+ res = rwnx_send_set_power(rwnx_hw, vif->vif_index, pwr, NULL);
+ if (res)
+ break;
+ }
+ }
+
+ return res;
+}
+
+
+/**
+ * @set_power_mgmt: set the power save to one of those two modes:
+ * Power-save off
+ * Power-save on - Dynamic mode
+ */
+static int rwnx_cfg80211_set_power_mgmt(struct wiphy *wiphy,
+ struct net_device *dev,
+ bool enabled, int timeout)
+{
+#if 0
+
+ struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy);
+ u8 ps_mode;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+ if (timeout >= 0)
+ netdev_info(dev, "Ignore timeout value %d", timeout);
+
+ if (!(rwnx_hw->version_cfm.features & BIT(MM_FEAT_PS_BIT)))
+ enabled = false;
+
+ if (enabled) {
+ /* Switch to Dynamic Power Save */
+ ps_mode = MM_PS_MODE_ON_DYN;
+ } else {
+ /* Exit Power Save */
+ ps_mode = MM_PS_MODE_OFF;
+ }
+
+ return rwnx_send_me_set_ps_mode(rwnx_hw, ps_mode);
+#else
+ return 0;
+#endif
+}
+
+
+static int rwnx_cfg80211_set_txq_params(struct wiphy *wiphy, struct net_device *dev,
+ struct ieee80211_txq_params *params)
+{
+ struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy);
+ struct rwnx_vif *rwnx_vif = netdev_priv(dev);
+ u8 hw_queue, aifs, cwmin, cwmax;
+ u32 param;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 5, 0)
+ hw_queue = rwnx_ac2hwq[0][params->queue];
+#else
+ hw_queue = rwnx_ac2hwq[0][params->ac];
+#endif
+
+ aifs = params->aifs;
+ cwmin = fls(params->cwmin);
+ cwmax = fls(params->cwmax);
+
+ /* Store queue information in general structure */
+ param = (u32) (aifs << 0);
+ param |= (u32) (cwmin << 4);
+ param |= (u32) (cwmax << 8);
+ param |= (u32) (params->txop) << 12;
+
+ /* Send the MM_SET_EDCA_REQ message to the FW */
+ return rwnx_send_set_edca(rwnx_hw, hw_queue, param, false, rwnx_vif->vif_index);
+}
+
+
+/**
+ * @remain_on_channel: Request the driver to remain awake on the specified
+ * channel for the specified duration to complete an off-channel
+ * operation (e.g., public action frame exchange). When the driver is
+ * ready on the requested channel, it must indicate this with an event
+ * notification by calling cfg80211_ready_on_channel().
+ */
+
+static int
+rwnx_cfg80211_remain_on_channel(struct wiphy *wiphy,
+ #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)
+ struct wireless_dev *wdev,
+ #else
+ struct net_device *dev,
+ #endif
+ struct ieee80211_channel *chan,
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 8, 0)
+ enum nl80211_channel_type channel_type,
+ #endif
+ unsigned int duration, u64 *cookie)
+{
+ struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)
+ struct rwnx_vif *rwnx_vif = container_of(wdev, struct rwnx_vif, wdev);
+#else
+ struct rwnx_vif *rwnx_vif = netdev_priv(dev);
+ struct wireless_dev *wdev = &rwnx_vif->wdev;
+#endif
+ struct rwnx_roc_elem *roc_elem;
+ struct mm_add_if_cfm add_if_cfm;
+ struct mm_remain_on_channel_cfm roc_cfm;
+ int error;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ /* For debug purpose (use ftrace kernel option) */
+#ifdef CREATE_TRACE_POINTS
+ trace_roc(rwnx_vif->vif_index, chan->center_freq, duration);
+#endif
+
+#if 1// Fix up layer to send 50ms duration.
+ if(duration < 100){
+ AICWFDBG(LOGINFO, "%s duration time change to 200ms \r\n", __func__);
+ duration = 200;
+ }
+#endif
+
+ /* Check that no other RoC procedure has been launched */
+ if (rwnx_hw->roc_elem) {
+ msleep(2);
+ if (rwnx_hw->roc_elem) {
+ AICWFDBG(LOGERROR, "remain_on_channel fail\n");
+#if 0//AIDEN test
+ roc_elem = rwnx_hw->roc_elem;
+ kfree(roc_elem);
+ rwnx_hw->roc_elem = NULL;
+ //msleep(500);
+#else
+ return -EBUSY;
+#endif
+ }
+ }
+ //msleep(500);
+ AICWFDBG(LOGINFO, "remain:%d,%d,%d,duration:%d\n",rwnx_vif->vif_index, rwnx_vif->is_p2p_vif, rwnx_hw->is_p2p_alive, duration);
+ //if (rwnx_vif->is_p2p_vif) {
+ AICWFDBG(LOGINFO, "rwnx_vif:%p rwnx_hw->p2p_dev_vif:%p rwnx_vif->up:%d\r\n",
+ rwnx_vif, rwnx_hw->p2p_dev_vif, rwnx_vif->up);
+#ifdef CONFIG_USE_P2P0
+ if (rwnx_vif->is_p2p_vif) {
+#else
+ if (rwnx_vif == rwnx_hw->p2p_dev_vif && !rwnx_vif->up) {
+#endif
+ if (!rwnx_hw->is_p2p_alive) {
+ error = rwnx_send_add_if (rwnx_hw, rwnx_vif->wdev.address, //wdev->netdev->dev_addr,
+ RWNX_VIF_TYPE(rwnx_vif), false, &add_if_cfm);
+ if (error)
+ return -EIO;
+
+ if (add_if_cfm.status != 0) {
+ return -EIO;
+ }
+ /* Save the index retrieved from LMAC */
+ spin_lock_bh(&rwnx_hw->cb_lock);
+ rwnx_vif->vif_index = add_if_cfm.inst_nbr;
+ rwnx_vif->up = true;
+ rwnx_hw->vif_started++;
+ rwnx_hw->vif_table[add_if_cfm.inst_nbr] = rwnx_vif;
+ spin_unlock_bh(&rwnx_hw->cb_lock);
+ rwnx_hw->is_p2p_alive = 1;
+ mod_timer(&rwnx_hw->p2p_alive_timer, jiffies + msecs_to_jiffies(1000));
+ atomic_set(&rwnx_hw->p2p_alive_timer_count, 0);
+ }
+ else {
+ mod_timer(&rwnx_hw->p2p_alive_timer, jiffies + msecs_to_jiffies(1000));
+ atomic_set(&rwnx_hw->p2p_alive_timer_count, 0);
+ }
+ }
+
+ /* Allocate a temporary RoC element */
+ roc_elem = kmalloc(sizeof(struct rwnx_roc_elem), GFP_KERNEL);
+
+ /* Verify that element has well been allocated */
+ if (!roc_elem) {
+ return -ENOMEM;
+ }
+
+ /* Initialize the RoC information element */
+ roc_elem->wdev = wdev;
+ roc_elem->chan = chan;
+ roc_elem->duration = duration;
+ roc_elem->mgmt_roc = false;
+ roc_elem->on_chan = false;
+
+ /* Initialize the OFFCHAN TX queue to allow off-channel transmissions */
+ rwnx_txq_offchan_init(rwnx_vif);
+
+ /* Forward the information to the FMAC */
+ rwnx_hw->roc_elem = roc_elem;
+ error = rwnx_send_roc(rwnx_hw, rwnx_vif, chan, duration, &roc_cfm);
+
+ /* If no error, keep all the information for handling of end of procedure */
+ if (error == 0) {
+
+ /* Set the cookie value */
+ *cookie = (u64)(rwnx_hw->roc_cookie_cnt);
+ if(roc_cfm.status) {
+ // failed to roc
+ rwnx_hw->roc_elem = NULL;
+ kfree(roc_elem);
+ rwnx_txq_offchan_deinit(rwnx_vif);
+ return -EBUSY;
+ }
+ } else {
+ /* Free the allocated element */
+ rwnx_hw->roc_elem = NULL;
+ kfree(roc_elem);
+ rwnx_txq_offchan_deinit(rwnx_vif);
+ }
+
+ return error;
+}
+
+/**
+ * @cancel_remain_on_channel: Cancel an on-going remain-on-channel operation.
+ * This allows the operation to be terminated prior to timeout based on
+ * the duration value.
+ */
+static int rwnx_cfg80211_cancel_remain_on_channel(struct wiphy *wiphy,
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0)
+ struct net_device *dev,
+ #else
+ struct wireless_dev *wdev,
+ #endif
+ u64 cookie)
+{
+ struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy);
+#ifdef CREATE_TRACE_POINTS
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0)
+ struct rwnx_vif *rwnx_vif = netdev_priv(dev);
+#else
+ struct rwnx_vif *rwnx_vif = container_of(wdev, struct rwnx_vif, wdev);
+#endif
+#endif
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+#ifdef CREATE_TRACE_POINTS
+ /* For debug purpose (use ftrace kernel option) */
+ trace_cancel_roc(rwnx_vif->vif_index);
+#endif
+ /* Check if a RoC procedure is pending */
+ if (!rwnx_hw->roc_elem) {
+ return 0;
+ }
+
+ /* Forward the information to the FMAC */
+ return rwnx_send_cancel_roc(rwnx_hw);
+}
+
+/**
+ * @dump_survey: get site survey information.
+ */
+static int rwnx_cfg80211_dump_survey(struct wiphy *wiphy, struct net_device *netdev,
+ int idx, struct survey_info *info)
+{
+ struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy);
+ struct ieee80211_supported_band *sband;
+ struct rwnx_survey_info *rwnx_survey;
+
+ //RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ if (idx >= ARRAY_SIZE(rwnx_hw->survey))
+ return -ENOENT;
+
+ rwnx_survey = &rwnx_hw->survey[idx];
+
+ // Check if provided index matches with a supported 2.4GHz channel
+ sband = wiphy->bands[NL80211_BAND_2GHZ];
+ if (sband && idx >= sband->n_channels) {
+ idx -= sband->n_channels;
+ sband = NULL;
+ }
+
+ //#ifdef USE_5G
+ if (rwnx_hw->band_5g_support) {
+ if (!sband) {
+ // Check if provided index matches with a supported 5GHz channel
+ sband = wiphy->bands[NL80211_BAND_5GHZ];
+
+ if (!sband || idx >= sband->n_channels)
+ return -ENOENT;
+ }
+ }else{
+ //#else
+ if (!sband || idx >= sband->n_channels)
+ return -ENOENT;
+ }
+ //#endif
+
+ // Fill the survey
+ info->channel = &sband->channels[idx];
+ info->filled = rwnx_survey->filled;
+
+ if (rwnx_survey->filled != 0) {
+ SURVEY_TIME(info) = (u64)rwnx_survey->chan_time_ms;
+ SURVEY_TIME_BUSY(info) = (u64)rwnx_survey->chan_time_busy_ms;
+ info->noise = rwnx_survey->noise_dbm;
+
+ // Set the survey report as not used
+ if(info->noise == 0){
+ rwnx_survey->filled = 0;
+ }else{
+ rwnx_survey->filled |= SURVEY_INFO_NOISE_DBM;
+ }
+
+ }
+
+ return 0;
+}
+
+/**
+ * @get_channel: Get the current operating channel for the virtual interface.
+ * For monitor interfaces, it should return %NULL unless there's a single
+ * current monitoring channel.
+ */
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0))
+static int rwnx_cfg80211_get_channel(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+#if LINUX_VERSION_CODE >= HIGH_KERNEL_VERSION
+ unsigned int link_id,
+#endif
+ struct cfg80211_chan_def *chandef)
+{
+ struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy);
+ struct rwnx_vif *rwnx_vif = container_of(wdev, struct rwnx_vif, wdev);
+ struct rwnx_chanctx *ctxt;
+
+ if (!rwnx_vif->up) {
+ return -ENODATA;
+ }
+
+ if (rwnx_vif->vif_index == rwnx_hw->monitor_vif)
+ {
+ //retrieve channel from firmware
+ rwnx_cfg80211_set_monitor_channel(wiphy, NULL);
+ }
+
+ //Check if channel context is valid
+ if (!rwnx_chanctx_valid(rwnx_hw, rwnx_vif->ch_index)){
+ return -ENODATA;
+ }
+
+ ctxt = &rwnx_hw->chanctx_table[rwnx_vif->ch_index];
+ *chandef = ctxt->chan_def;
+
+ return 0;
+}
+#else
+struct ieee80211_channel *rwnx_cfg80211_get_channel(struct wiphy *wiphy)
+{
+ struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy);
+ struct ieee80211_channel *chan = NULL;
+ struct rwnx_vif *vif;
+ bool_l found = false;
+ //may TBD
+
+ list_for_each_entry(vif, &rwnx_hw->vifs, list)
+ {
+ if(vif->wdev.iftype == NL80211_IFTYPE_AP)
+ {
+ found = true;
+ break;
+ }
+ }
+
+ if(found && rwnx_hw->set_chan.center_freq) {
+ chan = kzalloc(sizeof(struct ieee80211_channel), GFP_KERNEL);
+ memcpy((u8 *)chan, (u8 *)&rwnx_hw->set_chan, sizeof(struct ieee80211_channel));
+ }
+
+ return chan;
+}
+#endif
+
+
+/**
+ * @mgmt_tx: Transmit a management frame.
+ */
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0))
+static int rwnx_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
+ struct cfg80211_mgmt_tx_params *params,
+ u64 *cookie)
+#else
+static int rwnx_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
+ struct ieee80211_channel *channel, bool offchan,
+ unsigned int wait, const u8* buf, size_t len,
+ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0))
+ bool no_cck,
+ #endif
+ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 3, 0))
+ bool dont_wait_for_ack,
+ #endif
+ u64 *cookie)
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) */
+{
+ struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy);
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0))
+ struct rwnx_vif *rwnx_vif = netdev_priv(dev);
+ struct wireless_dev *wdev = &rwnx_vif->wdev;
+#else
+ struct rwnx_vif *rwnx_vif = container_of(wdev, struct rwnx_vif, wdev);
+#endif
+ struct rwnx_sta *rwnx_sta = NULL;
+ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0))
+ struct ieee80211_channel *channel = params->chan;
+ const u8 *buf = params->buf;
+ //size_t len = params->len;
+ #endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) */
+ struct ieee80211_mgmt *mgmt = (void *)buf;
+ bool ap = false;
+ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0))
+ bool offchan = false;
+ #endif
+
+ /* Check if provided VIF is an AP or a STA one */
+ switch (RWNX_VIF_TYPE(rwnx_vif)) {
+ case NL80211_IFTYPE_AP_VLAN:
+ rwnx_vif = rwnx_vif->ap_vlan.master;
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_P2P_GO:
+ case NL80211_IFTYPE_MESH_POINT:
+ ap = true;
+ break;
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_P2P_CLIENT:
+ default:
+ break;
+ }
+
+
+ /* Get STA on which management frame has to be sent */
+ rwnx_sta = rwnx_retrieve_sta(rwnx_hw, rwnx_vif, mgmt->da,
+ mgmt->frame_control, ap);
+
+ AICWFDBG(LOGDEBUG, "%s rwnx_sta:%p RWNX_VIF_TYPE(rwnx_vif):%d \r\n", __func__, rwnx_sta, RWNX_VIF_TYPE(rwnx_vif));
+
+#ifdef CREATE_TRACE_POINTS
+ trace_mgmt_tx((channel) ? channel->center_freq : 0,
+ rwnx_vif->vif_index, (rwnx_sta) ? rwnx_sta->sta_idx : 0xFF,
+ mgmt);
+#endif
+ if (ap || rwnx_sta)
+ goto send_frame;
+
+ /* Not an AP interface sending frame to unknown STA:
+ * This is allowed for external authetication */
+ if (rwnx_vif->sta.external_auth && ieee80211_is_auth(mgmt->frame_control))
+ goto send_frame;
+
+ /* Otherwise ROC is needed */
+ if (!channel) {
+ AICWFDBG(LOGERROR, "mgmt_tx fail since channel\n");
+ return -EINVAL;
+ }
+
+ /* Check that a RoC is already pending */
+ if (rwnx_hw->roc_elem) {
+ /* Get VIF used for current ROC */
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0))
+ struct rwnx_vif *rwnx_roc_vif = netdev_priv(rwnx_hw->roc_elem->wdev->netdev);
+#else
+ struct rwnx_vif *rwnx_roc_vif = container_of(rwnx_hw->roc_elem->wdev, struct rwnx_vif, wdev);//netdev_priv(rwnx_hw->roc_elem->wdev->netdev);
+#endif
+
+ /* Check if RoC channel is the same than the required one */
+ if ((rwnx_hw->roc_elem->chan->center_freq != channel->center_freq)
+ || (rwnx_vif->vif_index != rwnx_roc_vif->vif_index)) {
+ AICWFDBG(LOGERROR, "mgmt rx chan invalid: %d, %d", rwnx_hw->roc_elem->chan->center_freq, channel->center_freq);
+ return -EINVAL;
+ }
+ } else {
+ u64 cookie;
+ int error;
+
+ AICWFDBG(LOGINFO, "mgmt rx remain on chan\n");
+
+ /* Start a ROC procedure for 30ms */
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0))
+ error = rwnx_cfg80211_remain_on_channel(wiphy, wdev, channel,
+ 30, &cookie);
+#elif (LINUX_VERSION_CODE < KERNEL_VERSION(3, 8, 0)) && (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0))
+ error = rwnx_cfg80211_remain_on_channel(wiphy, wdev, channel, NL80211_CHAN_NO_HT,
+ 30, &cookie);
+#else
+ error = rwnx_cfg80211_remain_on_channel(wiphy, dev, channel, NL80211_CHAN_NO_HT,
+ 30, &cookie);
+#endif
+
+ if (error) {
+ AICWFDBG(LOGERROR, "mgmt rx chan err\n");
+ return error;
+ }
+ /* Need to keep in mind that RoC has been launched internally in order to
+ * avoid to call the cfg80211 callback once expired */
+ rwnx_hw->roc_elem->mgmt_roc = true;
+ }
+
+ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0))
+ offchan = true;
+ #endif
+
+send_frame:
+ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0))
+ return rwnx_start_mgmt_xmit(rwnx_vif, rwnx_sta, params, offchan, cookie);
+ #else
+ return rwnx_start_mgmt_xmit(rwnx_vif, rwnx_sta, channel, offchan, wait, buf, len, no_cck, dont_wait_for_ack, cookie);
+ #endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) */
+}
+
+/**
+ * @start_radar_detection: Start radar detection in the driver.
+ */
+static
+int rwnx_cfg80211_start_radar_detection(struct wiphy *wiphy,
+ struct net_device *dev,
+ struct cfg80211_chan_def *chandef
+ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 15, 0))
+ , u32 cac_time_ms
+ #endif
+ )
+{
+ struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy);
+ struct rwnx_vif *rwnx_vif = netdev_priv(dev);
+ struct apm_start_cac_cfm cfm;
+
+ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 15, 0))
+ rwnx_radar_start_cac(&rwnx_hw->radar, cac_time_ms, rwnx_vif);
+ #endif
+ rwnx_send_apm_start_cac_req(rwnx_hw, rwnx_vif, chandef, &cfm);
+
+ if (cfm.status == CO_OK) {
+ spin_lock_bh(&rwnx_hw->cb_lock);
+ rwnx_chanctx_link(rwnx_vif, cfm.ch_idx, chandef);
+ if (rwnx_hw->cur_chanctx == rwnx_vif->ch_index)
+ rwnx_radar_detection_enable(&rwnx_hw->radar,
+ RWNX_RADAR_DETECT_REPORT,
+ RWNX_RADAR_RIU);
+ spin_unlock_bh(&rwnx_hw->cb_lock);
+ } else {
+ return -EIO;
+ }
+
+ return 0;
+}
+
+/**
+ * @update_ft_ies: Provide updated Fast BSS Transition information to the
+ * driver. If the SME is in the driver/firmware, this information can be
+ * used in building Authentication and Reassociation Request frames.
+ */
+static
+int rwnx_cfg80211_update_ft_ies(struct wiphy *wiphy,
+ struct net_device *dev,
+ struct cfg80211_update_ft_ies_params *ftie)
+{
+ return 0;
+}
+
+/**
+ * @set_cqm_rssi_config: Configure connection quality monitor RSSI threshold.
+ */
+static
+int rwnx_cfg80211_set_cqm_rssi_config(struct wiphy *wiphy,
+ struct net_device *dev,
+ int32_t rssi_thold, uint32_t rssi_hyst)
+{
+ struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy);
+ struct rwnx_vif *rwnx_vif = netdev_priv(dev);
+
+ return rwnx_send_cfg_rssi_req(rwnx_hw, rwnx_vif->vif_index, rssi_thold, rssi_hyst);
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)
+/**
+ *
+ * @channel_switch: initiate channel-switch procedure (with CSA). Driver is
+ * responsible for veryfing if the switch is possible. Since this is
+ * inherently tricky driver may decide to disconnect an interface later
+ * with cfg80211_stop_iface(). This doesn't mean driver can accept
+ * everything. It should do it's best to verify requests and reject them
+ * as soon as possible.
+ */
+int rwnx_cfg80211_channel_switch(struct wiphy *wiphy,
+ struct net_device *dev,
+ struct cfg80211_csa_settings *params)
+{
+ struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy);
+ struct rwnx_vif *vif = netdev_priv(dev);
+ struct rwnx_ipc_elem_var elem;
+ struct rwnx_bcn *bcn, *bcn_after;
+ struct rwnx_csa *csa;
+ u16 csa_oft[BCN_MAX_CSA_CPT];
+ u8 *buf;
+ int i, error = 0;
+
+ //elem init
+ elem.dma_addr = 0;
+
+ if (vif->ap.csa)
+ return -EBUSY;
+
+ if (params->n_counter_offsets_beacon > BCN_MAX_CSA_CPT)
+ return -EINVAL;
+
+ /* Build the new beacon with CSA IE */
+ bcn = &vif->ap.bcn;
+ buf = rwnx_build_bcn(bcn, &params->beacon_csa);
+ if (!buf)
+ return -ENOMEM;
+
+ memset(csa_oft, 0, sizeof(csa_oft));
+ for (i = 0; i < params->n_counter_offsets_beacon; i++)
+ {
+ csa_oft[i] = params->counter_offsets_beacon[i] + bcn->head_len +
+ bcn->tim_len;
+ }
+
+ /* If count is set to 0 (i.e anytime after this beacon) force it to 2 */
+ if (params->count == 0) {
+ params->count = 2;
+ for (i = 0; i < params->n_counter_offsets_beacon; i++)
+ {
+ buf[csa_oft[i]] = 2;
+ }
+ }
+
+ #if 0
+ if ((error = rwnx_ipc_elem_var_allocs(rwnx_hw, &elem, bcn->len,
+ DMA_TO_DEVICE, buf, NULL, NULL))) {
+ goto end;
+ }
+ #else
+ error = rwnx_send_bcn(rwnx_hw, buf, vif->vif_index, bcn->len);
+ if (error) {
+ goto end;
+ }
+ #endif
+
+ /* Build the beacon to use after CSA. It will only be sent to fw once
+ CSA is over, but do it before sending the beacon as it must be ready
+ when CSA is finished. */
+ csa = kzalloc(sizeof(struct rwnx_csa), GFP_KERNEL);
+ if (!csa) {
+ error = -ENOMEM;
+ goto end;
+ }
+
+ bcn_after = &csa->bcn;
+ buf = rwnx_build_bcn(bcn_after, &params->beacon_after);
+ if (!buf) {
+ error = -ENOMEM;
+ rwnx_del_csa(vif);
+ goto end;
+ }
+
+ vif->ap.csa = csa;
+ csa->vif = vif;
+ csa->chandef = params->chandef;
+
+ /* Send new Beacon. FW will extract channel and count from the beacon */
+ error = rwnx_send_bcn_change(rwnx_hw, vif->vif_index, elem.dma_addr,
+ bcn->len, bcn->head_len, bcn->tim_len, csa_oft);
+
+ if (error) {
+ rwnx_del_csa(vif);
+ goto end;
+ } else {
+ INIT_WORK(&csa->work, rwnx_csa_finish);
+
+#if LINUX_VERSION_CODE >= HIGH_KERNEL_VERSION
+ cfg80211_ch_switch_started_notify(dev, &csa->chandef, 0, params->count, false, 0);
+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0)
+ cfg80211_ch_switch_started_notify(dev, &csa->chandef, params->count, params->block_tx);
+#else
+ cfg80211_ch_switch_started_notify(dev, &csa->chandef, params->count);
+#endif
+
+ }
+
+ end:
+ return error;
+}
+#endif
+
+#if 0
+/*
+ * @tdls_mgmt: prepare TDLS action frame packets and forward them to FW
+ */
+
+static int
+rwnx_cfg80211_tdls_mgmt(struct wiphy *wiphy,
+ struct net_device *dev,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)
+ const u8 *peer,
+#else
+ u8 *peer,
+#endif
+ u8 action_code,
+ u8 dialog_token,
+ u16 status_code,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 15, 0)
+ u32 peer_capability,
+#endif
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0)
+ bool initiator,
+#endif
+ const u8 *buf,
+ size_t len)
+{
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 15, 0)
+ u32 peer_capability = 0;
+ #endif
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0)
+ bool initiator = false;
+ #endif
+ struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy);
+ struct rwnx_vif *rwnx_vif = netdev_priv(dev);
+ int ret = 0;
+
+ /* make sure we support TDLS */
+ if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS))
+ return -ENOTSUPP;
+
+ /* make sure we are in station mode (and connected) */
+ if ((RWNX_VIF_TYPE(rwnx_vif) != NL80211_IFTYPE_STATION) ||
+ (!rwnx_vif->up) || (!rwnx_vif->sta.ap))
+ return -ENOTSUPP;
+
+ /* only one TDLS link is supported */
+ if ((action_code == WLAN_TDLS_SETUP_REQUEST) &&
+ (rwnx_vif->sta.tdls_sta) &&
+ (rwnx_vif->tdls_status == TDLS_LINK_ACTIVE)) {
+ printk("%s: only one TDLS link is supported!\n", __func__);
+ return -ENOTSUPP;
+ }
+
+ if ((action_code == WLAN_TDLS_DISCOVERY_REQUEST) &&
+ (rwnx_hw->mod_params->ps_on)) {
+ printk("%s: discovery request is not supported when "
+ "power-save is enabled!\n", __func__);
+ return -ENOTSUPP;
+ }
+
+ switch (action_code) {
+ case WLAN_TDLS_SETUP_RESPONSE:
+ /* only one TDLS link is supported */
+ if ((status_code == 0) &&
+ (rwnx_vif->sta.tdls_sta) &&
+ (rwnx_vif->tdls_status == TDLS_LINK_ACTIVE)) {
+ printk("%s: only one TDLS link is supported!\n", __func__);
+ status_code = WLAN_STATUS_REQUEST_DECLINED;
+ }
+ /* fall-through */
+ case WLAN_TDLS_SETUP_REQUEST:
+ case WLAN_TDLS_TEARDOWN:
+ case WLAN_TDLS_DISCOVERY_REQUEST:
+ case WLAN_TDLS_SETUP_CONFIRM:
+ case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
+ ret = rwnx_tdls_send_mgmt_packet_data(rwnx_hw, rwnx_vif, peer, action_code,
+ dialog_token, status_code, peer_capability, initiator, buf, len, 0, NULL);
+ break;
+
+ default:
+ printk("%s: Unknown TDLS mgmt/action frame %pM\n",
+ __func__, peer);
+ ret = -EOPNOTSUPP;
+ break;
+ }
+
+ if (action_code == WLAN_TDLS_SETUP_REQUEST) {
+ rwnx_vif->tdls_status = TDLS_SETUP_REQ_TX;
+ } else if (action_code == WLAN_TDLS_SETUP_RESPONSE) {
+ rwnx_vif->tdls_status = TDLS_SETUP_RSP_TX;
+ } else if ((action_code == WLAN_TDLS_SETUP_CONFIRM) && (ret == CO_OK)) {
+ rwnx_vif->tdls_status = TDLS_LINK_ACTIVE;
+ /* Set TDLS active */
+ rwnx_vif->sta.tdls_sta->tdls.active = true;
+ }
+
+ return ret;
+}
+#endif
+#if 0
+/*
+ * @tdls_oper: execute TDLS operation
+ */
+static int
+rwnx_cfg80211_tdls_oper(struct wiphy *wiphy,
+ struct net_device *dev,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)
+ const u8 *peer,
+#else
+ u8 *peer,
+#endif
+ enum nl80211_tdls_operation oper
+)
+{
+ struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy);
+ struct rwnx_vif *rwnx_vif = netdev_priv(dev);
+ int error;
+
+ if (oper != NL80211_TDLS_DISABLE_LINK)
+ return 0;
+
+ if (!rwnx_vif->sta.tdls_sta) {
+ printk("%s: TDLS station %pM does not exist\n", __func__, peer);
+ return -ENOLINK;
+ }
+
+ if (memcmp(rwnx_vif->sta.tdls_sta->mac_addr, peer, ETH_ALEN) == 0) {
+ /* Disable Channel Switch */
+ if (!rwnx_send_tdls_cancel_chan_switch_req(rwnx_hw, rwnx_vif,
+ rwnx_vif->sta.tdls_sta,
+ NULL))
+ rwnx_vif->sta.tdls_sta->tdls.chsw_en = false;
+
+ netdev_info(dev, "Del TDLS sta %d (%pM)",
+ rwnx_vif->sta.tdls_sta->sta_idx,
+ rwnx_vif->sta.tdls_sta->mac_addr);
+ /* Ensure that we won't process PS change ind */
+ spin_lock_bh(&rwnx_hw->cb_lock);
+ rwnx_vif->sta.tdls_sta->ps.active = false;
+ rwnx_vif->sta.tdls_sta->valid = false;
+ spin_unlock_bh(&rwnx_hw->cb_lock);
+ rwnx_txq_sta_deinit(rwnx_hw, rwnx_vif->sta.tdls_sta);
+ error = rwnx_send_me_sta_del(rwnx_hw, rwnx_vif->sta.tdls_sta->sta_idx, true);
+ if ((error != 0) && (error != -EPIPE))
+ return error;
+
+#ifdef CONFIG_RWNX_BFMER
+ // Disable Beamformer if supported
+ rwnx_bfmer_report_del(rwnx_hw, rwnx_vif->sta.tdls_sta);
+ rwnx_mu_group_sta_del(rwnx_hw, rwnx_vif->sta.tdls_sta);
+#endif /* CONFIG_RWNX_BFMER */
+
+ /* Set TDLS not active */
+ rwnx_vif->sta.tdls_sta->tdls.active = false;
+#ifdef CONFIG_DEBUG_FS
+ rwnx_dbgfs_unregister_rc_stat(rwnx_hw, rwnx_vif->sta.tdls_sta);
+#endif
+ // Remove TDLS station
+ rwnx_vif->tdls_status = TDLS_LINK_IDLE;
+ rwnx_vif->sta.tdls_sta = NULL;
+ }
+
+ return 0;
+}
+#endif
+#if 0
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)
+/*
+ * @tdls_channel_switch: enable TDLS channel switch
+ */
+static int
+rwnx_cfg80211_tdls_channel_switch(struct wiphy *wiphy,
+ struct net_device *dev,
+ const u8 *addr, u8 oper_class,
+ struct cfg80211_chan_def *chandef)
+{
+ struct rwnx_vif *rwnx_vif = netdev_priv(dev);
+ struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy);
+ struct rwnx_sta *rwnx_sta = rwnx_vif->sta.tdls_sta;
+ struct tdls_chan_switch_cfm cfm;
+ int error;
+
+ if ((!rwnx_sta) || (memcmp(addr, rwnx_sta->mac_addr, ETH_ALEN))) {
+ printk("%s: TDLS station %pM doesn't exist\n", __func__, addr);
+ return -ENOLINK;
+ }
+
+ if (!rwnx_sta->tdls.chsw_allowed) {
+ printk("%s: TDLS station %pM does not support TDLS channel switch\n", __func__, addr);
+ return -ENOTSUPP;
+ }
+
+ error = rwnx_send_tdls_chan_switch_req(rwnx_hw, rwnx_vif, rwnx_sta,
+ rwnx_sta->tdls.initiator,
+ oper_class, chandef, &cfm);
+ if (error)
+ return error;
+
+ if (!cfm.status) {
+ rwnx_sta->tdls.chsw_en = true;
+ return 0;
+ } else {
+ printk("%s: TDLS channel switch already enabled and only one is supported\n", __func__);
+ return -EALREADY;
+ }
+}
+#endif
+#if 0
+/*
+ * @tdls_cancel_channel_switch: disable TDLS channel switch
+ */
+static void
+rwnx_cfg80211_tdls_cancel_channel_switch(struct wiphy *wiphy,
+ struct net_device *dev,
+ const u8 *addr)
+{
+ struct rwnx_vif *rwnx_vif = netdev_priv(dev);
+ struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy);
+ struct rwnx_sta *rwnx_sta = rwnx_vif->sta.tdls_sta;
+ struct tdls_cancel_chan_switch_cfm cfm;
+
+ if (!rwnx_sta)
+ return;
+
+ if (!rwnx_send_tdls_cancel_chan_switch_req(rwnx_hw, rwnx_vif,
+ rwnx_sta, &cfm))
+ rwnx_sta->tdls.chsw_en = false;
+}
+#endif /* version >= 3.19 */
+#endif
+/**
+ * @change_bss: Modify parameters for a given BSS (mainly for AP mode).
+ */
+int rwnx_cfg80211_change_bss(struct wiphy *wiphy, struct net_device *dev,
+ struct bss_parameters *params)
+{
+ struct rwnx_vif *rwnx_vif = netdev_priv(dev);
+ int res = -EOPNOTSUPP;
+
+ if (((RWNX_VIF_TYPE(rwnx_vif) == NL80211_IFTYPE_AP) ||
+ (RWNX_VIF_TYPE(rwnx_vif) == NL80211_IFTYPE_P2P_GO)) &&
+ (params->ap_isolate > -1)) {
+
+ if (params->ap_isolate)
+ rwnx_vif->ap.flags |= RWNX_AP_ISOLATE;
+ else
+ rwnx_vif->ap.flags &= ~RWNX_AP_ISOLATE;
+
+ res = 0;
+ }
+
+ return res;
+}
+
+static int rwnx_fill_station_info(struct rwnx_sta *sta, struct rwnx_vif *vif,
+ struct station_info *sinfo)
+{
+
+ struct rwnx_sta_stats *stats = &sta->stats;
+ struct rx_vector_1 *rx_vect1 = &stats->last_rx.rx_vect1;
+ union rwnx_rate_ctrl_info *rate_info;
+ struct mm_get_sta_info_cfm cfm;
+
+ rwnx_send_get_sta_info_req(vif->rwnx_hw, sta->sta_idx, &cfm);
+ sinfo->tx_failed = cfm.txfailed;
+ rate_info = (union rwnx_rate_ctrl_info *)&cfm.rate_info;
+
+
+ AICWFDBG(LOGDEBUG, "%s ModTx:%d TxIndex:%d ModRx:%d RxHTIndex:%d RxVHTIndex:%d RxHEIndex:%d RSSI:%d \r\n", __func__,
+ rate_info->formatModTx, rate_info->mcsIndexTx, rx_vect1->format_mod,
+ rx_vect1->ht.mcs,
+ rx_vect1->vht.mcs,
+ rx_vect1->he.mcs,
+ (s8)cfm.rssi);
+
+
+ switch (rate_info->formatModTx) {
+ case FORMATMOD_NON_HT:
+ case FORMATMOD_NON_HT_DUP_OFDM:
+ sinfo->txrate.flags = 0;
+ sinfo->txrate.legacy = tx_legrates_lut_rate[rate_info->mcsIndexTx];
+ break;
+ case FORMATMOD_HT_MF:
+ case FORMATMOD_HT_GF:
+ sinfo->txrate.flags = RATE_INFO_FLAGS_MCS;
+ sinfo->txrate.mcs = rate_info->mcsIndexTx;
+ break;
+ case FORMATMOD_VHT:
+ sinfo->txrate.flags = RATE_INFO_FLAGS_VHT_MCS;
+ sinfo->txrate.mcs = rate_info->mcsIndexTx;
+ break;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)
+ case FORMATMOD_HE_MU:
+ case FORMATMOD_HE_SU:
+ case FORMATMOD_HE_ER:
+ sinfo->txrate.flags = RATE_INFO_FLAGS_HE_MCS;
+ sinfo->txrate.mcs = rate_info->mcsIndexTx;
+ break;
+#else
+ //kernel not support he
+ case FORMATMOD_HE_MU:
+ case FORMATMOD_HE_SU:
+ case FORMATMOD_HE_ER:
+ sinfo->txrate.flags = RATE_INFO_FLAGS_VHT_MCS;
+ sinfo->txrate.mcs = rate_info->mcsIndexTx;
+ break;
+#endif
+ default:
+ return -EINVAL;
+ }
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)
+ switch (rate_info->bwTx) {
+ case PHY_CHNL_BW_20:
+ sinfo->txrate.bw = RATE_INFO_BW_20;
+ break;
+ case PHY_CHNL_BW_40:
+ sinfo->txrate.bw = RATE_INFO_BW_40;
+ break;
+ case PHY_CHNL_BW_80:
+ sinfo->txrate.bw = RATE_INFO_BW_80;
+ break;
+ case PHY_CHNL_BW_160:
+ sinfo->txrate.bw = RATE_INFO_BW_160;
+ break;
+ default:
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)
+ sinfo->txrate.bw = RATE_INFO_BW_HE_RU;
+#else
+ sinfo->txrate.bw = RATE_INFO_BW_20;
+#endif
+ break;
+ }
+#endif
+
+ sinfo->txrate.nss = 1;
+ sinfo->filled |= (BIT(NL80211_STA_INFO_TX_BITRATE) | BIT(NL80211_STA_INFO_TX_FAILED));
+
+ sinfo->inactive_time = jiffies_to_msecs(jiffies - vif->rwnx_hw->stats.last_tx);
+ sinfo->rx_bytes = vif->net_stats.rx_bytes;
+ sinfo->tx_bytes = vif->net_stats.tx_bytes;
+ sinfo->tx_packets = vif->net_stats.tx_packets;
+ sinfo->rx_packets = vif->net_stats.rx_packets;
+ sinfo->signal = (s8)cfm.rssi;
+ sinfo->rxrate.nss = 1;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)
+ switch (rx_vect1->ch_bw) {
+ case PHY_CHNL_BW_20:
+ sinfo->rxrate.bw = RATE_INFO_BW_20;
+ break;
+ case PHY_CHNL_BW_40:
+ sinfo->rxrate.bw = RATE_INFO_BW_40;
+ break;
+ case PHY_CHNL_BW_80:
+ sinfo->rxrate.bw = RATE_INFO_BW_80;
+ break;
+ case PHY_CHNL_BW_160:
+ sinfo->rxrate.bw = RATE_INFO_BW_160;
+ break;
+ default:
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)
+ sinfo->rxrate.bw = RATE_INFO_BW_HE_RU;
+#else
+ sinfo->rxrate.bw = RATE_INFO_BW_20;
+#endif
+ break;
+ }
+#endif
+
+ switch (rx_vect1->format_mod) {
+ case FORMATMOD_NON_HT:
+ case FORMATMOD_NON_HT_DUP_OFDM:
+ sinfo->rxrate.flags = 0;
+ sinfo->rxrate.legacy = legrates_lut_rate[legrates_lut[rx_vect1->leg_rate]];
+ break;
+ case FORMATMOD_HT_MF:
+ case FORMATMOD_HT_GF:
+ sinfo->rxrate.flags = RATE_INFO_FLAGS_MCS;
+ if (rx_vect1->ht.short_gi)
+ sinfo->rxrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
+ sinfo->rxrate.mcs = rx_vect1->ht.mcs;
+ break;
+ case FORMATMOD_VHT:
+ sinfo->rxrate.flags = RATE_INFO_FLAGS_VHT_MCS;
+ if (rx_vect1->vht.short_gi)
+ sinfo->rxrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
+ sinfo->rxrate.mcs = rx_vect1->vht.mcs;
+ break;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)
+ case FORMATMOD_HE_MU:
+ sinfo->rxrate.he_ru_alloc = rx_vect1->he.ru_size;
+ case FORMATMOD_HE_SU:
+ case FORMATMOD_HE_ER:
+ sinfo->rxrate.flags = RATE_INFO_FLAGS_HE_MCS;
+ sinfo->rxrate.mcs = rx_vect1->he.mcs;
+ sinfo->rxrate.he_gi = rx_vect1->he.gi_type;
+ sinfo->rxrate.he_dcm = rx_vect1->he.dcm;
+ break;
+#else
+ //kernel not support he
+ case FORMATMOD_HE_MU:
+ case FORMATMOD_HE_SU:
+ case FORMATMOD_HE_ER:
+ sinfo->rxrate.flags = RATE_INFO_FLAGS_VHT_MCS;
+ sinfo->rxrate.mcs = rx_vect1->he.mcs;
+ break;
+#endif
+ default:
+ return -EINVAL;
+ }
+
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0)
+ sinfo->filled |= (STATION_INFO_INACTIVE_TIME |
+ STATION_INFO_RX_BYTES64 |
+ STATION_INFO_TX_BYTES64 |
+ STATION_INFO_RX_PACKETS |
+ STATION_INFO_TX_PACKETS |
+ STATION_INFO_SIGNAL |
+ STATION_INFO_RX_BITRATE);
+#else
+ sinfo->filled |= (BIT(NL80211_STA_INFO_INACTIVE_TIME) |
+ BIT(NL80211_STA_INFO_RX_BYTES64) |
+ BIT(NL80211_STA_INFO_TX_BYTES64) |
+ BIT(NL80211_STA_INFO_RX_PACKETS) |
+ BIT(NL80211_STA_INFO_TX_PACKETS) |
+ BIT(NL80211_STA_INFO_SIGNAL) |
+ BIT(NL80211_STA_INFO_RX_BITRATE));
+#endif
+
+ return 0;
+}
+
+
+/**
+ * @get_station: get station information for the station identified by @mac
+ */
+static int rwnx_cfg80211_get_station(struct wiphy *wiphy,
+ struct net_device *dev,
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 16, 0))
+ u8 *mac,
+#else
+ const u8 *mac,
+#endif
+ struct station_info *sinfo)
+{
+ struct rwnx_vif *vif = netdev_priv(dev);
+ struct rwnx_sta *sta = NULL;
+
+ if (RWNX_VIF_TYPE(vif) == NL80211_IFTYPE_MONITOR)
+ return -EINVAL;
+ else if ((RWNX_VIF_TYPE(vif) == NL80211_IFTYPE_STATION) ||
+ (RWNX_VIF_TYPE(vif) == NL80211_IFTYPE_P2P_CLIENT)) {
+ if (vif->sta.ap && ether_addr_equal(vif->sta.ap->mac_addr, mac))
+ sta = vif->sta.ap;
+ }
+ else
+ {
+ struct rwnx_sta *sta_iter;
+ spin_lock_bh(&vif->rwnx_hw->cb_lock);
+ list_for_each_entry(sta_iter, &vif->ap.sta_list, list) {
+ if (sta_iter->valid && ether_addr_equal(sta_iter->mac_addr, mac)) {
+ sta = sta_iter;
+ break;
+ }
+ }
+ spin_unlock_bh(&vif->rwnx_hw->cb_lock);
+ }
+
+ if (sta)
+ return rwnx_fill_station_info(sta, vif, sinfo);
+
+ return -ENOENT;
+}
+
+
+/**
+ * @dump_station: dump station callback -- resume dump at index @idx
+ */
+static int rwnx_cfg80211_dump_station(struct wiphy *wiphy, struct net_device *dev,
+ int idx, u8 *mac, struct station_info *sinfo)
+{
+ struct rwnx_vif *rwnx_vif = netdev_priv(dev);
+ struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy);
+ struct rwnx_sta *sta_iter, *sta = NULL;
+ struct mesh_peer_info_cfm peer_info_cfm;
+ int i = 0;
+
+ if (RWNX_VIF_TYPE(rwnx_vif) != NL80211_IFTYPE_MESH_POINT)
+ return -ENOTSUPP;
+
+ list_for_each_entry(sta_iter, &rwnx_vif->ap.sta_list, list) {
+ if (i < idx) {
+ i++;
+ continue;
+ }
+
+ sta = sta_iter;
+ break;
+ }
+
+ if (sta == NULL)
+ return -ENOENT;
+
+ /* Forward the information to the UMAC */
+ if (rwnx_send_mesh_peer_info_req(rwnx_hw, rwnx_vif, sta->sta_idx,
+ &peer_info_cfm))
+ return -ENOMEM;
+
+ /* Copy peer MAC address */
+ memcpy(mac, &sta->mac_addr, ETH_ALEN);
+
+ /* Fill station information */
+ sinfo->llid = peer_info_cfm.local_link_id;
+ sinfo->plid = peer_info_cfm.peer_link_id;
+ sinfo->plink_state = peer_info_cfm.link_state;
+ sinfo->local_pm = peer_info_cfm.local_ps_mode;
+ sinfo->peer_pm = peer_info_cfm.peer_ps_mode;
+ sinfo->nonpeer_pm = peer_info_cfm.non_peer_ps_mode;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0)
+ sinfo->filled = (STATION_INFO_LLID |
+ STATION_INFO_PLID |
+ STATION_INFO_PLINK_STATE |
+ STATION_INFO_LOCAL_PM |
+ STATION_INFO_PEER_PM |
+ STATION_INFO_NONPEER_PM);
+#else
+ sinfo->filled = (BIT(NL80211_STA_INFO_LLID) |
+ BIT(NL80211_STA_INFO_PLID) |
+ BIT(NL80211_STA_INFO_PLINK_STATE) |
+ BIT(NL80211_STA_INFO_LOCAL_PM) |
+ BIT(NL80211_STA_INFO_PEER_PM) |
+ BIT(NL80211_STA_INFO_NONPEER_PM));
+#endif
+
+ return 0;
+}
+
+/**
+ * @add_mpath: add a fixed mesh path
+ */
+static int rwnx_cfg80211_add_mpath(struct wiphy *wiphy, struct net_device *dev,
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 16, 0))
+ u8 *dst,
+ u8 *next_hop
+#else
+ const u8 *dst,
+ const u8 *next_hop
+#endif
+)
+{
+ struct rwnx_vif *rwnx_vif = netdev_priv(dev);
+ struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy);
+ struct mesh_path_update_cfm cfm;
+
+ if (RWNX_VIF_TYPE(rwnx_vif) != NL80211_IFTYPE_MESH_POINT)
+ return -ENOTSUPP;
+
+ return rwnx_send_mesh_path_update_req(rwnx_hw, rwnx_vif, dst, next_hop, &cfm);
+}
+
+/**
+ * @del_mpath: delete a given mesh path
+ */
+static int rwnx_cfg80211_del_mpath(struct wiphy *wiphy, struct net_device *dev,
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 16, 0))
+ u8 *dst
+#else
+ const u8 *dst
+#endif
+)
+{
+ struct rwnx_vif *rwnx_vif = netdev_priv(dev);
+ struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy);
+ struct mesh_path_update_cfm cfm;
+
+ if (RWNX_VIF_TYPE(rwnx_vif) != NL80211_IFTYPE_MESH_POINT)
+ return -ENOTSUPP;
+
+ return rwnx_send_mesh_path_update_req(rwnx_hw, rwnx_vif, dst, NULL, &cfm);
+}
+
+/**
+ * @change_mpath: change a given mesh path
+ */
+static int rwnx_cfg80211_change_mpath(struct wiphy *wiphy, struct net_device *dev,
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 16, 0))
+ u8 *dst,
+ u8 *next_hop
+#else
+ const u8 *dst,
+ const u8 *next_hop
+#endif
+)
+{
+ struct rwnx_vif *rwnx_vif = netdev_priv(dev);
+ struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy);
+ struct mesh_path_update_cfm cfm;
+
+ if (RWNX_VIF_TYPE(rwnx_vif) != NL80211_IFTYPE_MESH_POINT)
+ return -ENOTSUPP;
+
+ return rwnx_send_mesh_path_update_req(rwnx_hw, rwnx_vif, dst, next_hop, &cfm);
+}
+
+/**
+ * @get_mpath: get a mesh path for the given parameters
+ */
+static int rwnx_cfg80211_get_mpath(struct wiphy *wiphy, struct net_device *dev,
+ u8 *dst, u8 *next_hop, struct mpath_info *pinfo)
+{
+ struct rwnx_vif *rwnx_vif = netdev_priv(dev);
+ struct rwnx_mesh_path *mesh_path = NULL;
+ struct rwnx_mesh_path *cur;
+
+ if (RWNX_VIF_TYPE(rwnx_vif) != NL80211_IFTYPE_MESH_POINT)
+ return -ENOTSUPP;
+
+ list_for_each_entry(cur, &rwnx_vif->ap.mpath_list, list) {
+ /* Compare the path target address and the provided destination address */
+ if (memcmp(dst, &cur->tgt_mac_addr, ETH_ALEN)) {
+ continue;
+ }
+
+ mesh_path = cur;
+ break;
+ }
+
+ if (mesh_path == NULL)
+ return -ENOENT;
+
+ /* Copy next HOP MAC address */
+ if (mesh_path->p_nhop_sta)
+ memcpy(next_hop, &mesh_path->p_nhop_sta->mac_addr, ETH_ALEN);
+
+ /* Fill path information */
+ pinfo->filled = 0;
+ pinfo->generation = rwnx_vif->ap.generation;
+
+ return 0;
+}
+
+/**
+ * @dump_mpath: dump mesh path callback -- resume dump at index @idx
+ */
+static int rwnx_cfg80211_dump_mpath(struct wiphy *wiphy, struct net_device *dev,
+ int idx, u8 *dst, u8 *next_hop,
+ struct mpath_info *pinfo)
+{
+ struct rwnx_vif *rwnx_vif = netdev_priv(dev);
+ struct rwnx_mesh_path *mesh_path = NULL;
+ struct rwnx_mesh_path *cur;
+ int i = 0;
+
+ if (RWNX_VIF_TYPE(rwnx_vif) != NL80211_IFTYPE_MESH_POINT)
+ return -ENOTSUPP;
+
+ list_for_each_entry(cur, &rwnx_vif->ap.mpath_list, list) {
+ if (i < idx) {
+ i++;
+ continue;
+ }
+
+ mesh_path = cur;
+ break;
+ }
+
+ if (mesh_path == NULL)
+ return -ENOENT;
+
+ /* Copy target and next hop MAC address */
+ memcpy(dst, &mesh_path->tgt_mac_addr, ETH_ALEN);
+ if (mesh_path->p_nhop_sta)
+ memcpy(next_hop, &mesh_path->p_nhop_sta->mac_addr, ETH_ALEN);
+
+ /* Fill path information */
+ pinfo->filled = 0;
+ pinfo->generation = rwnx_vif->ap.generation;
+
+ return 0;
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)
+/**
+ * @get_mpp: get a mesh proxy path for the given parameters
+ */
+static int rwnx_cfg80211_get_mpp(struct wiphy *wiphy, struct net_device *dev,
+ u8 *dst, u8 *mpp, struct mpath_info *pinfo)
+{
+ struct rwnx_vif *rwnx_vif = netdev_priv(dev);
+ struct rwnx_mesh_proxy *mesh_proxy = NULL;
+ struct rwnx_mesh_proxy *cur;
+
+ if (RWNX_VIF_TYPE(rwnx_vif) != NL80211_IFTYPE_MESH_POINT)
+ return -ENOTSUPP;
+
+ list_for_each_entry(cur, &rwnx_vif->ap.proxy_list, list) {
+ if (cur->local) {
+ continue;
+ }
+
+ /* Compare the path target address and the provided destination address */
+ if (memcmp(dst, &cur->ext_sta_addr, ETH_ALEN)) {
+ continue;
+ }
+
+ mesh_proxy = cur;
+ break;
+ }
+
+ if (mesh_proxy == NULL)
+ return -ENOENT;
+
+ memcpy(mpp, &mesh_proxy->proxy_addr, ETH_ALEN);
+
+ /* Fill path information */
+ pinfo->filled = 0;
+ pinfo->generation = rwnx_vif->ap.generation;
+
+ return 0;
+}
+
+/**
+ * @dump_mpp: dump mesh proxy path callback -- resume dump at index @idx
+ */
+static int rwnx_cfg80211_dump_mpp(struct wiphy *wiphy, struct net_device *dev,
+ int idx, u8 *dst, u8 *mpp, struct mpath_info *pinfo)
+{
+ struct rwnx_vif *rwnx_vif = netdev_priv(dev);
+ struct rwnx_mesh_proxy *mesh_proxy = NULL;
+ struct rwnx_mesh_proxy *cur;
+ int i = 0;
+
+ if (RWNX_VIF_TYPE(rwnx_vif) != NL80211_IFTYPE_MESH_POINT)
+ return -ENOTSUPP;
+
+ list_for_each_entry(cur, &rwnx_vif->ap.proxy_list, list) {
+ if (cur->local) {
+ continue;
+ }
+
+ if (i < idx) {
+ i++;
+ continue;
+ }
+
+ mesh_proxy = cur;
+ break;
+ }
+
+ if (mesh_proxy == NULL)
+ return -ENOENT;
+
+ /* Copy target MAC address */
+ memcpy(dst, &mesh_proxy->ext_sta_addr, ETH_ALEN);
+ memcpy(mpp, &mesh_proxy->proxy_addr, ETH_ALEN);
+
+ /* Fill path information */
+ pinfo->filled = 0;
+ pinfo->generation = rwnx_vif->ap.generation;
+
+ return 0;
+}
+#endif /* version >= 3.19 */
+
+/**
+ * @get_mesh_config: Get the current mesh configuration
+ */
+static int rwnx_cfg80211_get_mesh_config(struct wiphy *wiphy, struct net_device *dev,
+ struct mesh_config *conf)
+{
+ struct rwnx_vif *rwnx_vif = netdev_priv(dev);
+
+ if (RWNX_VIF_TYPE(rwnx_vif) != NL80211_IFTYPE_MESH_POINT)
+ return -ENOTSUPP;
+
+ return 0;
+}
+
+/**
+ * @update_mesh_config: Update mesh parameters on a running mesh.
+ */
+static int rwnx_cfg80211_update_mesh_config(struct wiphy *wiphy, struct net_device *dev,
+ u32 mask, const struct mesh_config *nconf)
+{
+ struct rwnx_vif *rwnx_vif = netdev_priv(dev);
+ struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy);
+ struct mesh_update_cfm cfm;
+ int status;
+
+ if (RWNX_VIF_TYPE(rwnx_vif) != NL80211_IFTYPE_MESH_POINT)
+ return -ENOTSUPP;
+
+ if (mask & CO_BIT(NL80211_MESHCONF_POWER_MODE - 1)) {
+ rwnx_vif->ap.next_mesh_pm = nconf->power_mode;
+
+ if (!list_empty(&rwnx_vif->ap.sta_list)) {
+ // If there are mesh links we don't want to update the power mode
+ // It will be updated with rwnx_update_mesh_power_mode() when the
+ // ps mode of a link is updated or when a new link is added/removed
+ mask &= ~BIT(NL80211_MESHCONF_POWER_MODE - 1);
+
+ if (!mask)
+ return 0;
+ }
+ }
+
+ status = rwnx_send_mesh_update_req(rwnx_hw, rwnx_vif, mask, nconf, &cfm);
+
+ if (!status && (cfm.status != 0))
+ status = -EINVAL;
+
+ return status;
+}
+
+/**
+ * @join_mesh: join the mesh network with the specified parameters
+ * (invoked with the wireless_dev mutex held)
+ */
+static int rwnx_cfg80211_join_mesh(struct wiphy *wiphy, struct net_device *dev,
+ const struct mesh_config *conf, const struct mesh_setup *setup)
+{
+ struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy);
+ struct rwnx_vif *rwnx_vif = netdev_priv(dev);
+ struct mesh_start_cfm mesh_start_cfm;
+ int error = 0;
+ u8 txq_status = 0;
+ /* STA for BC/MC traffic */
+ struct rwnx_sta *sta;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ if (RWNX_VIF_TYPE(rwnx_vif) != NL80211_IFTYPE_MESH_POINT)
+ return -ENOTSUPP;
+
+ /* Forward the information to the UMAC */
+ if ((error = rwnx_send_mesh_start_req(rwnx_hw, rwnx_vif, conf, setup, &mesh_start_cfm))) {
+ return error;
+ }
+
+ /* Check the status */
+ switch (mesh_start_cfm.status) {
+ case CO_OK:
+ rwnx_vif->ap.bcmc_index = mesh_start_cfm.bcmc_idx;
+ rwnx_vif->ap.flags = 0;
+ rwnx_vif->use_4addr = true;
+ rwnx_vif->user_mpm = setup->user_mpm;
+
+ sta = &rwnx_hw->sta_table[mesh_start_cfm.bcmc_idx];
+ sta->valid = true;
+ sta->aid = 0;
+ sta->sta_idx = mesh_start_cfm.bcmc_idx;
+ sta->ch_idx = mesh_start_cfm.ch_idx;
+ sta->vif_idx = rwnx_vif->vif_index;
+ sta->qos = true;
+ sta->acm = 0;
+ sta->ps.active = false;
+ rwnx_mu_group_sta_init(sta, NULL);
+ spin_lock_bh(&rwnx_hw->cb_lock);
+ rwnx_chanctx_link(rwnx_vif, mesh_start_cfm.ch_idx,
+ (struct cfg80211_chan_def *)(&setup->chandef));
+ if (rwnx_hw->cur_chanctx != mesh_start_cfm.ch_idx) {
+ txq_status = RWNX_TXQ_STOP_CHAN;
+ }
+ rwnx_txq_vif_init(rwnx_hw, rwnx_vif, txq_status);
+ spin_unlock_bh(&rwnx_hw->cb_lock);
+
+ netif_tx_start_all_queues(dev);
+ netif_carrier_on(dev);
+
+ /* If the AP channel is already the active, we probably skip radar
+ activation on MM_CHANNEL_SWITCH_IND (unless another vif use this
+ ctxt). In anycase retest if radar detection must be activated
+ */
+ if (rwnx_hw->cur_chanctx == mesh_start_cfm.ch_idx) {
+ rwnx_radar_detection_enable_on_cur_channel(rwnx_hw);
+ }
+ break;
+
+ case CO_BUSY:
+ error = -EINPROGRESS;
+ break;
+
+ default:
+ error = -EIO;
+ break;
+ }
+
+ /* Print information about the operation */
+ if (error) {
+ netdev_info(dev, "Failed to start MP (%d)", error);
+ } else {
+ netdev_info(dev, "MP started: ch=%d, bcmc_idx=%d",
+ rwnx_vif->ch_index, rwnx_vif->ap.bcmc_index);
+ }
+
+ return error;
+}
+
+/**
+ * @leave_mesh: leave the current mesh network
+ * (invoked with the wireless_dev mutex held)
+ */
+static int rwnx_cfg80211_leave_mesh(struct wiphy *wiphy, struct net_device *dev)
+{
+ struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy);
+ struct rwnx_vif *rwnx_vif = netdev_priv(dev);
+ struct mesh_stop_cfm mesh_stop_cfm;
+ int error = 0;
+
+ error = rwnx_send_mesh_stop_req(rwnx_hw, rwnx_vif, &mesh_stop_cfm);
+
+ if (error == 0) {
+ /* Check the status */
+ switch (mesh_stop_cfm.status) {
+ case CO_OK:
+ spin_lock_bh(&rwnx_hw->cb_lock);
+ rwnx_chanctx_unlink(rwnx_vif);
+ rwnx_radar_cancel_cac(&rwnx_hw->radar);
+ spin_unlock_bh(&rwnx_hw->cb_lock);
+ /* delete BC/MC STA */
+ rwnx_txq_vif_deinit(rwnx_hw, rwnx_vif);
+ rwnx_del_bcn(&rwnx_vif->ap.bcn);
+
+ netif_tx_stop_all_queues(dev);
+ netif_carrier_off(dev);
+
+ break;
+
+ default:
+ error = -EIO;
+ break;
+ }
+ }
+
+ if (error) {
+ netdev_info(dev, "Failed to stop MP");
+ } else {
+ netdev_info(dev, "MP Stopped");
+ }
+
+ return 0;
+}
+
+static struct cfg80211_ops rwnx_cfg80211_ops = {
+ .add_virtual_intf = rwnx_cfg80211_add_iface,
+ .del_virtual_intf = rwnx_cfg80211_del_iface,
+ .change_virtual_intf = rwnx_cfg80211_change_iface,
+ .start_p2p_device = rwnx_cfgp2p_start_p2p_device,
+ .stop_p2p_device = rwnx_cfgp2p_stop_p2p_device,
+ .scan = rwnx_cfg80211_scan,
+ .connect = rwnx_cfg80211_connect,
+ .disconnect = rwnx_cfg80211_disconnect,
+ .add_key = rwnx_cfg80211_add_key,
+ .get_key = rwnx_cfg80211_get_key,
+ .del_key = rwnx_cfg80211_del_key,
+ .set_default_key = rwnx_cfg80211_set_default_key,
+ .set_default_mgmt_key = rwnx_cfg80211_set_default_mgmt_key,
+ .add_station = rwnx_cfg80211_add_station,
+ .del_station = rwnx_cfg80211_del_station_compat,
+ .change_station = rwnx_cfg80211_change_station,
+ .mgmt_tx = rwnx_cfg80211_mgmt_tx,
+ .start_ap = rwnx_cfg80211_start_ap,
+ .change_beacon = rwnx_cfg80211_change_beacon,
+ .stop_ap = rwnx_cfg80211_stop_ap,
+ .set_monitor_channel = rwnx_cfg80211_set_monitor_channel,
+ .probe_client = rwnx_cfg80211_probe_client,
+// .mgmt_frame_register = rwnx_cfg80211_mgmt_frame_register,
+ .set_wiphy_params = rwnx_cfg80211_set_wiphy_params,
+ .set_txq_params = rwnx_cfg80211_set_txq_params,
+ .set_tx_power = rwnx_cfg80211_set_tx_power,
+// .get_tx_power = rwnx_cfg80211_get_tx_power,
+ .set_power_mgmt = rwnx_cfg80211_set_power_mgmt,
+ .get_station = rwnx_cfg80211_get_station,
+ .remain_on_channel = rwnx_cfg80211_remain_on_channel,
+ .cancel_remain_on_channel = rwnx_cfg80211_cancel_remain_on_channel,
+ .dump_survey = rwnx_cfg80211_dump_survey,
+ .get_channel = rwnx_cfg80211_get_channel,
+ .start_radar_detection = rwnx_cfg80211_start_radar_detection,
+ .update_ft_ies = rwnx_cfg80211_update_ft_ies,
+ .set_cqm_rssi_config = rwnx_cfg80211_set_cqm_rssi_config,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)
+ .channel_switch = rwnx_cfg80211_channel_switch,
+#endif
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)
+ //.tdls_channel_switch = rwnx_cfg80211_tdls_channel_switch,
+ //.tdls_cancel_channel_switch = rwnx_cfg80211_tdls_cancel_channel_switch,
+#endif
+ //.tdls_mgmt = rwnx_cfg80211_tdls_mgmt,
+ //.tdls_oper = rwnx_cfg80211_tdls_oper,
+ .change_bss = rwnx_cfg80211_change_bss,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0) || defined(CONFIG_WPA3_FOR_OLD_KERNEL)
+ .external_auth = rwnx_cfg80211_external_auth,
+#endif
+#ifdef CONFIG_SCHED_SCAN
+ .sched_scan_start = rwnx_cfg80211_sched_scan_start,
+ .sched_scan_stop = rwnx_cfg80211_sched_scan_stop,
+#endif
+
+};
+
+
+/*********************************************************************
+ * Init/Exit functions
+ *********************************************************************/
+static void rwnx_wdev_unregister(struct rwnx_hw *rwnx_hw)
+{
+ struct rwnx_vif *rwnx_vif, *tmp;
+
+ rtnl_lock();
+ list_for_each_entry_safe(rwnx_vif, tmp, &rwnx_hw->vifs, list) {
+ #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)
+ rwnx_cfg80211_del_iface(rwnx_hw->wiphy, &rwnx_vif->wdev);
+ #else
+ rwnx_cfg80211_del_iface(rwnx_hw->wiphy, rwnx_vif->ndev);
+ #endif
+ }
+ rtnl_unlock();
+}
+
+static void rwnx_set_vers(struct rwnx_hw *rwnx_hw)
+{
+ u32 vers = rwnx_hw->version_cfm.version_lmac;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ snprintf(rwnx_hw->wiphy->fw_version,
+ sizeof(rwnx_hw->wiphy->fw_version), "%d.%d.%d.%d",
+ (vers & (0xff << 24)) >> 24, (vers & (0xff << 16)) >> 16,
+ (vers & (0xff << 8)) >> 8, (vers & (0xff << 0)) >> 0);
+}
+
+static void rwnx_reg_notifier(struct wiphy *wiphy,
+ struct regulatory_request *request)
+{
+ struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy);
+
+ printk("%s Enter\r\n", __func__);
+
+ // For now trust all initiator
+ rwnx_radar_set_domain(&rwnx_hw->radar, request->dfs_region);
+
+ if (rwnx_hw->usbdev->chipid == PRODUCT_ID_AIC8801 ||
+ ((rwnx_hw->usbdev->chipid == PRODUCT_ID_AIC8800DC ||
+ rwnx_hw->usbdev->chipid == PRODUCT_ID_AIC8800DW ||
+ rwnx_hw->usbdev->chipid == PRODUCT_ID_AIC8800D81) && testmode == 0)){
+ rwnx_send_me_chan_config_req(rwnx_hw);
+ }
+}
+
+static void rwnx_enable_mesh(struct rwnx_hw *rwnx_hw)
+{
+ struct wiphy *wiphy = rwnx_hw->wiphy;
+
+ if (!rwnx_mod_params.mesh)
+ return;
+
+ rwnx_cfg80211_ops.get_station = rwnx_cfg80211_get_station;
+ rwnx_cfg80211_ops.dump_station = rwnx_cfg80211_dump_station;
+ rwnx_cfg80211_ops.add_mpath = rwnx_cfg80211_add_mpath;
+ rwnx_cfg80211_ops.del_mpath = rwnx_cfg80211_del_mpath;
+ rwnx_cfg80211_ops.change_mpath = rwnx_cfg80211_change_mpath;
+ rwnx_cfg80211_ops.get_mpath = rwnx_cfg80211_get_mpath;
+ rwnx_cfg80211_ops.dump_mpath = rwnx_cfg80211_dump_mpath;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)
+ rwnx_cfg80211_ops.get_mpp = rwnx_cfg80211_get_mpp;
+ rwnx_cfg80211_ops.dump_mpp = rwnx_cfg80211_dump_mpp;
+#endif
+ rwnx_cfg80211_ops.get_mesh_config = rwnx_cfg80211_get_mesh_config;
+ rwnx_cfg80211_ops.update_mesh_config = rwnx_cfg80211_update_mesh_config;
+ rwnx_cfg80211_ops.join_mesh = rwnx_cfg80211_join_mesh;
+ rwnx_cfg80211_ops.leave_mesh = rwnx_cfg80211_leave_mesh;
+
+ wiphy->flags |= (WIPHY_FLAG_MESH_AUTH | WIPHY_FLAG_IBSS_RSN);
+ wiphy->features |= NL80211_FEATURE_USERSPACE_MPM;
+ wiphy->interface_modes |= BIT(NL80211_IFTYPE_MESH_POINT);
+
+ rwnx_limits[0].types |= BIT(NL80211_IFTYPE_MESH_POINT);
+ rwnx_limits_dfs[0].types |= BIT(NL80211_IFTYPE_MESH_POINT);
+}
+
+extern int rwnx_init_aic(struct rwnx_hw *rwnx_hw);
+#ifdef AICWF_USB_SUPPORT
+u32 patch_tbl[][2] =
+{
+ {0x0044, 0x00000002}, //hosttype
+ {0x0048, 0x00000060},
+ {0x004c, 0x00000046},
+ {0x0050, 0x00000000}, //ipc base
+ {0x0054, 0x001a0000}, //buf base
+ {0x0058, 0x001a0140}, //desc base
+ {0x005c, 0x00001020}, //desc size
+ {0x0060, 0x001a1020}, //pkt base
+ {0x0064, 0x000207e0}, //pkt size
+ {0x0068, 0x00000008},
+ {0x006c, 0x00000040},
+ {0x0070, 0x00000040},
+ {0x0074, 0x00000020},
+ {0x0078, 0x00000000},
+ {0x007c, 0x00000040},
+ {0x0080, 0x00190000},
+ {0x0084, 0x0000fc00},//63kB
+ {0x0088, 0x0019fc00},
+ #ifdef USE_5G
+ {0x00b4, 0xf3010001},
+ #else
+ {0x00b4, 0xf3010000},
+ #endif
+ //{0x00b8, 0x0f010a01}, //tx enhanced en, tx enhanced lo rate
+ //{0x00bc, 0x00000008}, //tx enhanced hi rate
+};
+#else
+//#ifdef CONFIG_ROM_PATCH_EN
+#if 0
+u32 patch_tbl[][2] =
+{
+ {0x0044, 0x02000001},
+ {0x0058, 0x001a0000},
+ {0x005c, 0x00002020},
+ {0x0060, 0x001a2020}, //pkt base
+ {0x0064, 0x00021820}, //pkt size
+ {0x0080, 0x00190000},
+ {0x0084, 0x0000fc00},//63kB
+ {0x0088, 0x0019fc00}
+};
+#else
+u32 patch_tbl[][2] =
+{
+ #ifdef USE_5G
+ {0x00b4, 0xf3010001},
+ #else
+ {0x00b4, 0xf3010000},
+ #endif
+};
+#endif
+#endif
+
+u32 patch_tbl_1[14][2] =
+{
+ {0x171b24, 0x1c4021}, //61
+ {0x171c00, 0x1c40b1}, //116
+ {0x172124, 0x1c43ed}, //12*8 + 1720c4
+ {0x171bfc, 0x1c4849}, //115, 171a30 + 115 * 4
+ {0x171ee4, 0x1c4941}, //301
+ {0x171ee8, 0x1c4b09}, //302
+ {0x172134, 0x1c4d65}, //14/15/16/17/18 * 8 + 1720c4{0x172134, 0x1c4d65},
+ {0x17213c, 0x1c4d65},
+ {0x172144, 0x1c4d65},
+ {0x17214c, 0x1c4d65},
+ {0x172154, 0x1c4d65},
+ {0x1721d0, 0x1c53dd}, // 1721c4 + 1*8 + 4
+ {0x1721f0, 0x1c5415}, // 1721c4 + 5*8 + 4
+ {0x171eb0, 0x1c54a1}, // 288
+};
+
+u32 func_tbl[1721] =
+{
+ 0x8cc88cc3,
+ 0xd8084283,
+ 0x1ac0d205,
+ 0xbfcc283f,
+ 0x20012000,
+ 0x20014770,
+ 0x20004770,
+ 0xbf004770,
+ 0x481d4a1c,
+ 0xb538491d,
+ 0x68056913,
+ 0xf8b16914,
+ 0xf8b100b0,
+ 0xf5a310b2,
+ 0xeb0363fa,
+ 0x1b1b1345,
+ 0x1a5b1a1b,
+ 0xdb1a2b00,
+ 0x681b4b15,
+ 0x68dcb143,
+ 0x1ae36913,
+ 0x63faf5a3,
+ 0x1a5b1a1b,
+ 0xdb012b00,
+ 0xbd382001,
+ 0x681b4b0f,
+ 0x3000f9b3,
+ 0xda062b00,
+ 0x1ae46913,
+ 0x549cf504,
+ 0x2c003408,
+ 0x2000db01,
+ 0x4909bd38,
+ 0xf44f4809,
+ 0xf66b7202,
+ 0x2000fad9,
+ 0xbf00bd38,
+ 0x40501000,
+ 0x40328040,
+ 0x0017192c,
+ 0x00178bf0,
+ 0x00173250,
+ 0x001c5a3c,
+ 0x001c56b4,
+ 0x4ff0e92d,
+ 0x8c05461c,
+ 0x3062f893,
+ 0x9020f8d2,
+ 0xf8d06987,
+ 0xb089b01c,
+ 0x02ad4616,
+ 0x2012e9dd,
+ 0xf8b4b9d3,
+ 0xf1bcc068,
+ 0xd0150f00,
+ 0x6ab38b52,
+ 0xf202fb0c,
+ 0x07189205,
+ 0xf20cfb05,
+ 0xd0199206,
+ 0xebb72300,
+ 0xf44f0a09,
+ 0x930776fa,
+ 0xf5039b05,
+ 0xe9c473c8,
+ 0xe071a31f,
+ 0xf0402800,
+ 0x950680ba,
+ 0x0a01f04f,
+ 0x6ab28b50,
+ 0xf000fb0a,
+ 0x90050712,
+ 0x8122f040,
+ 0xf8df6af2,
+ 0xf8dfc2bc,
+ 0xf8dc82bc,
+ 0x48983000,
+ 0x1203f3c2,
+ 0x037ff023,
+ 0x2002f818,
+ 0xf8cc4313,
+ 0x6ab33000,
+ 0xf3c34a93,
+ 0xea4113c0,
+ 0xf04f5103,
+ 0x60014300,
+ 0xf3bf6013,
+ 0xbf008f4f,
+ 0x00586813,
+ 0x4b8dd5fc,
+ 0xf9b16819,
+ 0x29001000,
+ 0x80aef2c0,
+ 0x68124a88,
+ 0xfa82fa1f,
+ 0x07126ab2,
+ 0x809df040,
+ 0xf8df6af2,
+ 0x4882c25c,
+ 0x1000f8dc,
+ 0x1203f3c2,
+ 0x017ff021,
+ 0x2002f818,
+ 0xf8cc430a,
+ 0x6ab22000,
+ 0xf3c2497c,
+ 0x051212c0,
+ 0x0218f042,
+ 0x4600f04f,
+ 0x600e6002,
+ 0x8f4ff3bf,
+ 0x680abf00,
+ 0xd5fc0056,
+ 0xf9b3681b,
+ 0x2b003000,
+ 0x4a72db6e,
+ 0x3062f894,
+ 0x22006816,
+ 0xebaab2b6,
+ 0x92070a06,
+ 0x0909ebb7,
+ 0x0a0aeb19,
+ 0xd0872b00,
+ 0x79e5ea4f,
+ 0x464b462a,
+ 0x46594638,
+ 0xfc50f679,
+ 0x92021bba,
+ 0xfb009a07,
+ 0xeb6bf309,
+ 0xfb050202,
+ 0x92033301,
+ 0x0105fba0,
+ 0xe9dd4419,
+ 0x42992302,
+ 0x4290bf08,
+ 0xe9cdbf38,
+ 0x4b5e0102,
+ 0x6702e9dd,
+ 0x9b066819,
+ 0x018918f6,
+ 0x9905d430,
+ 0xf5a11a71,
+ 0x4b597ac8,
+ 0x69194a59,
+ 0x691b6812,
+ 0x44511a89,
+ 0xf5a31acb,
+ 0x3b1453a5,
+ 0x6a632b00,
+ 0x1949bfb8,
+ 0xd022428b,
+ 0x6a1a4b52,
+ 0xd04142a2,
+ 0xf1044d51,
+ 0xf8d50018,
+ 0x479831e0,
+ 0x30a8f8d5,
+ 0xb0094620,
+ 0x4ff0e8bd,
+ 0xf8904718,
+ 0xf1baa002,
+ 0xd1010f00,
+ 0xa003f890,
+ 0xf00afb05,
+ 0xe73d9006,
+ 0x98054946,
+ 0xeba11a09,
+ 0x44b20a0a,
+ 0xb009e7cb,
+ 0x8ff0e8bd,
+ 0x0058680b,
+ 0x4941d48d,
+ 0xf44f4841,
+ 0xf66b72ae,
+ 0x2200f98f,
+ 0x3062f894,
+ 0xf5aa9207,
+ 0xf44f7afa,
+ 0xe78776fa,
+ 0x00516812,
+ 0xaf4ef53f,
+ 0x48384937,
+ 0x72aef44f,
+ 0xf97cf66b,
+ 0x7afaf44f,
+ 0xe7474b2c,
+ 0x62614a2c,
+ 0x01926812,
+ 0x4a32d4b8,
+ 0x68124d32,
+ 0x1024f893,
+ 0x4207f3c2,
+ 0x2d23f885,
+ 0x4a2fbb61,
+ 0x6812492f,
+ 0x20016809,
+ 0xf885b2d2,
+ 0xf8832d24,
+ 0x780b0024,
+ 0xd1054283,
+ 0x4a2b492a,
+ 0x6813600b,
+ 0x60134303,
+ 0x2d23f895,
+ 0x3d24f895,
+ 0xd21b429a,
+ 0x681b4b17,
+ 0x3000f9b3,
+ 0xda0d2b00,
+ 0x2d23f895,
+ 0x3d24f895,
+ 0xd307429a,
+ 0x48214920,
+ 0xf44f4d15,
+ 0xf66b7221,
+ 0xe787f96f,
+ 0xe7854d12,
+ 0xf44f2200,
+ 0x920776fa,
+ 0xe7354692,
+ 0x4b124814,
+ 0x1d23f895,
+ 0x2d24f895,
+ 0x6806681b,
+ 0xb2f64816,
+ 0x96000c1b,
+ 0xff82f66a,
+ 0xbf00e7d4,
+ 0x40328160,
+ 0x4032816c,
+ 0x00173250,
+ 0x4032004c,
+ 0x40501000,
+ 0x403280a4,
+ 0x00178d3c,
+ 0x00171a30,
+ 0xfffffe70,
+ 0x001c5a20,
+ 0x001c56e0,
+ 0x40328044,
+ 0x001e4000,
+ 0x40320090,
+ 0x00173270,
+ 0x40328070,
+ 0x40328074,
+ 0x001c5a58,
+ 0x001c5718,
+ 0x001c5704,
+ 0x40328164,
+ 0x00041830,
+ 0x4ff0e92d,
+ 0x9354f8df,
+ 0xb020f8d9,
+ 0xf44fb083,
+ 0xf6692000,
+ 0xf1bbfd9b,
+ 0xf0000f00,
+ 0x4cb480f1,
+ 0xa33cf8df,
+ 0x4fb37fa1,
+ 0x8338f8df,
+ 0x29004eb2,
+ 0x80e6f000,
+ 0xd50e0708,
+ 0xf8db4bb0,
+ 0x681b0070,
+ 0x2010f8da,
+ 0x4403685b,
+ 0x2b001a9b,
+ 0x80c7f2c0,
+ 0x01f7f001,
+ 0x074a77a1,
+ 0x8095f100,
+ 0xd515078b,
+ 0x3004f8db,
+ 0x0302f023,
+ 0x3004f8cb,
+ 0x301df899,
+ 0xd1082b05,
+ 0x48a34da2,
+ 0x31d8f8d5,
+ 0x23004798,
+ 0xf8897fa1,
+ 0xf001301d,
+ 0x77a101fd,
+ 0xd52d07cd,
+ 0x01586833,
+ 0x809ff140,
+ 0x0c096831,
+ 0x7f7cf411,
+ 0x80a6f000,
+ 0x4b983910,
+ 0x01fff001,
+ 0x1281eb01,
+ 0x03c2eb03,
+ 0x2021f893,
+ 0xf0002a00,
+ 0x4a93809d,
+ 0x68137f99,
+ 0xf9b34a92,
+ 0xf44f3000,
+ 0xfb0060a4,
+ 0x2b002201,
+ 0xf2c09200,
+ 0x683b809c,
+ 0x2fe0f413,
+ 0x80a5f000,
+ 0xf0017fa1,
+ 0x77a101fe,
+ 0xd59e068a,
+ 0x49894e88,
+ 0xf3c56835,
+ 0x463a1741,
+ 0xf66a2002,
+ 0x2300ff23,
+ 0x1547f3c5,
+ 0x3078f88b,
+ 0xf0402f00,
+ 0x4e7b80af,
+ 0x4d824a81,
+ 0xf4236813,
+ 0x60137300,
+ 0x3004f8db,
+ 0x21d8f8d6,
+ 0x0301f023,
+ 0x3004f8cb,
+ 0x0028f10b,
+ 0x682a4790,
+ 0x2b027813,
+ 0x8114f000,
+ 0x680b4974,
+ 0x0301f023,
+ 0x7813600b,
+ 0xd1122b01,
+ 0x41fff501,
+ 0x4a733134,
+ 0x3010f8da,
+ 0xf8b26808,
+ 0xf8d610b2,
+ 0xeb0321e0,
+ 0x1a591340,
+ 0x0018f10b,
+ 0x682b4790,
+ 0x2b02781b,
+ 0x80b4f000,
+ 0xf0017fa1,
+ 0x77a101df,
+ 0x4968e74f,
+ 0x20024d5d,
+ 0xfedcf66a,
+ 0x68634966,
+ 0xf022680a,
+ 0x600a0204,
+ 0xf4436822,
+ 0x431a7300,
+ 0xf8d5614a,
+ 0x60631498,
+ 0x0063f89b,
+ 0x3494f8d5,
+ 0x4798465a,
+ 0x7fa1683b,
+ 0x737cf023,
+ 0xf8d8603b,
+ 0xf0013000,
+ 0xf44301fb,
+ 0xf8c80380,
+ 0x77a13000,
+ 0x4856e742,
+ 0xfe66f66a,
+ 0x2200e782,
+ 0x006cf89b,
+ 0xf6584611,
+ 0xb160fd17,
+ 0xe72f7fa1,
+ 0xf66a4850,
+ 0xe775fe59,
+ 0xf66a484f,
+ 0xe771fe55,
+ 0xe8bdb003,
+ 0xf8da8ff0,
+ 0x7fa13010,
+ 0x3070f8cb,
+ 0x4593e71e,
+ 0xaf61f43f,
+ 0x48494948,
+ 0x22e6f240,
+ 0xf818f66b,
+ 0xf413683b,
+ 0xf47f2fe0,
+ 0x6832af5b,
+ 0x49446833,
+ 0xf3c30fd0,
+ 0x46057380,
+ 0x20024602,
+ 0xf66a9301,
+ 0x9b01fe81,
+ 0x0205ea53,
+ 0xf43f4628,
+ 0x4d2baf49,
+ 0x46199a00,
+ 0x323cf8d5,
+ 0x68334798,
+ 0x4300f023,
+ 0x68336033,
+ 0x4380f023,
+ 0xe7396033,
+ 0x2010f8da,
+ 0x529cf502,
+ 0x32084631,
+ 0xf8dae005,
+ 0x1ad33010,
+ 0xf2c02b00,
+ 0x680b80b2,
+ 0xd5f6071b,
+ 0x8080f8df,
+ 0x3000f8d8,
+ 0x0308f023,
+ 0x3000f8c8,
+ 0x3000f8d8,
+ 0xd51206de,
+ 0x49274e15,
+ 0xf66a2002,
+ 0xf8d6fe4b,
+ 0xf005323c,
+ 0x08780101,
+ 0x4798465a,
+ 0x3000f8d8,
+ 0x0310f023,
+ 0x3000f8c8,
+ 0x4b1fe722,
+ 0x4e0b491f,
+ 0x601d2501,
+ 0xf66a2002,
+ 0x4b1dfe35,
+ 0xe717601d,
+ 0x22304b1c,
+ 0xf657601a,
+ 0xe745fddb,
+ 0x00178bb8,
+ 0x40320074,
+ 0x40320070,
+ 0x00173244,
+ 0x00171a30,
+ 0x00178d48,
+ 0x00175aa8,
+ 0x00173250,
+ 0x00177738,
+ 0x4032008c,
+ 0x001c57ec,
+ 0x4033b390,
+ 0x00173270,
+ 0x0017192c,
+ 0x001c578c,
+ 0x4032004c,
+ 0x001c5790,
+ 0x001c57a8,
+ 0x001c57bc,
+ 0x001c5a70,
+ 0x001c57d0,
+ 0x001c57e0,
+ 0x001c580c,
+ 0x40328564,
+ 0x001c5818,
+ 0x40328568,
+ 0x40320038,
+ 0x00178d3c,
+ 0x40501000,
+ 0x4032006c,
+ 0x8310f3ef,
+ 0xd40307d8,
+ 0x4b31b672,
+ 0x601a2201,
+ 0x4f314b30,
+ 0x3201681a,
+ 0x2100601a,
+ 0x6039683a,
+ 0x080ff002,
+ 0x2010f8da,
+ 0x46164631,
+ 0xf247460a,
+ 0xe0045030,
+ 0x1010f8da,
+ 0x42811b89,
+ 0x6839d81b,
+ 0xd1f70709,
+ 0x49264825,
+ 0xc000f8d0,
+ 0x4616680f,
+ 0x2010f8da,
+ 0x0f00f1b8,
+ 0x4a22d11a,
+ 0x60112104,
+ 0xb132681a,
+ 0x3a01491a,
+ 0x680b601a,
+ 0xb103b90a,
+ 0x682ab662,
+ 0x491ce6b0,
+ 0x20029200,
+ 0xfdb0f66a,
+ 0x9a004b14,
+ 0x4919e7d3,
+ 0xf66a2002,
+ 0xe74bfda9,
+ 0x070cea07,
+ 0xd4e0077f,
+ 0x1600e9cd,
+ 0x46164680,
+ 0x077ae001,
+ 0x9a00d412,
+ 0x7000f8d8,
+ 0xf8da6812,
+ 0xf2471010,
+ 0x1b895030,
+ 0xea074281,
+ 0xd9f00702,
+ 0x2002490b,
+ 0xfd8cf66a,
+ 0xe7ea4b02,
+ 0xe7c49e01,
+ 0x0017569c,
+ 0x00172600,
+ 0x40320038,
+ 0x4032806c,
+ 0x40328074,
+ 0x40328070,
+ 0x001c5828,
+ 0x001c57f8,
+ 0x001c5834,
+ 0xf890b5f8,
+ 0x2b003064,
+ 0xf890d03a,
+ 0x4604308a,
+ 0xd1362b00,
+ 0xf8944e32,
+ 0x4a32306c,
+ 0x6a674932,
+ 0xeb036a09,
+ 0xeb021383,
+ 0x42a103c3,
+ 0x443d685d,
+ 0xf8d6d044,
+ 0x462931e0,
+ 0x0018f104,
+ 0x46204798,
+ 0xfafaf65e,
+ 0x1080f8d4,
+ 0x3214f8d6,
+ 0x46204439,
+ 0xf8d64798,
+ 0x462a30a4,
+ 0x46204639,
+ 0xb9784798,
+ 0x3078f894,
+ 0x49216862,
+ 0xb2db3301,
+ 0x0201f042,
+ 0xf8846809,
+ 0x60623078,
+ 0x4293780a,
+ 0xd029d811,
+ 0x3b01bdf8,
+ 0x2b01b2db,
+ 0x308af880,
+ 0x2b02d912,
+ 0xd1c04e13,
+ 0x0063f890,
+ 0x31c0f8d6,
+ 0x47982100,
+ 0xf8d6e7b9,
+ 0xf8941164,
+ 0x4622006c,
+ 0x40f8e8bd,
+ 0xbb84f658,
+ 0x40f8e8bd,
+ 0xbb3af65e,
+ 0x62654b0c,
+ 0x30b5f893,
+ 0xd1ba2b00,
+ 0x681b4b0a,
+ 0x2b02781b,
+ 0xe7b4d1af,
+ 0xe8bd4620,
+ 0xf66540f8,
+ 0xbf00b8d1,
+ 0x00171a30,
+ 0x00175aa8,
+ 0x00178d3c,
+ 0x00173244,
+ 0x0017192c,
+ 0x00173270,
+ 0x4c5cb538,
+ 0x68224b5c,
+ 0xf002495c,
+ 0x701a020f,
+ 0xf66a2002,
+ 0x6823fcef,
+ 0xd02e0718,
+ 0x4a594958,
+ 0x2000680b,
+ 0x4300f023,
+ 0x6020600b,
+ 0x07596813,
+ 0x4b55d5fc,
+ 0x4a554952,
+ 0x60182004,
+ 0xf043680b,
+ 0x600b4300,
+ 0xf0436813,
+ 0x60136300,
+ 0x20024950,
+ 0xfcd0f66a,
+ 0x4a4f4b47,
+ 0x494f4847,
+ 0x601c2420,
+ 0x24016813,
+ 0x0310f043,
+ 0x70446013,
+ 0x781b680b,
+ 0xd0382b03,
+ 0xd0062b01,
+ 0x4a44bd38,
+ 0xf0236813,
+ 0x60136300,
+ 0x4a45e7e2,
+ 0x035b6813,
+ 0x4a44d5fc,
+ 0x311c6911,
+ 0x1acb6913,
+ 0xdafb2b00,
+ 0x681c4b41,
+ 0x4b41b174,
+ 0xf9b3681b,
+ 0x2b003000,
+ 0x4a3fdb4e,
+ 0xf8b268e3,
+ 0x4a3a10b2,
+ 0x21041a5b,
+ 0x60916313,
+ 0x4a3c493b,
+ 0xf443680b,
+ 0x600b4300,
+ 0xf4436d13,
+ 0x65134300,
+ 0xf0236dd3,
+ 0xf0430303,
+ 0xf0434300,
+ 0x65d30301,
+ 0x4b2fbd38,
+ 0xb174681c,
+ 0x681b4b2e,
+ 0x3000f9b3,
+ 0xdb152b00,
+ 0x68e34a2c,
+ 0x10b2f8b2,
+ 0x1a5b4a27,
+ 0x63132104,
+ 0x4b2b6091,
+ 0xf042681a,
+ 0x601a0201,
+ 0x075a681b,
+ 0x4b28d5ae,
+ 0x7200f44f,
+ 0xbd38601a,
+ 0x4d214b1e,
+ 0x68e3691a,
+ 0x10b2f8b5,
+ 0x1a521a9a,
+ 0xdae32a00,
+ 0x48224921,
+ 0x32dbf240,
+ 0xfddef66a,
+ 0xf8b568e3,
+ 0xe7d910b2,
+ 0x69124d17,
+ 0xf8b568e3,
+ 0x1a9a10b2,
+ 0x2a001a52,
+ 0x4918daab,
+ 0xf2404818,
+ 0xf66a32e9,
+ 0x68e3fdcb,
+ 0x10b2f8b5,
+ 0xbf00e7a1,
+ 0x40320038,
+ 0x00172604,
+ 0x001c5844,
+ 0x40328074,
+ 0x4032806c,
+ 0x40328070,
+ 0x40328048,
+ 0x001c584c,
+ 0x40580010,
+ 0x00173274,
+ 0x40041020,
+ 0x40501000,
+ 0x00178bf0,
+ 0x00173250,
+ 0x0017192c,
+ 0x40240014,
+ 0x40506000,
+ 0x40044084,
+ 0x40044100,
+ 0x001c5a8c,
+ 0x001c5854,
+ 0xf890b380,
+ 0xb36b3064,
+ 0x47f0e92d,
+ 0x4a7b4b7a,
+ 0xf8df4e7b,
+ 0xf8df920c,
+ 0x4d7a8240,
+ 0x460c487a,
+ 0x2702497a,
+ 0xc470f8d1,
+ 0x7180f8c3,
+ 0x49786892,
+ 0xc044f8c2,
+ 0x601f2201,
+ 0xf8d97032,
+ 0x680b2010,
+ 0xf0236072,
+ 0x600b0302,
+ 0x1000f8d8,
+ 0xc0b2f8b5,
+ 0x68018f8b,
+ 0xebb34463,
+ 0xea4f1f41,
+ 0xd3021a41,
+ 0x87f0e8bd,
+ 0x496b4770,
+ 0xf66a4638,
+ 0xf8d9fbdf,
+ 0x68f32010,
+ 0x2b001a9b,
+ 0xf2804b67,
+ 0xf89380a2,
+ 0xf8932d23,
+ 0x2a012d24,
+ 0xf893d90a,
+ 0x68612d23,
+ 0xf0002a00,
+ 0xf893809e,
+ 0x3a012d23,
+ 0xaa02fb01,
+ 0x3d23f893,
+ 0x4652495d,
+ 0xf66a2002,
+ 0xf8d8fbbf,
+ 0xf8b53000,
+ 0x8f9a00b2,
+ 0x4b594955,
+ 0xc178f8df,
+ 0x680b691c,
+ 0xebaa4402,
+ 0xf0220202,
+ 0xf0030703,
+ 0x433b0303,
+ 0x600b4f53,
+ 0x60b2683b,
+ 0x0301f043,
+ 0xf8dc603b,
+ 0xf8dc3000,
+ 0xf3c31000,
+ 0xf4210309,
+ 0xf043717f,
+ 0xf0210301,
+ 0x430b0103,
+ 0x3000f8cc,
+ 0x4422683b,
+ 0x60f2075b,
+ 0x4b47d503,
+ 0x7200f44f,
+ 0x4a46601a,
+ 0x68134c46,
+ 0xf043493d,
+ 0x60130308,
+ 0xf0436823,
+ 0x60230302,
+ 0xf5a2680b,
+ 0x3a1022fe,
+ 0x0301f043,
+ 0x6911600b,
+ 0x7196f501,
+ 0x1a5b6913,
+ 0xdbfb2b00,
+ 0x681b4b3b,
+ 0x2b01781b,
+ 0x493ad188,
+ 0x6a4b4c3a,
+ 0xf0236824,
+ 0xf04303ff,
+ 0x624b03df,
+ 0xf4236a4b,
+ 0xf443437f,
+ 0x624b435f,
+ 0x4b34b194,
+ 0xf9b3681b,
+ 0x2b003000,
+ 0x68e2db31,
+ 0x48316861,
+ 0xfb04f66a,
+ 0x10b2f8b5,
+ 0x4a2568e3,
+ 0x21041a5b,
+ 0x60916313,
+ 0x4a28492c,
+ 0xf443680b,
+ 0x600b4300,
+ 0xf4436d13,
+ 0x65134300,
+ 0xf0236dd3,
+ 0xf0430303,
+ 0xf0434300,
+ 0x65d30301,
+ 0xf4436813,
+ 0x60135380,
+ 0x4922e74e,
+ 0x3d23f893,
+ 0x46524638,
+ 0xfb2ef66a,
+ 0xf893e76d,
+ 0x3a012d24,
+ 0xaa02fb01,
+ 0x6913e760,
+ 0x1ad368e2,
+ 0x28001a18,
+ 0x4919dac8,
+ 0xf2404819,
+ 0xf66a4231,
+ 0xe7c0fca1,
+ 0xe000e100,
+ 0xe000ed00,
+ 0x00173254,
+ 0x0017192c,
+ 0x40328040,
+ 0x00171a30,
+ 0x40320084,
+ 0x001c589c,
+ 0x001e4000,
+ 0x001c58b8,
+ 0x40501000,
+ 0x40044084,
+ 0x40044100,
+ 0x40580010,
+ 0x40580018,
+ 0x00173274,
+ 0x40506000,
+ 0x00178bf0,
+ 0x00173250,
+ 0x001c58c8,
+ 0x40240014,
+ 0x001c58a4,
+ 0x001c5aac,
+ 0x001c5854,
+ 0x00173244,
+ 0x4ff0e92d,
+ 0x4abd4bbc,
+ 0xf852681b,
+ 0xf9b34020,
+ 0x4abb3000,
+ 0x8b02ed2d,
+ 0x02c0eb02,
+ 0xee082b00,
+ 0xb08b2a10,
+ 0xea4f4683,
+ 0xf2c005c0,
+ 0x462082c4,
+ 0xf8d0f669,
+ 0xf8534bb2,
+ 0xf1b9903b,
+ 0xf0000f00,
+ 0x230080e5,
+ 0x3303e9cd,
+ 0xf5059302,
+ 0x9301629e,
+ 0xf10b469a,
+ 0x9208039e,
+ 0x464c9305,
+ 0xf898e079,
+ 0x06df3004,
+ 0x80d2f140,
+ 0xf4226932,
+ 0x9a010900,
+ 0x9010f8c6,
+ 0x065d3201,
+ 0xf1009201,
+ 0x4fa180e0,
+ 0xf8d74620,
+ 0x47983418,
+ 0xf4036b63,
+ 0xf5b31360,
+ 0xbf081f20,
+ 0xf3ef46a2,
+ 0x07d98310,
+ 0xb672d403,
+ 0x22014b99,
+ 0x4d99601a,
+ 0xee18682b,
+ 0x33010a10,
+ 0xf669602b,
+ 0x6b63f951,
+ 0x1360f403,
+ 0x1f60f5b3,
+ 0x80a9f000,
+ 0xb133682b,
+ 0x3b014a8f,
+ 0x602b6812,
+ 0xb102b90b,
+ 0xf8dfb662,
+ 0xf8d88240,
+ 0x78193000,
+ 0xf419b391,
+ 0xd1050300,
+ 0x002ef9b4,
+ 0x28008de2,
+ 0x817ef2c0,
+ 0x302cf894,
+ 0x202af894,
+ 0xf8b44884,
+ 0xeb03c008,
+ 0x441303c3,
+ 0x530ef203,
+ 0xf8502901,
+ 0xeba22023,
+ 0xf840020c,
+ 0xf0002023,
+ 0x6ce080d5,
+ 0xf650b138,
+ 0xf8d8fd4f,
+ 0x781e3000,
+ 0xf0002e01,
+ 0x4621810f,
+ 0xf66a4658,
+ 0x6b63fc95,
+ 0x1360f403,
+ 0x1f60f5b3,
+ 0x80eaf000,
+ 0xf8534b6d,
+ 0x2c00403b,
+ 0xf8d4d05c,
+ 0x6d268048,
+ 0x0f00f1b8,
+ 0xaf7ff47f,
+ 0x8310f3ef,
+ 0xd40307d9,
+ 0x4b67b672,
+ 0x601a2201,
+ 0x682b4d66,
+ 0x0a10ee18,
+ 0x602b3301,
+ 0xf8ecf669,
+ 0xb133682b,
+ 0x3b014a60,
+ 0x602b6812,
+ 0xb102b90b,
+ 0x4f5cb662,
+ 0x9178f8df,
+ 0x817cf8df,
+ 0xf6764620,
+ 0xf8d7ff21,
+ 0x46203418,
+ 0xf8d74798,
+ 0x220033ac,
+ 0xf18bfa5f,
+ 0x47984620,
+ 0x302cf894,
+ 0x102af894,
+ 0xeb038922,
+ 0x440b03c3,
+ 0x530ef203,
+ 0x1023f859,
+ 0xf8491a8a,
+ 0xf8d82023,
+ 0x781b3000,
+ 0xd0642b01,
+ 0xd0532b02,
+ 0xd1082b03,
+ 0x3054f894,
+ 0xf0002b01,
+ 0x8de381db,
+ 0xf10007d9,
+ 0x4621814c,
+ 0xf66a4658,
+ 0x4b3ffc31,
+ 0x403bf853,
+ 0xd1a22c00,
+ 0xecbdb00b,
+ 0xe8bd8b02,
+ 0x4b388ff0,
+ 0xf9b3681b,
+ 0x2b003000,
+ 0x808bf2c0,
+ 0xe9dd4650,
+ 0xf6761201,
+ 0x4640fee7,
+ 0xfbeef659,
+ 0xe9cd2300,
+ 0x469a3301,
+ 0xf8d8e742,
+ 0xb1b220dc,
+ 0x8ce38811,
+ 0x1311eba3,
+ 0x030bf3c3,
+ 0x71fef240,
+ 0xd816428b,
+ 0xea4f2b3f,
+ 0xd8121113,
+ 0x0241eb02,
+ 0x030ff003,
+ 0xfa428852,
+ 0x07d8f303,
+ 0x9b02d509,
+ 0x93023301,
+ 0x0304f44f,
+ 0x0903ea49,
+ 0x9010f8c6,
+ 0xf44fe6fb,
+ 0xe7f72380,
+ 0x3054f894,
+ 0xf0002b01,
+ 0x8de38117,
+ 0xd5ae07da,
+ 0xf6682080,
+ 0xf8d8ff75,
+ 0x781b3000,
+ 0xf894e79c,
+ 0x2b013054,
+ 0x80f7f000,
+ 0x07db8de3,
+ 0x2080d59f,
+ 0xff66f668,
+ 0x3000f8d8,
+ 0xe78b781b,
+ 0x3054f894,
+ 0xf47f2b01,
+ 0xf890af26,
+ 0xf0433f20,
+ 0xf1060302,
+ 0xf8800110,
+ 0x22043f20,
+ 0xf6512012,
+ 0x8ba1fd2b,
+ 0x46224809,
+ 0xf91ef66a,
+ 0xbf00e713,
+ 0x00173250,
+ 0x0003fc40,
+ 0x00175980,
+ 0x00171a30,
+ 0x0017569c,
+ 0x00172600,
+ 0x00173278,
+ 0x001c5904,
+ 0x00173274,
+ 0x2b009b03,
+ 0x808bf040,
+ 0xf48bfa5f,
+ 0xf6682080,
+ 0x4620ff2f,
+ 0xfebef659,
+ 0x93032300,
+ 0xf1bae706,
+ 0xf47f0f00,
+ 0x49aaaf71,
+ 0xf24048aa,
+ 0xf66a42e7,
+ 0xe769fac7,
+ 0xe9d34ba8,
+ 0x69130201,
+ 0x46804798,
+ 0xf43f2800,
+ 0xf650aee8,
+ 0x4603fd49,
+ 0xf0002800,
+ 0x22008096,
+ 0x600249a1,
+ 0x605a6808,
+ 0x609a4440,
+ 0xf3ef6018,
+ 0x07d28210,
+ 0x812ef140,
+ 0x6829489c,
+ 0x31016802,
+ 0x0201f042,
+ 0x60026029,
+ 0x68124a98,
+ 0xd4fb0790,
+ 0x68124a97,
+ 0xf0002a00,
+ 0x4e968116,
+ 0x2a006872,
+ 0x8149f000,
+ 0x4a926053,
+ 0x681289b0,
+ 0x4b906073,
+ 0x30013201,
+ 0x601a81b0,
+ 0x68134a8c,
+ 0x0301f023,
+ 0x29006013,
+ 0xaeadf43f,
+ 0x39014b8b,
+ 0x6029681b,
+ 0xf47f2900,
+ 0x2b00aea6,
+ 0xaea3f43f,
+ 0xe6a0b662,
+ 0x49866d20,
+ 0x64a36503,
+ 0xf8946103,
+ 0x63e3502b,
+ 0xf3c29b05,
+ 0x20a4020e,
+ 0x0201f042,
+ 0x3005fb10,
+ 0x63a4f44f,
+ 0x1305fb03,
+ 0xeb0185e2,
+ 0x4a7c00c0,
+ 0x46219304,
+ 0xffe2f668,
+ 0xf4036b63,
+ 0xf5b31360,
+ 0xd0021f60,
+ 0x93032301,
+ 0x9804e686,
+ 0xfd60f656,
+ 0xf43f2800,
+ 0x4d73af6f,
+ 0x31fff895,
+ 0xf47f2b00,
+ 0x9b04af69,
+ 0xf8cd9a08,
+ 0xfa5fb00c,
+ 0xf8ddf48b,
+ 0x46d39014,
+ 0x4698189e,
+ 0xe00946ba,
+ 0xff74f668,
+ 0x3424f8da,
+ 0x46214638,
+ 0xf8954798,
+ 0xb92331ff,
+ 0x7039f858,
+ 0x2f004630,
+ 0x46dad1f0,
+ 0xb00cf8dd,
+ 0x2080e74a,
+ 0xfe7af668,
+ 0x4640e6af,
+ 0xfb92f650,
+ 0xf899e647,
+ 0x22043f20,
+ 0x0302f043,
+ 0x0110f106,
+ 0xf8892012,
+ 0xf6513f20,
+ 0x8de3fc43,
+ 0xf53f07da,
+ 0xe6fdaefc,
+ 0xf6539306,
+ 0x9b06f95d,
+ 0x28009007,
+ 0x80c0f000,
+ 0x4b509309,
+ 0x2a00681a,
+ 0x80c2f000,
+ 0xf6684618,
+ 0x9b07ff39,
+ 0xf04f2204,
+ 0x21120e00,
+ 0x70994684,
+ 0xf883701a,
+ 0xf883e001,
+ 0xf106e003,
+ 0x18980110,
+ 0xc018f8cd,
+ 0xfdc4f678,
+ 0x8a194b42,
+ 0xf5b19b09,
+ 0xd85a7fc3,
+ 0xb29b1c4b,
+ 0x00ca9309,
+ 0x4b3e9806,
+ 0x68188181,
+ 0x4b3d9907,
+ 0x0c02eb00,
+ 0x0e01f04f,
+ 0x1004f8cc,
+ 0x400b5881,
+ 0x6380f043,
+ 0x0308f043,
+ 0xf8995083,
+ 0x4a333782,
+ 0x82119909,
+ 0xf8894473,
+ 0x9b063782,
+ 0x22082100,
+ 0xc004f8c3,
+ 0xe00ef883,
+ 0x609a6019,
+ 0x8310f3ef,
+ 0xd40307db,
+ 0x4b25b672,
+ 0xe000f8c3,
+ 0x482a682b,
+ 0x33019906,
+ 0xf668602b,
+ 0xf8d7fea5,
+ 0x47983444,
+ 0xb133682b,
+ 0x3b014a1d,
+ 0x602b6812,
+ 0xb102b90b,
+ 0x8de3b662,
+ 0xf53f07d8,
+ 0xe67cae7b,
+ 0x69324b1f,
+ 0x601a681b,
+ 0xffaef64e,
+ 0x4b1de61d,
+ 0x421c681b,
+ 0xad37f47f,
+ 0x481b490a,
+ 0x6293f44f,
+ 0xf988f66a,
+ 0x2200e52f,
+ 0x46119309,
+ 0x4a17e7a4,
+ 0xbb6a6812,
+ 0x4e094a15,
+ 0xe6e86013,
+ 0x4a08b672,
+ 0xe6cd6016,
+ 0x001c5ad0,
+ 0x001c58ec,
+ 0x001755b4,
+ 0x001719e4,
+ 0x40240060,
+ 0x40240064,
+ 0x0017559c,
+ 0x0017569c,
+ 0x00177738,
+ 0x001c4001,
+ 0x00175780,
+ 0x0017469c,
+ 0x001755d4,
+ 0x31ff0000,
+ 0x001746a4,
+ 0x00180000,
+ 0x0017a5a0,
+ 0x001c58d4,
+ 0x40240068,
+ 0x9306480b,
+ 0xff78f669,
+ 0x9b066829,
+ 0x4809e7ca,
+ 0xf6699306,
+ 0x6829ff71,
+ 0xe6b09b06,
+ 0xb00b4806,
+ 0x8b02ecbd,
+ 0x4ff0e8bd,
+ 0xbcdef64f,
+ 0xe7f64803,
+ 0x001c591c,
+ 0x001c5924,
+ 0x001c5938,
+ 0x001c594c,
+ 0xf240b530,
+ 0xb0834003,
+ 0x4619460d,
+ 0xf6682308,
+ 0xe9d5f98d,
+ 0x46043200,
+ 0x3200e9c0,
+ 0xe9cd4611,
+ 0x48042200,
+ 0xff48f669,
+ 0xf6684620,
+ 0x2000f9af,
+ 0xbd30b003,
+ 0x001c5970,
+ 0x460cb570,
+ 0x4619b084,
+ 0x4012f240,
+ 0xf6682308,
+ 0x6822f971,
+ 0x429a4b12,
+ 0xd0104605,
+ 0x686168a3,
+ 0xe9c54810,
+ 0xe9cd2300,
+ 0x92003301,
+ 0xf669461a,
+ 0x4628ff27,
+ 0xf98ef668,
+ 0xb0042000,
+ 0xe9d4bd70,
+ 0x68116301,
+ 0x404b4808,
+ 0x404b4033,
+ 0xe9d46013,
+ 0x680a1300,
+ 0x68a29200,
+ 0xff12f669,
+ 0xe7dd6822,
+ 0x40344058,
+ 0x001c59cc,
+ 0x001c59b0,
+ 0xbf002332,
+ 0xf0133b01,
+ 0xd1fa03ff,
+ 0xbf004770,
+ 0xbf0023c8,
+ 0xf0133b01,
+ 0xd1fa03ff,
+ 0xbf004770,
+ 0x49724a71,
+ 0xf8df6813,
+ 0xf023c208,
+ 0xb5f00302,
+ 0x680b6013,
+ 0x4c6f4a6e,
+ 0xf4234f6f,
+ 0x600b6300,
+ 0x496e6813,
+ 0xf4232800,
+ 0xbf1c7380,
+ 0x468c4627,
+ 0x20326013,
+ 0x3801bf00,
+ 0x00fff010,
+ 0xf8dfd1fa,
+ 0x4a63e18c,
+ 0x3000f8de,
+ 0xf4234e65,
+ 0xf8ce5380,
+ 0x68133000,
+ 0x7300f423,
+ 0xf8de6013,
+ 0xf4433000,
+ 0xf8ce6380,
+ 0xf8de3000,
+ 0xf4433000,
+ 0xf8ce6300,
+ 0x46723000,
+ 0x1cc125ff,
+ 0x4664b2c9,
+ 0xf0236813,
+ 0x430b03ff,
+ 0xf8546013,
+ 0x60333b04,
+ 0xf4436813,
+ 0x60137380,
+ 0xbf00bf00,
+ 0xbf00bf00,
+ 0x049b6813,
+ 0x3901d5fc,
+ 0x428db2c9,
+ 0x3004d1e8,
+ 0x28803504,
+ 0xf10cb2ed,
+ 0xd1de0c10,
+ 0x3000f8de,
+ 0x6380f423,
+ 0x3000f8ce,
+ 0xbf0023c8,
+ 0xf0133b01,
+ 0xd1fa03ff,
+ 0x493f4c3e,
+ 0x48436822,
+ 0x6200f422,
+ 0x680a6022,
+ 0x0280f042,
+ 0x680a600a,
+ 0x7280f442,
+ 0x600a3f04,
+ 0xf022680a,
+ 0x431a021f,
+ 0xf857600a,
+ 0x60022f04,
+ 0xf042680a,
+ 0x600a0220,
+ 0xbf00bf00,
+ 0xbf00bf00,
+ 0x0552680a,
+ 0x3301d5fc,
+ 0xd1e92b10,
+ 0xf023680b,
+ 0x600b0380,
+ 0xbf0023c8,
+ 0xf0133b01,
+ 0xd1fa03ff,
+ 0x4a2d4927,
+ 0xf423680b,
+ 0x600b7380,
+ 0xf4436813,
+ 0x60131300,
+ 0xbf002332,
+ 0xf0133b01,
+ 0xd1fa03ff,
+ 0x4a1f4b1e,
+ 0x4d256819,
+ 0x48264c25,
+ 0xf4414e26,
+ 0x60195180,
+ 0xf4416811,
+ 0x60117100,
+ 0xf4416819,
+ 0x60196100,
+ 0x4b216811,
+ 0x7180f441,
+ 0x682a6011,
+ 0xf442491f,
+ 0x602a5280,
+ 0xf4226822,
+ 0x60222280,
+ 0xf0226802,
+ 0x60025200,
+ 0x60306818,
+ 0x45bbf5a5,
+ 0x685c3d78,
+ 0x689d602c,
+ 0x4a16600d,
+ 0x601568dd,
+ 0x691d4815,
+ 0xe9d36005,
+ 0x4c145005,
+ 0x602569db,
+ 0x61136108,
+ 0xbf00bdf0,
+ 0x40580018,
+ 0x40344060,
+ 0x4034406c,
+ 0x001718a4,
+ 0x00171824,
+ 0x00171624,
+ 0x40344064,
+ 0x40344070,
+ 0x40344058,
+ 0x40342014,
+ 0x40342018,
+ 0x4034201c,
+ 0x4033c218,
+ 0x001718e4,
+ 0x4033c220,
+ 0x4033c224,
+ 0x4033c228,
+ 0x4033c22c,
+ 0x00171324,
+ 0x6c616821,
+ 0x6e6f615f,
+ 0x656d6974,
+ 0x69745f72,
+ 0x705f656d,
+ 0x28747361,
+ 0x656d6974,
+ 0x743e2d72,
+ 0x20656d69,
+ 0x3035202b,
+ 0x00293030,
+ 0x616d786e,
+ 0x69745f63,
+ 0x6f5f656d,
+ 0x69615f6e,
+ 0x61765f72,
+ 0x5f64696c,
+ 0x66746567,
+ 0x21202928,
+ 0x0030203d,
+ 0x6d697464,
+ 0x2c64253a,
+ 0x252c6425,
+ 0x64252c64,
+ 0x00000a0d,
+ 0x6f76282a,
+ 0x6974616c,
+ 0x7520656c,
+ 0x38746e69,
+ 0x2a20745f,
+ 0x5f672629,
+ 0x5f6e6f61,
+ 0x72616873,
+ 0x642e6465,
+ 0x5f6d6974,
+ 0x5f746e63,
+ 0x736e6f61,
+ 0x65726168,
+ 0x203c2064,
+ 0x6f76282a,
+ 0x6974616c,
+ 0x7520656c,
+ 0x38746e69,
+ 0x2a20745f,
+ 0x5f672629,
+ 0x5f6e6f61,
+ 0x72616873,
+ 0x642e6465,
+ 0x5f6d6974,
+ 0x69726570,
+ 0x615f646f,
+ 0x68736e6f,
+ 0x64657261,
+ 0x00000000,
+ 0x00002c4c,
+ 0x654b849b,
+ 0x78646979,
+ 0x766e6920,
+ 0x64696c61,
+ 0x3230252c,
+ 0x00000a58,
+ 0x6e49849b,
+ 0x696c6176,
+ 0x656b2064,
+ 0x78646979,
+ 0x0000000a,
+ 0x6e49849b,
+ 0x696c6176,
+ 0x54532064,
+ 0x64253a41,
+ 0x0000000a,
+ 0x5f666976,
+ 0x20617473,
+ 0x76203d3d,
+ 0x00006669,
+ 0x64253d54,
+ 0x0d64252c,
+ 0x0000000a,
+ 0x3a4e4342,
+ 0x0a0d6425,
+ 0x00000000,
+ 0x206e6362,
+ 0x656e6f64,
+ 0x6d697420,
+ 0x74756f65,
+ 0x00000a0d,
+ 0x20736366,
+ 0x0a0d6b6f,
+ 0x00000000,
+ 0x20736366,
+ 0x20746f6e,
+ 0x0a0d6b6f,
+ 0x00000000,
+ 0x656c6469,
+ 0x72726520,
+ 0x00000a0d,
+ 0x656c6469,
+ 0x746e6920,
+ 0x72726520,
+ 0x00000a0d,
+ 0x2c642564,
+ 0x00000000,
+ 0x0a0d6564,
+ 0x00000000,
+ 0x6c616821,
+ 0x63616d5f,
+ 0x745f7768,
+ 0x5f656d69,
+ 0x74736170,
+ 0x6d697428,
+ 0x3e2d7265,
+ 0x656d6974,
+ 0x67202d20,
+ 0x6669775f,
+ 0x65735f69,
+ 0x6e697474,
+ 0x702e7367,
+ 0x6f5f7277,
+ 0x5f6e6570,
+ 0x64737973,
+ 0x79616c65,
+ 0x00000029,
+ 0x78253d74,
+ 0x00000a0d,
+ 0x20746f6e,
+ 0x74736170,
+ 0x6425203a,
+ 0x0d64252c,
+ 0x0000000a,
+ 0x73736170,
+ 0x6425203a,
+ 0x0d64252c,
+ 0x0000000a,
+ 0x3a706c73,
+ 0x252c7825,
+ 0x000a0d78,
+ 0x655f656b,
+ 0x675f7476,
+ 0x29287465,
+ 0x65202620,
+ 0x625f7476,
+ 0x00007469,
+ 0x65647874,
+ 0x665f6373,
+ 0x74737269,
+ 0x203d2120,
+ 0x4c4c554e,
+ 0x00000000,
+ 0x73212121,
+ 0x20646e65,
+ 0x206d6663,
+ 0x78253a31,
+ 0x0d78252c,
+ 0x0000000a,
+ 0x0d677562,
+ 0x0000000a,
+ 0x6f696473,
+ 0x69617420,
+ 0x7265206c,
+ 0x0d726f72,
+ 0x0000000a,
+ 0x3a727265,
+ 0x206f6e20,
+ 0x2067736d,
+ 0x21746b70,
+ 0x00000a0d,
+ 0x21727265,
+ 0x74202121,
+ 0x63206c78,
+ 0x6e206d66,
+ 0x7562206f,
+ 0x72656666,
+ 0x726f6620,
+ 0x62737520,
+ 0x00000a0d,
+ 0x4244819d,
+ 0x57203a47,
+ 0x69746972,
+ 0x6d20676e,
+ 0x726f6d65,
+ 0x69772079,
+ 0x30206874,
+ 0x38302578,
+ 0x202f2078,
+ 0x203a6425,
+ 0x2578305b,
+ 0x5d783830,
+ 0x30203d20,
+ 0x38302578,
+ 0x202f2078,
+ 0x000a6425,
+ 0x6b73616d,
+ 0x69727720,
+ 0x253a6574,
+ 0x78252c78,
+ 0x2c78252c,
+ 0x0a0d7825,
+ 0x00000000,
+ 0x4244819d,
+ 0x57203a47,
+ 0x69746972,
+ 0x6d20676e,
+ 0x726f6d65,
+ 0x69772079,
+ 0x6d206874,
+ 0x3a6b7361,
+ 0x30257830,
+ 0x202c7838,
+ 0x61746164,
+ 0x2578303a,
+ 0x20783830,
+ 0x6425202f,
+ 0x305b203a,
+ 0x38302578,
+ 0x3d205d78,
+ 0x25783020,
+ 0x20783830,
+ 0x6425202f,
+ 0x0000000a,
+ 0x5f6c6168,
+ 0x6863616d,
+ 0x78725f77,
+ 0x6e63625f,
+ 0x7275645f,
+ 0x6f697461,
+ 0x0000006e,
+ 0x5f6c6168,
+ 0x6863616d,
+ 0x6c735f77,
+ 0x5f706565,
+ 0x63656863,
+ 0x61705f6b,
+ 0x00686374,
+ 0x745f6d6d,
+ 0x5f747462,
+ 0x706d6f63,
+ 0x5f657475,
+ 0x63746170,
+ 0x00000068,
+ 0x735f6d6d,
+ 0x7065656c,
+ 0x6f666e69,
+ 0x5f78725f,
+ 0x5f747665,
+ 0x63746170,
+ 0x00000068,
+ 0x786e7772,
+ 0x656c735f,
+ 0x635f7065,
+ 0x61676b6c,
+ 0x635f6574,
+ 0x69666e6f,
+ 0x61705f67,
+ 0x00686374,
+ 0x786e7772,
+ 0x656c735f,
+ 0x645f7065,
+ 0x73706565,
+ 0x7065656c,
+ 0x6e6f635f,
+ 0x5f676966,
+ 0x63746170,
+ 0x00000068,
+ 0x5f6c7874,
+ 0x5f6d6663,
+ 0x5f747665,
+ 0x63746170,
+ 0x00000068,
+
+};
+
+struct aic_feature_t {
+ int hwinfo;
+ int fwlog_en;
+};
+
+#define CHIP_REV_U02 0x3
+#define CHIP_REV_U03 0x7
+#define CHIP_SUB_REV_U04 0x20
+u8 chip_id = 0;
+u8 chip_sub_id = 0; // rom_id for 8800dc
+u8 chip_mcu_id = 0;
+
+#ifdef CONFIG_PMIC_SETTING
+u32 syscfg_tbl_pmic_u02[][2] = {
+ {0x40040000, 0x00001AC8}, // 1) fix panic
+ {0x40040084, 0x00011580},
+ {0x40040080, 0x00000001},
+ {0x40100058, 0x00000000},
+};
+#endif /* CONFIG_PMIC_SETTING */
+
+u32 syscfg_tbl_u04[][2] = {
+ {0x40040000, 0x0000042C}, // protect usb replenish rxq / flush rxq, skip flush rxq before start_app
+ {0x40040004, 0x0000DD44},
+ {0x40040008, 0x00000448},
+ {0x4004000C, 0x0000044C},
+ {0x0019B800, 0xB9F0F19B},
+ {0x0019B804, 0x0019B81D},
+ {0x0019B808, 0xBF00FA79},
+ {0x0019B80C, 0xF007BF00},
+ {0x0019B810, 0x4605B672}, // code
+ {0x0019B814, 0x21E0F04F},
+ {0x0019B818, 0xBE0BF664},
+ {0x0019B81C, 0xF665B510},
+ {0x0019B820, 0x4804FC9D},
+ {0x0019B824, 0xFA9EF66C},
+ {0x0019B828, 0xFCA8F665},
+ {0x0019B82C, 0x4010E8BD},
+ {0x0019B830, 0xBAC6F66C},
+ {0x0019B834, 0x0019A0C4},
+ {0x40040084, 0x0019B800}, // out base
+ {0x40040080, 0x0000000F},
+ {0x40100058, 0x00000000},
+};
+
+u32 syscfg_tbl[][2] = {
+ {0x40500014, 0x00000101}, // 1)
+ {0x40500018, 0x0000010D}, // 2)
+ {0x40500004, 0x00000010}, // 3) the order should not be changed
+ #ifdef CONFIG_PMIC_SETTING
+ {0x50000000, 0x03220204}, // 2) pmic interface init
+ {0x50019150, 0x00000002}, // 3) for 26m xtal, set div1
+ {0x50017008, 0x00000000}, // 4) stop wdg
+ #endif /* CONFIG_PMIC_SETTING */
+};
+
+u32 syscfg_tbl_masked[][3] = {
+ {0x40506024, 0x000000FF, 0x000000DF}, // for clk gate lp_level
+ #ifdef CONFIG_PMIC_SETTING
+ //{0x50017008, 0x00000002, 0x00000000}, // stop wdg
+ #endif /* CONFIG_PMIC_SETTING */
+};
+
+
+u32 rf_tbl_masked[][3] = {
+ {0x40344058, 0x00800000, 0x00000000},// pll trx
+};
+
+u32 wdt_tbl_masked[][3] = {
+ {0x4010300c, 0x00000001, 0x00000001},
+};
+
+static void system_config_8800(struct rwnx_hw *rwnx_hw){
+ int syscfg_num;
+ int ret, cnt;
+ const u32 mem_addr = 0x40500000;
+ struct dbg_mem_read_cfm rd_mem_addr_cfm;
+ ret = rwnx_send_dbg_mem_read_req(rwnx_hw, mem_addr, &rd_mem_addr_cfm);
+ if (ret) {
+ AICWFDBG(LOGERROR, "%x rd fail: %d\n", mem_addr, ret);
+ return;
+ }
+ chip_id = (u8)(rd_mem_addr_cfm.memdata >> 16);
+ //printk("%x=%x\n", rd_mem_addr_cfm.memaddr, rd_mem_addr_cfm.memdata);
+ ret = rwnx_send_dbg_mem_read_req(rwnx_hw, 0x00000004, &rd_mem_addr_cfm);
+ if (ret) {
+ AICWFDBG(LOGERROR, "[0x00000004] rd fail: %d\n", ret);
+ return;
+ }
+ chip_sub_id = (u8)(rd_mem_addr_cfm.memdata >> 4);
+ //printk("%x=%x\n", rd_mem_addr_cfm.memaddr, rd_mem_addr_cfm.memdata);
+ AICWFDBG(LOGINFO, "chip_id=%x, chip_sub_id=%x\n", chip_id, chip_sub_id);
+
+
+#ifdef CONFIG_PMIC_SETTING
+ if(rwnx_hw->usbdev->chipid == PRODUCT_ID_AIC8801){
+ if (chip_id == CHIP_REV_U02) {
+ syscfg_num = sizeof(syscfg_tbl_pmic_u02) / sizeof(u32) / 2;
+ for (cnt = 0; cnt < syscfg_num; cnt++) {
+ ret = rwnx_send_dbg_mem_write_req(rwnx_hw, syscfg_tbl_pmic_u02[cnt][0], syscfg_tbl_pmic_u02[cnt][1]);
+ if (ret) {
+ AICWFDBG(LOGERROR, "%x write fail: %d\n", syscfg_tbl_pmic_u02[cnt][0], ret);
+ return;
+ }
+ }
+ }
+ }
+#endif
+ if(rwnx_hw->usbdev->chipid == PRODUCT_ID_AIC8801){
+ if ((chip_id == CHIP_REV_U03) && (chip_sub_id == CHIP_SUB_REV_U04)) {
+ syscfg_num = sizeof(syscfg_tbl_u04) / sizeof(u32) / 2;
+ AICWFDBG(LOGINFO, "cfg u04\n");
+ for (cnt = 0; cnt < syscfg_num; cnt++) {
+ ret = rwnx_send_dbg_mem_write_req(rwnx_hw, syscfg_tbl_u04[cnt][0], syscfg_tbl_u04[cnt][1]);
+ if (ret) {
+ AICWFDBG(LOGERROR, "%x write fail: %d\n", syscfg_tbl_u04[cnt][0], ret);
+ return;
+ }
+ }
+ }
+ }
+
+ syscfg_num = sizeof(syscfg_tbl) / sizeof(u32) / 2;
+
+ for (cnt = 0; cnt < syscfg_num; cnt++) {
+ ret = rwnx_send_dbg_mem_write_req(rwnx_hw, syscfg_tbl[cnt][0], syscfg_tbl[cnt][1]);
+ if (ret) {
+ AICWFDBG(LOGERROR, "%x write fail: %d\n", syscfg_tbl[cnt][0], ret);
+ return;
+ }
+ }
+ syscfg_num = sizeof(syscfg_tbl_masked) / sizeof(u32) / 3;
+ for (cnt = 0; cnt < syscfg_num; cnt++) {
+ if (syscfg_tbl_masked[cnt][0] == 0x00000000) {
+ break;
+ }
+
+ ret = rwnx_send_dbg_mem_mask_write_req(rwnx_hw,
+ syscfg_tbl_masked[cnt][0], syscfg_tbl_masked[cnt][1], syscfg_tbl_masked[cnt][2]);
+ if (ret) {
+ AICWFDBG(LOGERROR, "%x mask write fail: %d\n", syscfg_tbl_masked[cnt][0], ret);
+ return;
+ }
+ }
+
+
+}
+
+
+static void system_config(struct rwnx_hw *rwnx_hw)
+{
+ if(rwnx_hw->usbdev->chipid == PRODUCT_ID_AIC8801){
+ system_config_8800(rwnx_hw);
+ }else if(rwnx_hw->usbdev->chipid == PRODUCT_ID_AIC8800DC ||
+ rwnx_hw->usbdev->chipid == PRODUCT_ID_AIC8800DW){
+ system_config_8800dc(rwnx_hw);
+ }
+}
+
+static int wdt_config(struct rwnx_hw *rwnx_hw)
+{
+ int ret = 0;
+ ret = rwnx_send_dbg_mem_mask_write_req(rwnx_hw,
+ wdt_tbl_masked[0][0], wdt_tbl_masked[0][1], wdt_tbl_masked[0][2]);
+ if (ret) {
+ printk("wdt config %x write fail: %d\n", wdt_tbl_masked[0][0], ret);
+ }
+ return ret;
+}
+#if 0
+static void rf_config(struct rwnx_hw *rwnx_hw)
+{
+ int ret;
+ ret = rwnx_send_dbg_mem_mask_write_req(rwnx_hw,
+ rf_tbl_masked[0][0], rf_tbl_masked[0][1], rf_tbl_masked[0][2]);
+ if (ret) {
+ printk("rf config %x write fail: %d\n", rf_tbl_masked[0][0], ret);
+ }
+}
+#endif
+#if 0
+static int start_from_bootrom(struct rwnx_hw *rwnx_hw)
+{
+ int ret = 0;
+
+ /* memory access */
+#ifdef CONFIG_ROM_PATCH_EN
+ const u32 rd_addr = ROM_FMAC_FW_ADDR;
+ const u32 fw_addr = ROM_FMAC_FW_ADDR;
+#else
+ const u32 rd_addr = RAM_FMAC_FW_ADDR;
+ const u32 fw_addr = RAM_FMAC_FW_ADDR;
+#endif
+ struct dbg_mem_read_cfm rd_cfm;
+ printk("Read FW mem: %08x\n", rd_addr);
+ if ((ret = rwnx_send_dbg_mem_read_req(rwnx_hw, rd_addr, &rd_cfm))) {
+ return -1;
+ }
+ printk("cfm: [%08x] = %08x\n", rd_cfm.memaddr, rd_cfm.memdata);
+
+ /* fw start */
+ printk("Start app: %08x\n", fw_addr);
+ if ((ret = rwnx_send_dbg_start_app_req(rwnx_hw, fw_addr, HOST_START_APP_AUTO))) {
+ return -1;
+ }
+ return 0;
+}
+#endif
+/**
+ *
+ */
+
+
+#ifdef CONFIG_GPIO_WAKEUP
+static const struct wiphy_wowlan_support aic_wowlan_support = {
+ .flags = WIPHY_WOWLAN_ANY | WIPHY_WOWLAN_MAGIC_PKT,
+};
+#endif
+
+extern int get_hardware_info(void);
+
+#ifdef AICWF_USB_SUPPORT
+u32 usbcfg_tbl[][2] = {
+ {0x40200028, 0x0021047e},
+ {0x40200024, 0x0000011d},
+};
+
+static void aicwf_usb_config(struct rwnx_hw *rwnx_hw)
+{
+ int usbcfg_num = 0;
+ int ret = 0, cnt = 0;
+ struct dbg_mem_read_cfm rd_mem_addr_cfm;
+ const u32 mem_addr = 0x40200024;
+
+ ret = rwnx_send_dbg_mem_read_req(rwnx_hw, mem_addr, &rd_mem_addr_cfm);
+ if (ret) {
+ AICWFDBG(LOGERROR, "%x rd fail: %d\n", mem_addr, ret);
+ return;
+ }
+ AICWFDBG(LOGINFO, "usb config read %x\n", rd_mem_addr_cfm.memdata);
+ if ((rd_mem_addr_cfm.memdata & 0xffff) == 0x119) {
+ cnt = 0;
+ usbcfg_num = sizeof(usbcfg_tbl) / sizeof(u32) / 2;
+ for (cnt = 0; cnt < usbcfg_num; cnt++) {
+ ret = rwnx_send_dbg_mem_write_req(rwnx_hw, usbcfg_tbl[cnt][0], usbcfg_tbl[cnt][1]);
+ if (ret) {
+ AICWFDBG(LOGERROR, "%x write fail: %d\n", usbcfg_tbl[cnt][0], ret);
+ return;
+ }
+ }
+ }
+}
+#endif // (AICWF_USB_SUPPORT)
+
+static int start_from_bootrom(struct rwnx_hw *rwnx_hw)
+{
+ int ret = 0;
+
+ /* memory access */
+#ifdef CONFIG_ROM_PATCH_EN
+ const u32 rd_addr = RAM_LMAC_FW_ADDR;
+ const u32 fw_addr = RAM_LMAC_FW_ADDR;
+#else
+ const u32 rd_addr = RAM_FMAC_FW_ADDR;
+ const u32 fw_addr = RAM_FMAC_FW_ADDR;
+#endif
+ u32 boot_type;
+ struct dbg_mem_read_cfm rd_cfm;
+ AICWFDBG(LOGINFO, "Read FW mem: %08x\n", rd_addr);
+ if ((ret = rwnx_send_dbg_mem_read_req(rwnx_hw, rd_addr, &rd_cfm))) {
+ return -1;
+ }
+ AICWFDBG(LOGINFO, "cfm: [%08x] = %08x\n", rd_cfm.memaddr, rd_cfm.memdata);
+
+ if (testmode == 0) {
+ boot_type = HOST_START_APP_DUMMY;
+ } else {
+ boot_type = HOST_START_APP_AUTO;
+ }
+ /* fw start */
+ AICWFDBG(LOGINFO, "Start app: %08x, %d\n", fw_addr, boot_type);
+ if ((ret = rwnx_send_dbg_start_app_req(rwnx_hw, fw_addr, boot_type))) {
+ return -1;
+ }
+ return 0;
+}
+
+
+int rwnx_ic_system_init(struct rwnx_hw *rwnx_hw){
+
+ if(rwnx_hw->usbdev->chipid == PRODUCT_ID_AIC8801){
+ system_config(rwnx_hw);
+ }else if(rwnx_hw->usbdev->chipid == PRODUCT_ID_AIC8800DC ||
+ rwnx_hw->usbdev->chipid == PRODUCT_ID_AIC8800DW){
+#ifdef AICWF_USB_SUPPORT
+ aicwf_usb_config(rwnx_hw);
+#endif
+ system_config(rwnx_hw);
+ if (rwnx_platform_on(rwnx_hw, NULL))
+ return -1;
+
+#if defined(CONFIG_START_FROM_BOOTROM)
+ if (chip_sub_id < 2) {
+ if (wdt_config(rwnx_hw)) {
+ return -1;
+ }
+ }
+ if (start_from_bootrom(rwnx_hw))
+ return -1;
+#endif
+ }else if(rwnx_hw->usbdev->chipid == PRODUCT_ID_AIC8800D81){
+ rwnx_plat_userconfig_load_8800d80(rwnx_hw);
+ }
+
+ return 0;
+}
+
+
+int rwnx_ic_rf_init(struct rwnx_hw *rwnx_hw){
+ struct mm_set_rf_calib_cfm cfm;
+ int ret = 0;
+#ifdef CONFIG_5M10M
+ uint32_t hwconfig_id = 4;
+ int32_t param[1];
+ param[0] = BWMODE10M;
+#endif
+ if(rwnx_hw->usbdev->chipid == PRODUCT_ID_AIC8801){
+ if ((ret = rwnx_send_txpwr_idx_req(rwnx_hw))) {
+ return -1;
+ }
+
+ if ((ret = rwnx_send_txpwr_ofst_req(rwnx_hw))) {
+ return -1;
+ }
+
+ if (testmode == 0) {
+ if ((ret = rwnx_send_rf_calib_req(rwnx_hw, &cfm)))
+ return -1;
+ }
+
+ }else if(rwnx_hw->usbdev->chipid == PRODUCT_ID_AIC8800DC ||
+ rwnx_hw->usbdev->chipid == PRODUCT_ID_AIC8800DW){
+ if ((ret = aicwf_set_rf_config_8800dc(rwnx_hw, &cfm)))
+ return -1;
+ }else if(rwnx_hw->usbdev->chipid == PRODUCT_ID_AIC8800D81){
+ if ((ret = aicwf_set_rf_config_8800d80(rwnx_hw, &cfm)))
+ return -1;
+ }
+#ifdef CONFIG_5M10M
+ rwnx_send_vendor_hwconfig_req(rwnx_hw, hwconfig_id, param);
+#endif
+ return 0;
+}
+
+extern void *aicwf_prealloc_txq_alloc(size_t size);
+extern int aicwf_vendor_init(struct wiphy *wiphy);
+int rwnx_cfg80211_init(struct rwnx_plat *rwnx_plat, void **platform_data)
+{
+ struct rwnx_hw *rwnx_hw;
+ struct rwnx_conf_file init_conf;
+ int ret = 0;
+ struct wiphy *wiphy;
+ struct rwnx_vif *vif;
+ int i;
+ u8 dflt_mac[ETH_ALEN] = { 0x88, 0x00, 0x33, 0x77, 0x10, 0x99};
+ struct mm_get_fw_version_cfm fw_version;
+ u8_l mac_addr_efuse[ETH_ALEN];
+#ifndef USE_5G
+ struct aic_feature_t feature;
+#endif
+ struct mm_set_stack_start_cfm set_start_cfm;
+
+ int nx_remote_sta_max = NX_REMOTE_STA_MAX;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+
+if((g_rwnx_plat->usbdev->chipid == PRODUCT_ID_AIC8801) ||
+ ((g_rwnx_plat->usbdev->chipid == PRODUCT_ID_AIC8800DC ||
+ g_rwnx_plat->usbdev->chipid == PRODUCT_ID_AIC8800DW) && chip_id < 3)){
+ nx_remote_sta_max = NX_REMOTE_STA_MAX_FOR_OLD_IC;
+}
+
+
+//#ifndef CONFIG_RFTEST
+#ifdef CONFIG_MAC_RANDOM_IF_NO_MAC_IN_EFUSE
+ get_random_bytes(&dflt_mac[4], 2);
+#endif
+//#endif
+ /* create a new wiphy for use with cfg80211 */
+ AICWFDBG(LOGINFO, "%s sizeof(struct rwnx_hw):%d \r\n", __func__, (int)sizeof(struct rwnx_hw));
+ wiphy = wiphy_new(&rwnx_cfg80211_ops, sizeof(struct rwnx_hw));
+ //dev_set_name(&wiphy->dev,"aicphy%d",0);
+
+ if (!wiphy) {
+ dev_err(rwnx_platform_get_dev(rwnx_plat), "Failed to create new wiphy\n");
+ ret = -ENOMEM;
+ goto err_out;
+ }
+
+ rwnx_hw = wiphy_priv(wiphy);
+ rwnx_hw->wiphy = wiphy;
+ rwnx_hw->plat = rwnx_plat;
+ rwnx_hw->dev = rwnx_platform_get_dev(rwnx_plat);
+#ifdef AICWF_SDIO_SUPPORT
+ rwnx_hw->sdiodev = rwnx_plat->sdiodev;
+ rwnx_plat->sdiodev->rwnx_hw = rwnx_hw;
+ rwnx_hw->cmd_mgr = &rwnx_plat->sdiodev->cmd_mgr;
+#else
+ rwnx_hw->usbdev = rwnx_plat->usbdev;
+ rwnx_plat->usbdev->rwnx_hw = rwnx_hw;
+ rwnx_hw->cmd_mgr = &rwnx_plat->usbdev->cmd_mgr;
+#endif
+ rwnx_hw->mod_params = &rwnx_mod_params;
+ rwnx_hw->tcp_pacing_shift = 7;
+
+#ifdef CONFIG_SCHED_SCAN
+ rwnx_hw->is_sched_scan = false;
+#endif//CONFIG_SCHED_SCAN
+
+ rwnx_init_aic(rwnx_hw);
+ /* set device pointer for wiphy */
+ set_wiphy_dev(wiphy, rwnx_hw->dev);
+
+ /* Create cache to allocate sw_txhdr */
+ rwnx_hw->sw_txhdr_cache = KMEM_CACHE(rwnx_sw_txhdr, 0);
+ if (!rwnx_hw->sw_txhdr_cache) {
+ wiphy_err(wiphy, "Cannot allocate cache for sw TX header\n");
+ ret = -ENOMEM;
+ goto err_cache;
+ }
+
+#if 0
+ if ((ret = rwnx_parse_configfile(rwnx_hw, RWNX_CONFIG_FW_NAME, &init_conf))) {
+ wiphy_err(wiphy, "rwnx_parse_configfile failed\n");
+ goto err_config;
+ }
+#else
+ memcpy(init_conf.mac_addr, dflt_mac, ETH_ALEN);
+#endif
+
+ rwnx_hw->vif_started = 0;
+ rwnx_hw->monitor_vif = RWNX_INVALID_VIF;
+ rwnx_hw->adding_sta = false;
+
+ rwnx_hw->scan_ie.addr = NULL;
+
+ for (i = 0; i < NX_VIRT_DEV_MAX + nx_remote_sta_max; i++){
+ rwnx_hw->avail_idx_map |= BIT(i);
+ }
+
+ rwnx_hwq_init(rwnx_hw);
+
+#ifdef CONFIG_PREALLOC_TXQ
+ rwnx_hw->txq = (struct rwnx_txq*)aicwf_prealloc_txq_alloc(sizeof(struct rwnx_txq)*NX_NB_TXQ);
+#endif
+
+ for (i = 0; i < NX_NB_TXQ; i++) {
+ rwnx_hw->txq[i].idx = TXQ_INACTIVE;
+ }
+
+ rwnx_mu_group_init(rwnx_hw);
+
+ /* Initialize RoC element pointer to NULL, indicate that RoC can be started */
+ rwnx_hw->roc_elem = NULL;
+ /* Cookie can not be 0 */
+ rwnx_hw->roc_cookie_cnt = 1;
+
+ INIT_LIST_HEAD(&rwnx_hw->vifs);
+ mutex_init(&rwnx_hw->mutex);
+ mutex_init(&rwnx_hw->dbgdump_elem.mutex);
+ spin_lock_init(&rwnx_hw->tx_lock);
+ spin_lock_init(&rwnx_hw->cb_lock);
+
+ INIT_WORK(&rwnx_hw->apmStalossWork, apm_staloss_work_process);
+ rwnx_hw->apmStaloss_wq = create_singlethread_workqueue("apmStaloss_wq");
+ if (!rwnx_hw->apmStaloss_wq) {
+ txrx_err("insufficient memory to create apmStaloss workqueue.\n");
+ goto err_cache;
+ }
+
+ wiphy->mgmt_stypes = rwnx_default_mgmt_stypes;
+
+#ifdef CONFIG_FWLOG_EN
+ rwnx_hw->fwlog_en = true;
+#else
+ rwnx_hw->fwlog_en = false;
+#endif
+ //init ic system
+ if((ret = rwnx_ic_system_init(rwnx_hw))){
+ goto err_lmac_reqs;
+ }
+
+#ifdef USE_5G
+ if(rwnx_hw->usbdev->chipid == PRODUCT_ID_AIC8800DC ||
+ rwnx_hw->usbdev->chipid == PRODUCT_ID_AIC8800DW){
+ ret = rwnx_send_set_stack_start_req(rwnx_hw, 1, 0, 0, rwnx_hw->fwlog_en, &set_start_cfm);
+ } else {
+ ret = rwnx_send_set_stack_start_req(rwnx_hw, 1, 0, CO_BIT(5), rwnx_hw->fwlog_en, &set_start_cfm);
+ }
+#else
+ if(rwnx_hw->usbdev->chipid == PRODUCT_ID_AIC8800D81){
+ ret = rwnx_send_set_stack_start_req(rwnx_hw, 1, 0, CO_BIT(5), rwnx_hw->fwlog_en, &set_start_cfm);
+ } else {
+ ret = rwnx_send_set_stack_start_req(rwnx_hw, 1, get_hardware_info(), feature.hwinfo, rwnx_hw->fwlog_en, &set_start_cfm);
+ }
+#endif
+
+ if (ret){
+ goto err_lmac_reqs;
+ }
+
+ AICWFDBG(LOGINFO, "is 5g support = %d, vendor_info = 0x%02X\n", set_start_cfm.is_5g_support, set_start_cfm.vendor_info);
+ rwnx_hw->band_5g_support = set_start_cfm.is_5g_support;
+
+ ret = rwnx_send_get_fw_version_req(rwnx_hw, &fw_version);
+ memcpy(wiphy->fw_version, fw_version.fw_version, fw_version.fw_version_len>32? 32 : fw_version.fw_version_len>32);
+ AICWFDBG(LOGINFO, "Firmware Version: %s\r\n", fw_version.fw_version);
+
+ wiphy->bands[NL80211_BAND_2GHZ] = &rwnx_band_2GHz;
+//#ifdef USE_5G
+ if(rwnx_hw->band_5g_support){
+ wiphy->bands[NL80211_BAND_5GHZ] = &rwnx_band_5GHz;
+ }
+//#endif
+ wiphy->interface_modes =
+ BIT(NL80211_IFTYPE_STATION) |
+ BIT(NL80211_IFTYPE_AP) |
+ BIT(NL80211_IFTYPE_AP_VLAN) |
+ BIT(NL80211_IFTYPE_P2P_CLIENT) |
+ BIT(NL80211_IFTYPE_P2P_GO) |
+ #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 7, 0)
+ #ifndef CONFIG_USE_P2P0
+ BIT(NL80211_IFTYPE_P2P_DEVICE) |
+ #endif
+ #endif
+ BIT(NL80211_IFTYPE_MONITOR);
+
+#ifdef CONFIG_GPIO_WAKEUP
+ /* Set WoWLAN flags */
+ printk("%s Wowlan support\r\n", __func__);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0)
+ wiphy->wowlan = &aic_wowlan_support;
+#else
+ wiphy->wowlan.flags = aic_wowlan_support.flags;
+#endif
+#endif
+
+ wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL |
+ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0))
+ WIPHY_FLAG_HAS_CHANNEL_SWITCH |
+ #endif
+ WIPHY_FLAG_4ADDR_STATION |
+ WIPHY_FLAG_4ADDR_AP;
+
+ #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)
+ wiphy->max_num_csa_counters = BCN_MAX_CSA_CPT;
+ #endif
+
+ wiphy->max_remain_on_channel_duration = rwnx_hw->mod_params->roc_dur_max;
+
+ wiphy->features |= NL80211_FEATURE_NEED_OBSS_SCAN |
+ NL80211_FEATURE_SK_TX_STATUS |
+ NL80211_FEATURE_VIF_TXPOWER |
+ #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)
+ NL80211_FEATURE_ACTIVE_MONITOR |
+ #endif
+ #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)
+ NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE |
+ #endif
+ 0;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0) || defined(CONFIG_WPA3_FOR_OLD_KERNEL)
+ wiphy->features |= NL80211_FEATURE_SAE;
+#endif
+
+ if (rwnx_mod_params.tdls)
+ /* TDLS support */
+ wiphy->features |= NL80211_FEATURE_TDLS_CHANNEL_SWITCH;
+
+ wiphy->iface_combinations = rwnx_combinations;
+ /* -1 not to include combination with radar detection, will be re-added in
+ rwnx_handle_dynparams if supported */
+ wiphy->n_iface_combinations = ARRAY_SIZE(rwnx_combinations) - 1;
+ wiphy->reg_notifier = rwnx_reg_notifier;
+
+ wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
+
+ rwnx_enable_wapi(rwnx_hw);
+
+ wiphy->cipher_suites = cipher_suites;
+ wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites) - NB_RESERVED_CIPHER;
+
+ rwnx_hw->ext_capa[0] = WLAN_EXT_CAPA1_EXT_CHANNEL_SWITCHING;
+ rwnx_hw->ext_capa[7] = WLAN_EXT_CAPA8_OPMODE_NOTIF;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
+ wiphy->extended_capabilities = rwnx_hw->ext_capa;
+ wiphy->extended_capabilities_mask = rwnx_hw->ext_capa;
+ wiphy->extended_capabilities_len = ARRAY_SIZE(rwnx_hw->ext_capa);
+#endif
+#ifdef CONFIG_SCHED_SCAN
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)
+ wiphy->max_sched_scan_reqs = 1;
+#endif
+ wiphy->max_sched_scan_ssids = SCAN_SSID_MAX;//16;
+ wiphy->max_match_sets = SCAN_SSID_MAX;//16;
+ wiphy->max_sched_scan_ie_len = 2048;
+#endif//CONFIG_SCHED_SCAN
+
+ tasklet_init(&rwnx_hw->task, rwnx_task, (unsigned long)rwnx_hw);
+
+ //init ic rf
+ if((ret = rwnx_ic_rf_init(rwnx_hw))){
+ goto err_lmac_reqs;
+ }
+
+ if ((ret = rwnx_send_get_macaddr_req(rwnx_hw, (struct mm_get_mac_addr_cfm *)mac_addr_efuse)))
+ goto err_lmac_reqs;
+ if (mac_addr_efuse[0] | mac_addr_efuse[1] | mac_addr_efuse[2] | mac_addr_efuse[3])
+ {
+ memcpy(init_conf.mac_addr, mac_addr_efuse, ETH_ALEN);
+ }else{
+ AICWFDBG(LOGERROR, "no mac address in efuse!");
+ }
+
+ AICWFDBG(LOGINFO, "get macaddr:%x,%x\r\n", mac_addr_efuse[0], mac_addr_efuse[5]);
+
+
+ memcpy(wiphy->perm_addr, init_conf.mac_addr, ETH_ALEN);
+
+ /* Reset FW */
+ if ((ret = rwnx_send_reset(rwnx_hw)))
+ goto err_lmac_reqs;
+
+ if ((ret = rwnx_send_version_req(rwnx_hw, &rwnx_hw->version_cfm)))
+ goto err_lmac_reqs;
+ rwnx_set_vers(rwnx_hw);
+
+ if ((ret = rwnx_handle_dynparams(rwnx_hw, rwnx_hw->wiphy)))
+ goto err_lmac_reqs;
+
+ rwnx_enable_mesh(rwnx_hw);
+ rwnx_radar_detection_init(&rwnx_hw->radar);
+
+ /* Set parameters to firmware */
+
+ if (rwnx_hw->usbdev->chipid == PRODUCT_ID_AIC8801 ||
+ ((rwnx_hw->usbdev->chipid == PRODUCT_ID_AIC8800DC ||
+ rwnx_hw->usbdev->chipid == PRODUCT_ID_AIC8800DW ||
+ rwnx_hw->usbdev->chipid == PRODUCT_ID_AIC8800D81) && testmode == 0)) {
+ rwnx_send_me_config_req(rwnx_hw);
+ }
+
+ /* Only monitor mode supported when custom channels are enabled */
+ if (rwnx_mod_params.custchan) {
+ rwnx_limits[0].types = BIT(NL80211_IFTYPE_MONITOR);
+ rwnx_limits_dfs[0].types = BIT(NL80211_IFTYPE_MONITOR);
+ }
+
+ aicwf_vendor_init(wiphy);
+
+ if ((ret = wiphy_register(wiphy))) {
+ wiphy_err(wiphy, "Could not register wiphy device\n");
+ goto err_register_wiphy;
+ }
+
+ /* Update regulatory (if needed) and set channel parameters to firmware
+ (must be done after WiPHY registration) */
+ rwnx_custregd(rwnx_hw, wiphy);
+ if (rwnx_hw->usbdev->chipid == PRODUCT_ID_AIC8801 ||
+ ((rwnx_hw->usbdev->chipid == PRODUCT_ID_AIC8800DC ||
+ rwnx_hw->usbdev->chipid == PRODUCT_ID_AIC8800DW ||
+ rwnx_hw->usbdev->chipid == PRODUCT_ID_AIC8800D81) && testmode == 0)) {
+ rwnx_send_me_chan_config_req(rwnx_hw);
+ #ifdef CONFIG_COEX
+ rwnx_send_coex_req(rwnx_hw, 0, 1);
+ #endif
+ }
+ *platform_data = rwnx_hw;
+
+#ifdef CONFIG_DEBUG_FS
+ if ((ret = rwnx_dbgfs_register(rwnx_hw, "rwnx"))) {
+ wiphy_err(wiphy, "Failed to register debugfs entries");
+ goto err_debugfs;
+ }
+#endif
+ rtnl_lock();
+
+ /* Add an initial station interface */
+ vif = rwnx_interface_add(rwnx_hw, "wlan%d", NET_NAME_UNKNOWN,
+ NL80211_IFTYPE_STATION, NULL);
+
+
+ #ifdef CONFIG_RWNX_MON_DATA
+ /* Add an initial station interface */
+ vif = rwnx_interface_add(rwnx_hw, "wlan%d", 1,
+ NL80211_IFTYPE_MONITOR, NULL);
+ #endif
+
+ rtnl_unlock();
+
+ if (!vif) {
+ wiphy_err(wiphy, "Failed to instantiate a network device\n");
+ ret = -ENOMEM;
+ goto err_add_interface;
+ }
+
+#if 0
+ wiphy_info(wiphy, "New interface create %s", vif->ndev->name);
+#endif
+
+ AICWFDBG(LOGINFO, "New interface create %s\r\n", vif->ndev->name);
+
+#ifdef CONFIG_USE_P2P0
+
+ rtnl_lock();
+ /* Add an initial p2p0 interface */
+ vif = rwnx_interface_add(rwnx_hw, "p2p%d", NET_NAME_UNKNOWN,
+ NL80211_IFTYPE_STATION, NULL);
+ vif->is_p2p_vif = 1;
+ rtnl_unlock();
+
+ if (!vif) {
+ wiphy_err(wiphy, "Failed to instantiate a network device\n");
+ ret = -ENOMEM;
+ goto err_add_interface;
+ }
+
+ //wiphy_info(wiphy, "New interface create %s", vif->ndev->name);
+ AICWFDBG(LOGINFO, "New interface create %s \r\n", vif->ndev->name);
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0)
+ init_timer(&rwnx_hw->p2p_alive_timer);
+ rwnx_hw->p2p_alive_timer.data = (unsigned long)vif;
+ rwnx_hw->p2p_alive_timer.function = aicwf_p2p_alive_timeout;
+#else
+ timer_setup(&rwnx_hw->p2p_alive_timer, aicwf_p2p_alive_timeout, 0);
+#endif
+ rwnx_hw->is_p2p_alive = 0;
+ rwnx_hw->is_p2p_connected = 0;
+ atomic_set(&rwnx_hw->p2p_alive_timer_count, 0);
+#endif
+
+ return 0;
+
+err_add_interface:
+#ifdef CONFIG_DEBUG_FS
+ rwnx_dbgfs_unregister(rwnx_hw);
+err_debugfs:
+#endif
+if(rwnx_hw->wiphy){
+ wiphy_unregister(rwnx_hw->wiphy);
+}
+err_register_wiphy:
+err_lmac_reqs:
+ AICWFDBG(LOGERROR, "err_lmac_reqs\n");
+ //rwnx_fw_trace_dump(rwnx_hw);
+ rwnx_platform_off(rwnx_hw, NULL);
+//err_platon:
+//err_config:
+ kmem_cache_destroy(rwnx_hw->sw_txhdr_cache);
+err_cache:
+ wiphy_free(wiphy);
+err_out:
+ return ret;
+}
+
+/**
+ *
+ */
+
+void rwnx_cfg80211_deinit(struct rwnx_hw *rwnx_hw)
+{
+ struct mm_set_stack_start_cfm set_start_cfm;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ rwnx_send_set_stack_start_req(rwnx_hw, 0, 0, 0, 0, &set_start_cfm);
+
+ rwnx_hw->fwlog_en = 0;
+
+#ifdef CONFIG_DEBUG_FS
+ rwnx_dbgfs_unregister(rwnx_hw);
+#endif
+ flush_workqueue(rwnx_hw->apmStaloss_wq);
+ destroy_workqueue(rwnx_hw->apmStaloss_wq);
+
+ rwnx_wdev_unregister(rwnx_hw);
+ if(rwnx_hw->wiphy){
+ AICWFDBG(LOGINFO, "%s wiphy_unregister \r\n", __func__);
+ wiphy_unregister(rwnx_hw->wiphy);
+ }
+ rwnx_radar_detection_deinit(&rwnx_hw->radar);
+ rwnx_platform_off(rwnx_hw, NULL);
+ kmem_cache_destroy(rwnx_hw->sw_txhdr_cache);
+ if(rwnx_hw->wiphy){
+ wiphy_free(rwnx_hw->wiphy);
+ }
+}
+
+static void aicsmac_driver_register(void)
+{
+#ifdef AICWF_SDIO_SUPPORT
+ aicwf_sdio_register();
+#endif
+#ifdef AICWF_USB_SUPPORT
+ aicwf_usb_register();
+#endif
+#ifdef AICWF_PCIE_SUPPORT
+ aicwf_pcie_register();
+#endif
+}
+
+//static DECLARE_WORK(aicsmac_driver_work, aicsmac_driver_register);
+
+struct completion hostif_register_done;
+
+#define REGISTRATION_TIMEOUT 9000
+
+void aicwf_hostif_ready(void)
+{
+ g_rwnx_plat->enabled = true;
+ complete(&hostif_register_done);
+}
+
+static int __init rwnx_mod_init(void)
+{
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+ rwnx_print_version();
+ AICWFDBG(LOGINFO, "RELEASE DATE:%s \r\n", RELEASE_DATE);
+ rwnx_init_cmd_array();
+
+ sema_init(&aicwf_deinit_sem, 1);
+ atomic_set(&aicwf_deinit_atomic, 1);
+
+ init_completion(&hostif_register_done);
+
+ aicsmac_driver_register();
+
+#ifdef AICWF_SDIO_SUPPORT
+ if ((wait_for_completion_timeout(&hostif_register_done, msecs_to_jiffies(REGISTRATION_TIMEOUT)) == 0)) {
+ AICWFDBG(LOGERROR, "register_driver timeout or error\n");
+ aicwf_sdio_exit();
+ return -ENODEV;
+}
+
+#endif /* AICWF_SDIO_SUPPORT */
+#ifdef AICWF_USB_SUPPORT
+ //aicwf_usb_exit();
+#endif /*AICWF_USB_SUPPORT */
+
+
+#ifdef AICWF_PCIE_SUPPORT
+ return rwnx_platform_register_drv();
+#else
+ return 0;
+#endif
+}
+
+/**
+ *
+ */
+static void __exit rwnx_mod_exit(void)
+{
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+#ifdef AICWF_PCIE_SUPPORT
+ rwnx_platform_unregister_drv();
+#endif
+
+#ifdef AICWF_SDIO_SUPPORT
+ aicwf_sdio_exit();
+#endif
+
+#ifdef AICWF_USB_SUPPORT
+ aicwf_usb_exit();
+#endif
+ rwnx_free_cmd_array();
+ AICWFDBG(LOGINFO, "%s exit\r\n", __func__);
+}
+
+module_init(rwnx_mod_init);
+module_exit(rwnx_mod_exit);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)
+MODULE_IMPORT_NS(VFS_internal_I_am_really_a_filesystem_and_am_NOT_a_driver);
+#endif
+MODULE_FIRMWARE(RWNX_CONFIG_FW_NAME);
+
+MODULE_DESCRIPTION(RW_DRV_DESCRIPTION);
+MODULE_VERSION(RWNX_VERS_MOD);
+MODULE_AUTHOR(RW_DRV_COPYRIGHT " " RW_DRV_AUTHOR);
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_main.h b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_main.h
new file mode 100644
index 000000000000..0728b4f77a62
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_main.h
@@ -0,0 +1,38 @@
+/**
+ ******************************************************************************
+ *
+ * @file rwnx_main.h
+ *
+ * Copyright (C) RivieraWaves 2012-2019
+ *
+ ******************************************************************************
+ */
+
+#ifndef _RWNX_MAIN_H_
+#define _RWNX_MAIN_H_
+
+#include "rwnx_defs.h"
+
+typedef struct _android_wifi_priv_cmd {
+ char *buf;
+ int used_len;
+ int total_len;
+} android_wifi_priv_cmd;
+
+#ifdef CONFIG_COMPAT
+typedef struct _compat_android_wifi_priv_cmd {
+ compat_caddr_t buf;
+ int used_len;
+ int total_len;
+} compat_android_wifi_priv_cmd;
+#endif /* CONFIG_COMPAT */
+
+int rwnx_cfg80211_init(struct rwnx_plat *rwnx_plat, void **platform_data);
+void rwnx_cfg80211_deinit(struct rwnx_hw *rwnx_hw);
+extern int testmode;
+extern u8 chip_id;
+extern u8 chip_sub_id;
+extern u8 chip_mcu_id;
+
+
+#endif /* _RWNX_MAIN_H_ */
diff --git a/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_mesh.c b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_mesh.c
new file mode 100644
index 000000000000..791abab4cb56
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_mesh.c
@@ -0,0 +1,42 @@
+/**
+ ****************************************************************************************
+ *
+ * @file rwnx_mesh.c
+ *
+ * Copyright (C) RivieraWaves 2016-2019
+ *
+ ****************************************************************************************
+ */
+
+/**
+ * INCLUDE FILES
+ ****************************************************************************************
+ */
+
+#include "rwnx_mesh.h"
+
+/**
+ * FUNCTION DEFINITIONS
+ ****************************************************************************************
+ */
+
+struct rwnx_mesh_proxy *rwnx_get_mesh_proxy_info(struct rwnx_vif *p_rwnx_vif, u8 *p_sta_addr, bool local)
+{
+ struct rwnx_mesh_proxy *p_mesh_proxy = NULL;
+ struct rwnx_mesh_proxy *p_cur_proxy;
+
+ /* Look for proxied devices with provided address */
+ list_for_each_entry(p_cur_proxy, &p_rwnx_vif->ap.proxy_list, list) {
+ if (p_cur_proxy->local != local) {
+ continue;
+ }
+
+ if (!memcmp(&p_cur_proxy->ext_sta_addr, p_sta_addr, ETH_ALEN)) {
+ p_mesh_proxy = p_cur_proxy;
+ break;
+ }
+ }
+
+ /* Return the found information */
+ return p_mesh_proxy;
+}
diff --git a/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_mesh.h b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_mesh.h
new file mode 100644
index 000000000000..db8950f160de
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_mesh.h
@@ -0,0 +1,45 @@
+/**
+ ****************************************************************************************
+ *
+ * @file rwnx_mesh.h
+ *
+ * @brief VHT Beamformer function declarations
+ *
+ * Copyright (C) RivieraWaves 2016-2019
+ *
+ ****************************************************************************************
+ */
+
+#ifndef _RWNX_MESH_H_
+#define _RWNX_MESH_H_
+
+/**
+ * INCLUDE FILES
+ ****************************************************************************************
+ */
+
+#include "rwnx_defs.h"
+
+/**
+ * DEFINES
+ ****************************************************************************************
+ */
+
+/**
+ * TYPE DEFINITIONS
+ ****************************************************************************************
+ */
+
+/**
+ * FUNCTION DECLARATIONS
+ ****************************************************************************************
+ */
+
+/**
+ ****************************************************************************************
+ * @brief TODO [LT]
+ ****************************************************************************************
+ */
+struct rwnx_mesh_proxy *rwnx_get_mesh_proxy_info(struct rwnx_vif *p_rwnx_vif, u8 *p_sta_addr, bool local);
+
+#endif /* _RWNX_MESH_H_ */
diff --git a/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_mod_params.c b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_mod_params.c
new file mode 100644
index 000000000000..803341419a55
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_mod_params.c
@@ -0,0 +1,1755 @@
+/**
+******************************************************************************
+*
+* @file rwnx_mod_params.c
+*
+* @brief Set configuration according to modules parameters
+*
+* Copyright (C) RivieraWaves 2012-2019
+*
+******************************************************************************
+*/
+#include <linux/module.h>
+#include <linux/rtnetlink.h>
+
+#include "rwnx_defs.h"
+#include "rwnx_tx.h"
+#include "hal_desc.h"
+#include "rwnx_cfgfile.h"
+#include "rwnx_dini.h"
+#include "reg_access.h"
+#include "rwnx_compat.h"
+
+#ifdef CONFIG_RWNX_FULLMAC
+#define COMMON_PARAM(name, default_softmac, default_fullmac) \
+ .name = default_fullmac,
+#define SOFTMAC_PARAM(name, default)
+#define FULLMAC_PARAM(name, default) .name = default,
+#endif /* CONFIG_RWNX_FULLMAC */
+
+struct rwnx_mod_params rwnx_mod_params = {
+ /* common parameters */
+ COMMON_PARAM(ht_on, true, true)
+ COMMON_PARAM(vht_on, true, true)
+ COMMON_PARAM(he_on, true, true)
+ COMMON_PARAM(mcs_map, IEEE80211_VHT_MCS_SUPPORT_0_9, IEEE80211_VHT_MCS_SUPPORT_0_9)
+ COMMON_PARAM(he_mcs_map, IEEE80211_HE_MCS_SUPPORT_0_11, IEEE80211_HE_MCS_SUPPORT_0_11)
+ COMMON_PARAM(he_ul_on, false, false)
+ COMMON_PARAM(ldpc_on, true, true)
+ COMMON_PARAM(stbc_on, true, true)
+ COMMON_PARAM(gf_rx_on, false, false)
+ COMMON_PARAM(phy_cfg, 2, 2)
+ COMMON_PARAM(uapsd_timeout, 300, 300)
+ COMMON_PARAM(ap_uapsd_on, true, true)
+ COMMON_PARAM(sgi, true, true)
+ COMMON_PARAM(sgi80, true, true)
+ COMMON_PARAM(use_2040, 1, 1)
+ COMMON_PARAM(nss, 1, 1)
+ COMMON_PARAM(amsdu_rx_max, 2, 2)
+ COMMON_PARAM(bfmee, true, true)
+ COMMON_PARAM(bfmer, false, false)
+ COMMON_PARAM(mesh, true, true)
+ COMMON_PARAM(murx, true, true)
+ COMMON_PARAM(mutx, true, true)
+ COMMON_PARAM(mutx_on, true, true)
+ COMMON_PARAM(use_80, true, true)
+ COMMON_PARAM(custregd, true, true)
+ COMMON_PARAM(custchan, false, false)
+ COMMON_PARAM(roc_dur_max, 500, 500)
+ COMMON_PARAM(listen_itv, 0, 0)
+ COMMON_PARAM(listen_bcmc, true, true)
+ COMMON_PARAM(lp_clk_ppm, 20, 20)
+ COMMON_PARAM(ps_on, true, true)
+ COMMON_PARAM(tx_lft, RWNX_TX_LIFETIME_MS, RWNX_TX_LIFETIME_MS)
+ COMMON_PARAM(amsdu_maxnb, NX_TX_PAYLOAD_MAX, NX_TX_PAYLOAD_MAX)
+ // By default, only enable UAPSD for Voice queue (see IEEE80211_DEFAULT_UAPSD_QUEUE comment)
+ COMMON_PARAM(uapsd_queues, IEEE80211_WMM_IE_STA_QOSINFO_AC_VO, IEEE80211_WMM_IE_STA_QOSINFO_AC_VO)
+ COMMON_PARAM(tdls, false, false)
+ COMMON_PARAM(uf, false, false)
+ COMMON_PARAM(auto_reply, false, false)
+ COMMON_PARAM(ftl, "", "")
+ COMMON_PARAM(dpsm, false, false)
+
+ /* SOFTMAC only parameters */
+ SOFTMAC_PARAM(mfp_on, false)
+ SOFTMAC_PARAM(gf_on, false)
+ SOFTMAC_PARAM(bwsig_on, true)
+ SOFTMAC_PARAM(dynbw_on, true)
+ SOFTMAC_PARAM(agg_tx, true)
+ SOFTMAC_PARAM(amsdu_force, 2)
+ SOFTMAC_PARAM(rc_probes_on, false)
+ SOFTMAC_PARAM(cmon, true)
+ SOFTMAC_PARAM(hwscan, true)
+ SOFTMAC_PARAM(autobcn, true)
+ SOFTMAC_PARAM(dpsm, true)
+
+ /* FULLMAC only parameters */
+ FULLMAC_PARAM(ant_div, true)
+};
+
+#ifdef CONFIG_RWNX_FULLMAC
+/* FULLMAC specific parameters*/
+module_param_named(ant_div, rwnx_mod_params.ant_div, bool, S_IRUGO);
+MODULE_PARM_DESC(ant_div, "Enable Antenna Diversity (Default: 1)");
+#endif /* CONFIG_RWNX_FULLMAC */
+
+module_param_named(ht_on, rwnx_mod_params.ht_on, bool, S_IRUGO);
+MODULE_PARM_DESC(ht_on, "Enable HT (Default: 1)");
+
+module_param_named(vht_on, rwnx_mod_params.vht_on, bool, S_IRUGO);
+MODULE_PARM_DESC(vht_on, "Enable VHT (Default: 1)");
+
+module_param_named(he_on, rwnx_mod_params.he_on, bool, S_IRUGO);
+MODULE_PARM_DESC(he_on, "Enable HE (Default: 1)");
+
+module_param_named(mcs_map, rwnx_mod_params.mcs_map, int, S_IRUGO);
+MODULE_PARM_DESC(mcs_map, "VHT MCS map value 0: MCS0_7, 1: MCS0_8, 2: MCS0_9"
+ " (Default: 2)");
+
+module_param_named(he_mcs_map, rwnx_mod_params.he_mcs_map, int, S_IRUGO);
+MODULE_PARM_DESC(he_mcs_map, "HE MCS map value 0: MCS0_7, 1: MCS0_9, 2: MCS0_11"
+ " (Default: 2)");
+
+module_param_named(he_ul_on, rwnx_mod_params.he_ul_on, bool, S_IRUGO);
+MODULE_PARM_DESC(he_ul_on, "Enable HE OFDMA UL (Default: 0)");
+
+module_param_named(amsdu_maxnb, rwnx_mod_params.amsdu_maxnb, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(amsdu_maxnb, "Maximum number of MSDUs inside an A-MSDU in TX: (Default: NX_TX_PAYLOAD_MAX)");
+
+module_param_named(ps_on, rwnx_mod_params.ps_on, bool, S_IRUGO);
+MODULE_PARM_DESC(ps_on, "Enable PowerSaving (Default: 1-Enabled)");
+
+module_param_named(tx_lft, rwnx_mod_params.tx_lft, int, 0644);
+MODULE_PARM_DESC(tx_lft, "Tx lifetime (ms) - setting it to 0 disables retries "
+ "(Default: "__stringify(RWNX_TX_LIFETIME_MS)")");
+
+module_param_named(ldpc_on, rwnx_mod_params.ldpc_on, bool, S_IRUGO);
+MODULE_PARM_DESC(ldpc_on, "Enable LDPC (Default: 1)");
+
+module_param_named(stbc_on, rwnx_mod_params.stbc_on, bool, S_IRUGO);
+MODULE_PARM_DESC(stbc_on, "Enable STBC in RX (Default: 1)");
+
+module_param_named(gf_rx_on, rwnx_mod_params.gf_rx_on, bool, S_IRUGO);
+MODULE_PARM_DESC(gf_rx_on, "Enable HT greenfield in reception (Default: 1)");
+
+module_param_named(phycfg, rwnx_mod_params.phy_cfg, int, S_IRUGO);
+MODULE_PARM_DESC(phycfg,
+ "0 <= phycfg <= 5 : RF Channel Conf (Default: 2(C0-A1-B2))");
+
+module_param_named(uapsd_timeout, rwnx_mod_params.uapsd_timeout, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(uapsd_timeout,
+ "UAPSD Timer timeout, in ms (Default: 300). If 0, UAPSD is disabled");
+
+module_param_named(uapsd_queues, rwnx_mod_params.uapsd_queues, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(uapsd_queues, "UAPSD Queues, integer value, must be seen as a bitfield\n"
+ " Bit 0 = VO\n"
+ " Bit 1 = VI\n"
+ " Bit 2 = BK\n"
+ " Bit 3 = BE\n"
+ " -> uapsd_queues=7 will enable uapsd for VO, VI and BK queues");
+
+module_param_named(ap_uapsd_on, rwnx_mod_params.ap_uapsd_on, bool, S_IRUGO);
+MODULE_PARM_DESC(ap_uapsd_on, "Enable UAPSD in AP mode (Default: 1)");
+
+module_param_named(sgi, rwnx_mod_params.sgi, bool, S_IRUGO);
+MODULE_PARM_DESC(sgi, "Advertise Short Guard Interval support (Default: 1)");
+
+module_param_named(sgi80, rwnx_mod_params.sgi80, bool, S_IRUGO);
+MODULE_PARM_DESC(sgi80, "Advertise Short Guard Interval support for 80MHz (Default: 1)");
+
+module_param_named(use_2040, rwnx_mod_params.use_2040, bool, S_IRUGO);
+MODULE_PARM_DESC(use_2040, "Use tweaked 20-40MHz mode (Default: 1)");
+
+module_param_named(use_80, rwnx_mod_params.use_80, bool, S_IRUGO);
+MODULE_PARM_DESC(use_80, "Enable 80MHz (Default: 1)");
+
+module_param_named(custregd, rwnx_mod_params.custregd, bool, S_IRUGO);
+MODULE_PARM_DESC(custregd,
+ "Use permissive custom regulatory rules (for testing ONLY) (Default: 0)");
+
+module_param_named(custchan, rwnx_mod_params.custchan, bool, S_IRUGO);
+MODULE_PARM_DESC(custchan,
+ "Extend channel set to non-standard channels (for testing ONLY) (Default: 0)");
+
+module_param_named(nss, rwnx_mod_params.nss, int, S_IRUGO);
+MODULE_PARM_DESC(nss, "1 <= nss <= 2 : Supported number of Spatial Streams (Default: 1)");
+
+module_param_named(amsdu_rx_max, rwnx_mod_params.amsdu_rx_max, int, S_IRUGO);
+MODULE_PARM_DESC(amsdu_rx_max, "0 <= amsdu_rx_max <= 2 : Maximum A-MSDU size supported in RX\n"
+ " 0: 3895 bytes\n"
+ " 1: 7991 bytes\n"
+ " 2: 11454 bytes\n"
+ " This value might be reduced according to the FW capabilities.\n"
+ " Default: 2");
+
+module_param_named(bfmee, rwnx_mod_params.bfmee, bool, S_IRUGO);
+MODULE_PARM_DESC(bfmee, "Enable Beamformee Capability (Default: 1-Enabled)");
+
+module_param_named(bfmer, rwnx_mod_params.bfmer, bool, S_IRUGO);
+MODULE_PARM_DESC(bfmer, "Enable Beamformer Capability (Default: 0-Disabled)");
+
+module_param_named(mesh, rwnx_mod_params.mesh, bool, S_IRUGO);
+MODULE_PARM_DESC(mesh, "Enable Meshing Capability (Default: 1-Enabled)");
+
+module_param_named(murx, rwnx_mod_params.murx, bool, S_IRUGO);
+MODULE_PARM_DESC(murx, "Enable MU-MIMO RX Capability (Default: 1-Enabled)");
+
+module_param_named(mutx, rwnx_mod_params.mutx, bool, S_IRUGO);
+MODULE_PARM_DESC(mutx, "Enable MU-MIMO TX Capability (Default: 1-Enabled)");
+
+module_param_named(mutx_on, rwnx_mod_params.mutx_on, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(mutx_on, "Enable MU-MIMO transmissions (Default: 1-Enabled)");
+
+module_param_named(roc_dur_max, rwnx_mod_params.roc_dur_max, int, S_IRUGO);
+MODULE_PARM_DESC(roc_dur_max, "Maximum Remain on Channel duration");
+
+module_param_named(listen_itv, rwnx_mod_params.listen_itv, int, S_IRUGO);
+MODULE_PARM_DESC(listen_itv, "Maximum listen interval");
+
+module_param_named(listen_bcmc, rwnx_mod_params.listen_bcmc, bool, S_IRUGO);
+MODULE_PARM_DESC(listen_bcmc, "Wait for BC/MC traffic following DTIM beacon");
+
+module_param_named(lp_clk_ppm, rwnx_mod_params.lp_clk_ppm, int, S_IRUGO);
+MODULE_PARM_DESC(lp_clk_ppm, "Low Power Clock accuracy of the local device");
+
+module_param_named(tdls, rwnx_mod_params.tdls, bool, S_IRUGO);
+MODULE_PARM_DESC(tdls, "Enable TDLS (Default: 1-Enabled)");
+
+module_param_named(uf, rwnx_mod_params.uf, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(uf, "Enable Unsupported HT Frame Logging (Default: 0-Disabled)");
+
+module_param_named(auto_reply, rwnx_mod_params.auto_reply, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(auto_reply, "Enable Monitor MacAddr Auto-Reply (Default: 0-Disabled)");
+
+module_param_named(ftl, rwnx_mod_params.ftl, charp, S_IRUGO);
+MODULE_PARM_DESC(ftl, "Firmware trace level (Default: \"\")");
+
+module_param_named(dpsm, rwnx_mod_params.dpsm, bool, S_IRUGO);
+MODULE_PARM_DESC(dpsm, "Enable Dynamic PowerSaving (Default: 1-Enabled)");
+
+#ifdef DEFAULT_COUNTRY_CODE
+char default_ccode[4] = DEFAULT_COUNTRY_CODE;
+#else
+char default_ccode[4] = "00";
+#endif
+
+char country_code[4];
+module_param_string(country_code, country_code, 4, 0600);
+
+#define RWNX_REG_RULE(start, end, bw, reg_flags) REG_RULE(start, end, bw, 0, 0, reg_flags)
+struct rwnx_regdomain {
+ char country_code[4];
+ struct ieee80211_regdomain *prRegdRules;
+};
+
+static const int mcs_map_to_rate[4][3] = {
+ [PHY_CHNL_BW_20][IEEE80211_VHT_MCS_SUPPORT_0_7] = 65,
+ [PHY_CHNL_BW_20][IEEE80211_VHT_MCS_SUPPORT_0_8] = 78,
+ [PHY_CHNL_BW_20][IEEE80211_VHT_MCS_SUPPORT_0_9] = 78,
+ [PHY_CHNL_BW_40][IEEE80211_VHT_MCS_SUPPORT_0_7] = 135,
+ [PHY_CHNL_BW_40][IEEE80211_VHT_MCS_SUPPORT_0_8] = 162,
+ [PHY_CHNL_BW_40][IEEE80211_VHT_MCS_SUPPORT_0_9] = 180,
+ [PHY_CHNL_BW_80][IEEE80211_VHT_MCS_SUPPORT_0_7] = 292,
+ [PHY_CHNL_BW_80][IEEE80211_VHT_MCS_SUPPORT_0_8] = 351,
+ [PHY_CHNL_BW_80][IEEE80211_VHT_MCS_SUPPORT_0_9] = 390,
+ [PHY_CHNL_BW_160][IEEE80211_VHT_MCS_SUPPORT_0_7] = 585,
+ [PHY_CHNL_BW_160][IEEE80211_VHT_MCS_SUPPORT_0_8] = 702,
+ [PHY_CHNL_BW_160][IEEE80211_VHT_MCS_SUPPORT_0_9] = 780,
+};
+
+#define MAX_VHT_RATE(map, nss, bw) (mcs_map_to_rate[bw][map] * (nss))
+
+extern struct ieee80211_regdomain *reg_regdb[];
+
+char ccode_channels[200];
+int index_for_channel_list = 0;
+module_param_string(ccode_channels, ccode_channels, 200, 0600);
+
+void rwnx_get_countrycode_channels(struct wiphy *wiphy,
+ struct ieee80211_regdomain *regdomain){
+ enum nl80211_band band;
+ struct ieee80211_supported_band *sband;
+ int channel_index;
+ int rule_index;
+ int band_num = 0;
+ int rule_num = regdomain->n_reg_rules;
+ int start_freq = 0;
+ int end_freq = 0;
+ int center_freq = 0;
+ char channel[4];
+#ifdef CONFIG_USE_WIRELESS_EXT
+ struct rwnx_hw *rwnx_hw = wiphy_priv(wiphy);
+ int support_freqs_counter = 0;
+#endif
+
+ band_num = NUM_NL80211_BANDS;
+
+ memset(ccode_channels, 0, 200);
+
+ index_for_channel_list = 0;
+
+ for (band = 0; band < band_num; band++) {
+ sband = wiphy->bands[band];// bands: 0:2.4G 1:5G 2:60G
+ if (!sband)
+ continue;
+
+ for (channel_index = 0; channel_index < sband->n_channels; channel_index++) {
+ for(rule_index = 0; rule_index < rule_num; rule_index++){
+ start_freq = regdomain->reg_rules[rule_index].freq_range.start_freq_khz/1000;
+ end_freq = regdomain->reg_rules[rule_index].freq_range.end_freq_khz/1000;
+ center_freq = sband->channels[channel_index].center_freq;
+ if((center_freq - 10) >= start_freq && (center_freq + 10) <= end_freq){
+#ifdef CONFIG_USE_WIRELESS_EXT
+ rwnx_hw->support_freqs[support_freqs_counter++] = center_freq;
+#endif
+ sprintf(channel, "%d",ieee80211_frequency_to_channel(center_freq));
+
+ memcpy(ccode_channels + index_for_channel_list, channel, strlen(channel));
+
+ index_for_channel_list += strlen(channel);
+
+ memcpy(ccode_channels + index_for_channel_list, " ", 1);
+
+ index_for_channel_list += 1;
+ break;
+
+ }
+ }
+ }
+ }
+#ifdef CONFIG_USE_WIRELESS_EXT
+ rwnx_hw->support_freqs_number = support_freqs_counter;
+#endif
+ AICWFDBG(LOGINFO, "%s support channel:%s\r\n", __func__, ccode_channels);
+}
+
+
+struct ieee80211_regdomain *getRegdomainFromRwnxDBIndex(struct wiphy *wiphy,
+ int index)
+{
+ u8 idx;
+
+ idx = index;
+
+ memset(country_code, 0, 4);
+ country_code[0] = reg_regdb[idx]->alpha2[0];
+ country_code[1] = reg_regdb[idx]->alpha2[1];
+
+ AICWFDBG(LOGINFO, "%s set ccode:%s \r\n", __func__, country_code);
+
+ rwnx_get_countrycode_channels(wiphy, reg_regdb[idx]);
+
+ return reg_regdb[idx];
+}
+
+extern int reg_regdb_size;
+
+struct ieee80211_regdomain *getRegdomainFromRwnxDB(struct wiphy *wiphy,
+ char *alpha2)
+{
+ u8 idx;
+
+ memset(country_code, 0, 4);
+
+ AICWFDBG(LOGINFO, "%s set ccode:%s \r\n", __func__, alpha2);
+
+ idx = 0;
+
+ while (reg_regdb[idx]){
+ if((reg_regdb[idx]->alpha2[0] == alpha2[0]) &&
+ (reg_regdb[idx]->alpha2[1] == alpha2[1])){
+ memcpy(country_code, alpha2, 2);
+ rwnx_get_countrycode_channels(wiphy, reg_regdb[idx]);
+ return reg_regdb[idx];
+ }
+ idx++;
+ if(idx == reg_regdb_size){
+ break;
+ }
+ }
+
+ AICWFDBG(LOGERROR, "%s(): Error, wrong country = %s\n",
+ __func__, alpha2);
+ AICWFDBG(LOGINFO, "Set as default 00\n");
+
+ memcpy(country_code, default_ccode, sizeof(default_ccode));
+ rwnx_get_countrycode_channels(wiphy, reg_regdb[0]);
+
+ return reg_regdb[0];
+}
+
+
+/**
+ * Do some sanity check
+ *
+ */
+#if 0
+static int rwnx_check_fw_hw_feature(struct rwnx_hw *rwnx_hw,
+ struct wiphy *wiphy)
+{
+ u32_l sys_feat = rwnx_hw->version_cfm.features;
+ u32_l mac_feat = rwnx_hw->version_cfm.version_machw_1;
+ u32_l phy_feat = rwnx_hw->version_cfm.version_phy_1;
+ u32_l phy_vers = rwnx_hw->version_cfm.version_phy_2;
+ u16_l max_sta_nb = rwnx_hw->version_cfm.max_sta_nb;
+ u8_l max_vif_nb = rwnx_hw->version_cfm.max_vif_nb;
+ int bw, res = 0;
+ int amsdu_rx;
+
+ if (!rwnx_hw->mod_params->custregd)
+ rwnx_hw->mod_params->custchan = false;
+
+ if (rwnx_hw->mod_params->custchan) {
+ rwnx_hw->mod_params->mesh = false;
+ rwnx_hw->mod_params->tdls = false;
+ }
+
+#ifdef CONFIG_RWNX_FULLMAC
+
+ if (!(sys_feat & BIT(MM_FEAT_UMAC_BIT))) {
+ wiphy_err(wiphy,
+ "Loading softmac firmware with fullmac driver\n");
+ res = -1;
+ }
+
+ if (!(sys_feat & BIT(MM_FEAT_ANT_DIV_BIT))) {
+ rwnx_hw->mod_params->ant_div = false;
+ }
+
+#endif /* CONFIG_RWNX_FULLMAC */
+
+ if (!(sys_feat & BIT(MM_FEAT_VHT_BIT))) {
+ rwnx_hw->mod_params->vht_on = false;
+ }
+
+ // Check if HE is supported
+ if (!(sys_feat & BIT(MM_FEAT_HE_BIT))) {
+ rwnx_hw->mod_params->he_on = false;
+ rwnx_hw->mod_params->he_ul_on = false;
+ }
+
+ if (!(sys_feat & BIT(MM_FEAT_PS_BIT))) {
+ rwnx_hw->mod_params->ps_on = false;
+ }
+
+ /* AMSDU (non)support implies different shared structure definition
+ so insure that fw and drv have consistent compilation option */
+ if (sys_feat & BIT(MM_FEAT_AMSDU_BIT)) {
+#ifndef CONFIG_RWNX_SPLIT_TX_BUF
+ wiphy_err(wiphy,
+ "AMSDU enabled in firmware but support not compiled in driver\n");
+ res = -1;
+#else
+ if (rwnx_hw->mod_params->amsdu_maxnb > NX_TX_PAYLOAD_MAX)
+ rwnx_hw->mod_params->amsdu_maxnb = NX_TX_PAYLOAD_MAX;
+#endif /* CONFIG_RWNX_SPLIT_TX_BUF */
+ } else {
+#ifdef CONFIG_RWNX_SPLIT_TX_BUF
+ wiphy_err(wiphy,
+ "AMSDU disabled in firmware but support compiled in driver\n");
+ res = -1;
+#endif /* CONFIG_RWNX_SPLIT_TX_BUF */
+ }
+
+ if (!(sys_feat & BIT(MM_FEAT_UAPSD_BIT))) {
+ rwnx_hw->mod_params->uapsd_timeout = 0;
+ }
+
+ if (!(sys_feat & BIT(MM_FEAT_BFMEE_BIT))) {
+ rwnx_hw->mod_params->bfmee = false;
+ }
+
+ if ((sys_feat & BIT(MM_FEAT_BFMER_BIT))) {
+#ifndef CONFIG_RWNX_BFMER
+ wiphy_err(wiphy,
+ "BFMER enabled in firmware but support not compiled in driver\n");
+ res = -1;
+#endif /* CONFIG_RWNX_BFMER */
+ // Check PHY and MAC HW BFMER support and update parameter accordingly
+ if (!(phy_feat & MDM_BFMER_BIT) || !(mac_feat & NXMAC_BFMER_BIT)) {
+ rwnx_hw->mod_params->bfmer = false;
+ // Disable the feature in the bitfield so that it won't be displayed
+ sys_feat &= ~BIT(MM_FEAT_BFMER_BIT);
+ }
+ } else {
+#ifdef CONFIG_RWNX_BFMER
+ wiphy_err(wiphy,
+ "BFMER disabled in firmware but support compiled in driver\n");
+ res = -1;
+#else
+ rwnx_hw->mod_params->bfmer = false;
+#endif /* CONFIG_RWNX_BFMER */
+ }
+
+ if (!(sys_feat & BIT(MM_FEAT_MESH_BIT))) {
+ rwnx_hw->mod_params->mesh = false;
+ }
+
+ if (!(sys_feat & BIT(MM_FEAT_TDLS_BIT))) {
+ rwnx_hw->mod_params->tdls = false;
+ }
+
+ if (!(sys_feat & BIT(MM_FEAT_UF_BIT))) {
+ rwnx_hw->mod_params->uf = false;
+ }
+
+#ifdef CONFIG_RWNX_FULLMAC
+ if ((sys_feat & BIT(MM_FEAT_MON_DATA_BIT))) {
+#ifndef CONFIG_RWNX_MON_DATA
+ wiphy_err(wiphy,
+ "Monitor+Data interface support (MON_DATA) is enabled in firmware but support not compiled in driver\n");
+ res = -1;
+#endif /* CONFIG_RWNX_MON_DATA */
+ } else {
+#ifdef CONFIG_RWNX_MON_DATA
+ wiphy_err(wiphy,
+ "Monitor+Data interface support (MON_DATA) disabled in firmware but support compiled in driver\n");
+ res = -1;
+#endif /* CONFIG_RWNX_MON_DATA */
+ }
+#endif
+
+ // Check supported AMSDU RX size
+ amsdu_rx = (sys_feat >> MM_AMSDU_MAX_SIZE_BIT0) & 0x03;
+ if (amsdu_rx < rwnx_hw->mod_params->amsdu_rx_max) {
+ rwnx_hw->mod_params->amsdu_rx_max = amsdu_rx;
+ }
+
+ // Check supported BW
+ bw = (phy_feat & MDM_CHBW_MASK) >> MDM_CHBW_LSB;
+ // Check if 80MHz BW is supported
+ if (bw < 2) {
+ rwnx_hw->mod_params->use_80 = false;
+ }
+ // Check if 40MHz BW is supported
+ if (bw < 1)
+ rwnx_hw->mod_params->use_2040 = false;
+
+ // 80MHz BW shall be disabled if 40MHz is not enabled
+ if (!rwnx_hw->mod_params->use_2040)
+ rwnx_hw->mod_params->use_80 = false;
+
+ // Check if HT is supposed to be supported. If not, disable VHT/HE too
+ if (!rwnx_hw->mod_params->ht_on)
+ {
+ rwnx_hw->mod_params->vht_on = false;
+ rwnx_hw->mod_params->he_on = false;
+ rwnx_hw->mod_params->he_ul_on = false;
+ rwnx_hw->mod_params->use_80 = false;
+ rwnx_hw->mod_params->use_2040 = false;
+ }
+
+ // LDPC is mandatory for HE40 and above, so if LDPC is not supported, then disable
+ // HE to use HT/VHT only
+ if (rwnx_hw->mod_params->use_2040 && !rwnx_hw->mod_params->ldpc_on)
+ {
+ rwnx_hw->mod_params->he_on = false;
+ rwnx_hw->mod_params->he_ul_on = false;
+ }
+
+ // HT greenfield is not supported in modem >= 3.0
+ if (__MDM_MAJOR_VERSION(phy_vers) > 0) {
+ rwnx_hw->mod_params->gf_rx_on = false;
+ }
+
+ if (!(sys_feat & BIT(MM_FEAT_MU_MIMO_RX_BIT)) ||
+ !rwnx_hw->mod_params->bfmee) {
+ rwnx_hw->mod_params->murx = false;
+ }
+
+ if ((sys_feat & BIT(MM_FEAT_MU_MIMO_TX_BIT))) {
+#ifndef CONFIG_RWNX_MUMIMO_TX
+ wiphy_err(wiphy,
+ "MU-MIMO TX enabled in firmware but support not compiled in driver\n");
+ res = -1;
+#endif /* CONFIG_RWNX_MUMIMO_TX */
+ if (!rwnx_hw->mod_params->bfmer)
+ rwnx_hw->mod_params->mutx = false;
+ // Check PHY and MAC HW MU-MIMO TX support and update parameter accordingly
+ else if (!(phy_feat & MDM_MUMIMOTX_BIT) || !(mac_feat & NXMAC_MU_MIMO_TX_BIT)) {
+ rwnx_hw->mod_params->mutx = false;
+ // Disable the feature in the bitfield so that it won't be displayed
+ sys_feat &= ~BIT(MM_FEAT_MU_MIMO_TX_BIT);
+ }
+ } else {
+#ifdef CONFIG_RWNX_MUMIMO_TX
+ wiphy_err(wiphy,
+ "MU-MIMO TX disabled in firmware but support compiled in driver\n");
+ res = -1;
+#else
+ rwnx_hw->mod_params->mutx = false;
+#endif /* CONFIG_RWNX_MUMIMO_TX */
+ }
+
+ if (sys_feat & BIT(MM_FEAT_WAPI_BIT)) {
+ rwnx_enable_wapi(rwnx_hw);
+ }
+
+#ifdef CONFIG_RWNX_FULLMAC
+ if (sys_feat & BIT(MM_FEAT_MFP_BIT)) {
+ rwnx_enable_mfp(rwnx_hw);
+ }
+#endif
+
+#ifdef CONFIG_RWNX_FULLMAC
+#define QUEUE_NAME "Broadcast/Multicast queue "
+#endif /* CONFIG_RWNX_FULLMAC */
+
+ if (sys_feat & BIT(MM_FEAT_BCN_BIT)) {
+#if NX_TXQ_CNT == 4
+ wiphy_err(wiphy, QUEUE_NAME
+ "enabled in firmware but support not compiled in driver\n");
+ res = -1;
+#endif /* NX_TXQ_CNT == 4 */
+ } else {
+#if NX_TXQ_CNT == 5
+ wiphy_err(wiphy, QUEUE_NAME
+ "disabled in firmware but support compiled in driver\n");
+ res = -1;
+#endif /* NX_TXQ_CNT == 5 */
+ }
+#undef QUEUE_NAME
+
+#ifdef CONFIG_RWNX_RADAR
+ if (sys_feat & BIT(MM_FEAT_RADAR_BIT)) {
+ /* Enable combination with radar detection */
+ wiphy->n_iface_combinations++;
+ }
+#endif /* CONFIG_RWNX_RADAR */
+
+#ifndef CONFIG_RWNX_SDM
+ switch (__MDM_PHYCFG_FROM_VERS(phy_feat)) {
+ case MDM_PHY_CONFIG_TRIDENT:
+ case MDM_PHY_CONFIG_ELMA:
+ rwnx_hw->mod_params->nss = 1;
+ break;
+ case MDM_PHY_CONFIG_KARST:
+ {
+ int nss_supp = (phy_feat & MDM_NSS_MASK) >> MDM_NSS_LSB;
+ if (rwnx_hw->mod_params->nss > nss_supp)
+ rwnx_hw->mod_params->nss = nss_supp;
+ }
+ break;
+ default:
+ WARN_ON(1);
+ break;
+ }
+#endif /* CONFIG_RWNX_SDM */
+
+ if (rwnx_hw->mod_params->nss < 1 || rwnx_hw->mod_params->nss > 2)
+ rwnx_hw->mod_params->nss = 1;
+
+ if (rwnx_hw->mod_params->phy_cfg < 0 || rwnx_hw->mod_params->phy_cfg > 5)
+ rwnx_hw->mod_params->phy_cfg = 2;
+
+ if (rwnx_hw->mod_params->mcs_map < 0 || rwnx_hw->mod_params->mcs_map > 2)
+ rwnx_hw->mod_params->mcs_map = 0;
+
+ wiphy_info(wiphy, "PHY features: [NSS=%d][CHBW=%d]%s%s\n",
+ rwnx_hw->mod_params->nss,
+ 20 * (1 << ((phy_feat & MDM_CHBW_MASK) >> MDM_CHBW_LSB)),
+ rwnx_hw->mod_params->ldpc_on ? "[LDPC]" : "",
+ rwnx_hw->mod_params->he_on ? "[HE]" : "");
+
+#define PRINT_RWNX_FEAT(feat) \
+ (sys_feat & BIT(MM_FEAT_##feat##_BIT) ? "["#feat"]" : "")
+
+ wiphy_info(wiphy, "FW features: %s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n",
+ PRINT_RWNX_FEAT(BCN),
+ PRINT_RWNX_FEAT(AUTOBCN),
+ PRINT_RWNX_FEAT(HWSCAN),
+ PRINT_RWNX_FEAT(CMON),
+ PRINT_RWNX_FEAT(MROLE),
+ PRINT_RWNX_FEAT(RADAR),
+ PRINT_RWNX_FEAT(PS),
+ PRINT_RWNX_FEAT(UAPSD),
+ PRINT_RWNX_FEAT(DPSM),
+ PRINT_RWNX_FEAT(AMPDU),
+ PRINT_RWNX_FEAT(AMSDU),
+ PRINT_RWNX_FEAT(CHNL_CTXT),
+ PRINT_RWNX_FEAT(REORD),
+ PRINT_RWNX_FEAT(P2P),
+ PRINT_RWNX_FEAT(P2P_GO),
+ PRINT_RWNX_FEAT(UMAC),
+ PRINT_RWNX_FEAT(VHT),
+ PRINT_RWNX_FEAT(HE),
+ PRINT_RWNX_FEAT(BFMEE),
+ PRINT_RWNX_FEAT(BFMER),
+ PRINT_RWNX_FEAT(WAPI),
+ PRINT_RWNX_FEAT(MFP),
+ PRINT_RWNX_FEAT(MU_MIMO_RX),
+ PRINT_RWNX_FEAT(MU_MIMO_TX),
+ PRINT_RWNX_FEAT(MESH),
+ PRINT_RWNX_FEAT(TDLS),
+ PRINT_RWNX_FEAT(ANT_DIV));
+#undef PRINT_RWNX_FEAT
+
+ if(max_sta_nb != NX_REMOTE_STA_MAX)
+ {
+ wiphy_err(wiphy, "Different number of supported stations between driver and FW (%d != %d)\n",
+ NX_REMOTE_STA_MAX, max_sta_nb);
+ res = -1;
+ }
+
+ if(max_vif_nb != NX_VIRT_DEV_MAX)
+ {
+ wiphy_err(wiphy, "Different number of supported virtual interfaces between driver and FW (%d != %d)\n",
+ NX_VIRT_DEV_MAX, max_vif_nb);
+ res = -1;
+ }
+
+ return res;
+}
+#endif
+
+
+static void rwnx_set_vht_capa(struct rwnx_hw *rwnx_hw, struct wiphy *wiphy)
+{
+#ifdef CONFIG_VHT_FOR_OLD_KERNEL
+ #ifdef USE_5G
+ struct ieee80211_supported_band *band_5GHz = wiphy->bands[NL80211_BAND_5GHZ];
+ #endif
+ struct ieee80211_supported_band *band_2GHz = wiphy->bands[NL80211_BAND_2GHZ];
+
+ int i;
+ int nss = rwnx_hw->mod_params->nss;
+ int mcs_map;
+ int mcs_map_max;
+ int bw_max;
+
+ if (!rwnx_hw->mod_params->vht_on) {
+ return;
+ }
+
+ rwnx_hw->vht_cap_2G.vht_supported = true;
+ if (rwnx_hw->mod_params->sgi80)
+ rwnx_hw->vht_cap_2G.cap |= IEEE80211_VHT_CAP_SHORT_GI_80;
+ if (rwnx_hw->mod_params->stbc_on)
+ rwnx_hw->vht_cap_2G.cap |= IEEE80211_VHT_CAP_RXSTBC_1;
+ if (rwnx_hw->mod_params->ldpc_on)
+ rwnx_hw->vht_cap_2G.cap |= IEEE80211_VHT_CAP_RXLDPC;
+ if (rwnx_hw->mod_params->bfmee) {
+ rwnx_hw->vht_cap_2G.cap |= IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE;
+ #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
+ rwnx_hw->vht_cap_2G.cap |= 3 << IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT;
+ #else
+ rwnx_hw->vht_cap_2G.cap |= 3 << 13;
+ #endif
+ }
+ if (nss > 1)
+ rwnx_hw->vht_cap_2G.cap |= IEEE80211_VHT_CAP_TXSTBC;
+
+ // Update the AMSDU max RX size (not shifted as located at offset 0 of the VHT cap)
+ rwnx_hw->vht_cap_2G.cap |= rwnx_hw->mod_params->amsdu_rx_max;
+
+ if (rwnx_hw->mod_params->bfmer) {
+ rwnx_hw->vht_cap_2G.cap |= IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE;
+ /* Set number of sounding dimensions */
+ #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
+ rwnx_hw->vht_cap_2G.cap |= (nss - 1) << IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_SHIFT;
+ #else
+ rwnx_hw->vht_cap_2G.cap |= (nss - 1) << 16;
+ #endif
+ }
+ if (rwnx_hw->mod_params->murx)
+ rwnx_hw->vht_cap_2G.cap |= IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE;
+ if (rwnx_hw->mod_params->mutx)
+ rwnx_hw->vht_cap_2G.cap |= IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE;
+
+ /*
+ * MCS map:
+ * This capabilities are filled according to the mcs_map module parameter.
+ * However currently we have some limitations due to FPGA clock constraints
+ * that prevent always using the range of MCS that is defined by the
+ * parameter:
+ * - in RX, 2SS, we support up to MCS7
+ * - in TX, 2SS, we support up to MCS8
+ */
+ // Get max supported BW
+ if (rwnx_hw->mod_params->use_80)
+ bw_max = PHY_CHNL_BW_80;
+ else if (rwnx_hw->mod_params->use_2040)
+ bw_max = PHY_CHNL_BW_40;
+ else
+ bw_max = PHY_CHNL_BW_20;
+
+ // Check if MCS map should be limited to MCS0_8 due to the standard. Indeed in BW20,
+ // MCS9 is not supported in 1 and 2 SS
+ if (rwnx_hw->mod_params->use_2040)
+ mcs_map_max = IEEE80211_VHT_MCS_SUPPORT_0_9;
+ else
+ mcs_map_max = IEEE80211_VHT_MCS_SUPPORT_0_8;
+
+ mcs_map = min_t(int, rwnx_hw->mod_params->mcs_map, mcs_map_max);
+ rwnx_hw->vht_cap_2G.vht_mcs.rx_mcs_map = cpu_to_le16(0);
+ for (i = 0; i < nss; i++) {
+ rwnx_hw->vht_cap_2G.vht_mcs.rx_mcs_map |= cpu_to_le16(mcs_map << (i*2));
+ rwnx_hw->vht_cap_2G.vht_mcs.rx_highest = MAX_VHT_RATE(mcs_map, nss, bw_max);
+ mcs_map = IEEE80211_VHT_MCS_SUPPORT_0_7;
+ }
+ for (; i < 8; i++) {
+ rwnx_hw->vht_cap_2G.vht_mcs.rx_mcs_map |= cpu_to_le16(
+ IEEE80211_VHT_MCS_NOT_SUPPORTED << (i*2));
+ }
+
+ mcs_map = min_t(int, rwnx_hw->mod_params->mcs_map, mcs_map_max);
+ rwnx_hw->vht_cap_2G.vht_mcs.tx_mcs_map = cpu_to_le16(0);
+ for (i = 0; i < nss; i++) {
+ rwnx_hw->vht_cap_2G.vht_mcs.tx_mcs_map |= cpu_to_le16(mcs_map << (i*2));
+ rwnx_hw->vht_cap_2G.vht_mcs.tx_highest = MAX_VHT_RATE(mcs_map, nss, bw_max);
+ mcs_map = min_t(int, rwnx_hw->mod_params->mcs_map,
+ IEEE80211_VHT_MCS_SUPPORT_0_8);
+ }
+ for (; i < 8; i++) {
+ rwnx_hw->vht_cap_2G.vht_mcs.tx_mcs_map |= cpu_to_le16(
+ IEEE80211_VHT_MCS_NOT_SUPPORTED << (i*2));
+ }
+
+ if (!rwnx_hw->mod_params->use_80) {
+#ifdef CONFIG_VENDOR_RWNX_VHT_NO80
+ rwnx_hw->vht_cap_2G.cap |= IEEE80211_VHT_CAP_NOT_SUP_WIDTH_80;
+#endif
+ rwnx_hw->vht_cap_2G.cap &= ~IEEE80211_VHT_CAP_SHORT_GI_80;
+ }
+
+ rwnx_hw->vht_cap_2G.cap |= IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK;
+ printk("%s, vht_capa_info=0x%x\n", __func__, rwnx_hw->vht_cap_2G.cap);
+#ifdef USE_5G
+ if (rwnx_hw->band_5g_support) {
+ rwnx_hw->vht_cap_5G.vht_supported = true;
+ if (rwnx_hw->mod_params->sgi80)
+ rwnx_hw->vht_cap_5G.cap |= IEEE80211_VHT_CAP_SHORT_GI_80;
+ if (rwnx_hw->mod_params->stbc_on)
+ rwnx_hw->vht_cap_5G.cap |= IEEE80211_VHT_CAP_RXSTBC_1;
+ if (rwnx_hw->mod_params->ldpc_on)
+ rwnx_hw->vht_cap_5G.cap |= IEEE80211_VHT_CAP_RXLDPC;
+ if (rwnx_hw->mod_params->bfmee) {
+ rwnx_hw->vht_cap_5G.cap |= IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE;
+ #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
+ rwnx_hw->vht_cap_5G.cap |= 3 << IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT;
+ #else
+ rwnx_hw->vht_cap_5G.cap |= 3 << 13;
+ #endif
+ }
+ if (nss > 1)
+ rwnx_hw->vht_cap_5G.cap |= IEEE80211_VHT_CAP_TXSTBC;
+
+ // Update the AMSDU max RX size (not shifted as located at offset 0 of the VHT cap)
+ rwnx_hw->vht_cap_5G.cap |= rwnx_hw->mod_params->amsdu_rx_max;
+
+ if (rwnx_hw->mod_params->bfmer) {
+ rwnx_hw->vht_cap_5G.cap |= IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE;
+ /* Set number of sounding dimensions */
+ #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
+ rwnx_hw->vht_cap_5G.cap |= (nss - 1) << IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_SHIFT;
+ #else
+ rwnx_hw->vht_cap_5G.cap |= (nss - 1) << 16;
+ #endif
+ }
+ if (rwnx_hw->mod_params->murx)
+ rwnx_hw->vht_cap_5G.cap |= IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE;
+ if (rwnx_hw->mod_params->mutx)
+ rwnx_hw->vht_cap_5G.cap |= IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE;
+
+ /*
+ * MCS map:
+ * This capabilities are filled according to the mcs_map module parameter.
+ * However currently we have some limitations due to FPGA clock constraints
+ * that prevent always using the range of MCS that is defined by the
+ * parameter:
+ * - in RX, 2SS, we support up to MCS7
+ * - in TX, 2SS, we support up to MCS8
+ */
+ // Get max supported BW
+ if (rwnx_hw->mod_params->use_80)
+ bw_max = PHY_CHNL_BW_80;
+ else if (rwnx_hw->mod_params->use_2040)
+ bw_max = PHY_CHNL_BW_40;
+ else
+ bw_max = PHY_CHNL_BW_20;
+
+ // Check if MCS map should be limited to MCS0_8 due to the standard. Indeed in BW20,
+ // MCS9 is not supported in 1 and 2 SS
+ if (rwnx_hw->mod_params->use_2040)
+ mcs_map_max = IEEE80211_VHT_MCS_SUPPORT_0_9;
+ else
+ mcs_map_max = IEEE80211_VHT_MCS_SUPPORT_0_8;
+
+ mcs_map = min_t(int, rwnx_hw->mod_params->mcs_map, mcs_map_max);
+ rwnx_hw->vht_cap_5G.vht_mcs.rx_mcs_map = cpu_to_le16(0);
+ for (i = 0; i < nss; i++) {
+ rwnx_hw->vht_cap_5G.vht_mcs.rx_mcs_map |= cpu_to_le16(mcs_map << (i*2));
+ rwnx_hw->vht_cap_5G.vht_mcs.rx_highest = MAX_VHT_RATE(mcs_map, nss, bw_max);
+ mcs_map = IEEE80211_VHT_MCS_SUPPORT_0_7;
+ }
+ for (; i < 8; i++) {
+ rwnx_hw->vht_cap_5G.vht_mcs.rx_mcs_map |= cpu_to_le16(
+ IEEE80211_VHT_MCS_NOT_SUPPORTED << (i*2));
+ }
+
+ mcs_map = min_t(int, rwnx_hw->mod_params->mcs_map, mcs_map_max);
+ rwnx_hw->vht_cap_5G.vht_mcs.tx_mcs_map = cpu_to_le16(0);
+ for (i = 0; i < nss; i++) {
+ rwnx_hw->vht_cap_5G.vht_mcs.tx_mcs_map |= cpu_to_le16(mcs_map << (i*2));
+ rwnx_hw->vht_cap_5G.vht_mcs.tx_highest = MAX_VHT_RATE(mcs_map, nss, bw_max);
+ mcs_map = min_t(int, rwnx_hw->mod_params->mcs_map,
+ IEEE80211_VHT_MCS_SUPPORT_0_8);
+ }
+ for (; i < 8; i++) {
+ rwnx_hw->vht_cap_5G.vht_mcs.tx_mcs_map |= cpu_to_le16(
+ IEEE80211_VHT_MCS_NOT_SUPPORTED << (i*2));
+ }
+
+ if (!rwnx_hw->mod_params->use_80) {
+#ifdef CONFIG_VENDOR_RWNX_VHT_NO80
+ rwnx_hw->vht_cap_5G.cap |= IEEE80211_VHT_CAP_NOT_SUP_WIDTH_80;
+#endif
+ rwnx_hw->vht_cap_5G.cap &= ~IEEE80211_VHT_CAP_SHORT_GI_80;
+ }
+ }
+#endif
+ return;
+#endif
+
+ //#ifdef USE_5G
+ struct ieee80211_supported_band *band_5GHz = wiphy->bands[NL80211_BAND_5GHZ];
+ //#endif
+ struct ieee80211_supported_band *band_2GHz = wiphy->bands[NL80211_BAND_2GHZ];
+
+ int i;
+ int nss = rwnx_hw->mod_params->nss;
+ int mcs_map;
+ int mcs_map_max;
+ int bw_max;
+
+ if (!rwnx_hw->mod_params->vht_on) {
+ return;
+ }
+
+ band_2GHz->vht_cap.vht_supported = true;
+ if (rwnx_hw->mod_params->sgi80)
+ band_2GHz->vht_cap.cap |= IEEE80211_VHT_CAP_SHORT_GI_80;
+ if (rwnx_hw->mod_params->stbc_on)
+ band_2GHz->vht_cap.cap |= IEEE80211_VHT_CAP_RXSTBC_1;
+ if (rwnx_hw->mod_params->ldpc_on)
+ band_2GHz->vht_cap.cap |= IEEE80211_VHT_CAP_RXLDPC;
+ if (rwnx_hw->mod_params->bfmee) {
+ band_2GHz->vht_cap.cap |= IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE;
+ #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
+ band_2GHz->vht_cap.cap |= 3 << IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT;
+ #else
+ band_2GHz->vht_cap.cap |= 3 << 13;
+ #endif
+ }
+ if (nss > 1)
+ band_2GHz->vht_cap.cap |= IEEE80211_VHT_CAP_TXSTBC;
+
+ // Update the AMSDU max RX size (not shifted as located at offset 0 of the VHT cap)
+ band_2GHz->vht_cap.cap |= rwnx_hw->mod_params->amsdu_rx_max;
+
+ if (rwnx_hw->mod_params->bfmer) {
+ band_2GHz->vht_cap.cap |= IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE;
+ /* Set number of sounding dimensions */
+ #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
+ band_2GHz->vht_cap.cap |= (nss - 1) << IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_SHIFT;
+ #else
+ band_2GHz->vht_cap.cap |= (nss - 1) << 16;
+ #endif
+ }
+ if (rwnx_hw->mod_params->murx)
+ band_2GHz->vht_cap.cap |= IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE;
+ if (rwnx_hw->mod_params->mutx)
+ band_2GHz->vht_cap.cap |= IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE;
+
+ /*
+ * MCS map:
+ * This capabilities are filled according to the mcs_map module parameter.
+ * However currently we have some limitations due to FPGA clock constraints
+ * that prevent always using the range of MCS that is defined by the
+ * parameter:
+ * - in RX, 2SS, we support up to MCS7
+ * - in TX, 2SS, we support up to MCS8
+ */
+ // Get max supported BW
+ if (rwnx_hw->mod_params->use_80)
+ bw_max = PHY_CHNL_BW_80;
+ else if (rwnx_hw->mod_params->use_2040)
+ bw_max = PHY_CHNL_BW_40;
+ else
+ bw_max = PHY_CHNL_BW_20;
+
+ // Check if MCS map should be limited to MCS0_8 due to the standard. Indeed in BW20,
+ // MCS9 is not supported in 1 and 2 SS
+ if (rwnx_hw->mod_params->use_2040)
+ mcs_map_max = IEEE80211_VHT_MCS_SUPPORT_0_9;
+ else
+ mcs_map_max = IEEE80211_VHT_MCS_SUPPORT_0_8;
+
+ mcs_map = min_t(int, rwnx_hw->mod_params->mcs_map, mcs_map_max);
+ band_2GHz->vht_cap.vht_mcs.rx_mcs_map = cpu_to_le16(0);
+ for (i = 0; i < nss; i++) {
+ band_2GHz->vht_cap.vht_mcs.rx_mcs_map |= cpu_to_le16(mcs_map << (i*2));
+ band_2GHz->vht_cap.vht_mcs.rx_highest = MAX_VHT_RATE(mcs_map, nss, bw_max);
+ mcs_map = IEEE80211_VHT_MCS_SUPPORT_0_7;
+ }
+ for (; i < 8; i++) {
+ band_2GHz->vht_cap.vht_mcs.rx_mcs_map |= cpu_to_le16(
+ IEEE80211_VHT_MCS_NOT_SUPPORTED << (i*2));
+ }
+
+ mcs_map = min_t(int, rwnx_hw->mod_params->mcs_map, mcs_map_max);
+ band_2GHz->vht_cap.vht_mcs.tx_mcs_map = cpu_to_le16(0);
+ for (i = 0; i < nss; i++) {
+ band_2GHz->vht_cap.vht_mcs.tx_mcs_map |= cpu_to_le16(mcs_map << (i*2));
+ band_2GHz->vht_cap.vht_mcs.tx_highest = MAX_VHT_RATE(mcs_map, nss, bw_max);
+ mcs_map = min_t(int, rwnx_hw->mod_params->mcs_map,
+ IEEE80211_VHT_MCS_SUPPORT_0_8);
+ }
+ for (; i < 8; i++) {
+ band_2GHz->vht_cap.vht_mcs.tx_mcs_map |= cpu_to_le16(
+ IEEE80211_VHT_MCS_NOT_SUPPORTED << (i*2));
+ }
+
+ if (!rwnx_hw->mod_params->use_80) {
+#ifdef CONFIG_VENDOR_RWNX_VHT_NO80
+ band_2GHz->vht_cap.cap |= IEEE80211_VHT_CAP_NOT_SUP_WIDTH_80;
+#endif
+ band_2GHz->vht_cap.cap &= ~IEEE80211_VHT_CAP_SHORT_GI_80;
+ }
+
+
+//#ifdef USE_5G
+ if (rwnx_hw->band_5g_support) {
+ band_5GHz->vht_cap.vht_supported = true;
+ if (rwnx_hw->mod_params->sgi80)
+ band_5GHz->vht_cap.cap |= IEEE80211_VHT_CAP_SHORT_GI_80;
+ if (rwnx_hw->mod_params->stbc_on)
+ band_5GHz->vht_cap.cap |= IEEE80211_VHT_CAP_RXSTBC_1;
+ if (rwnx_hw->mod_params->ldpc_on)
+ band_5GHz->vht_cap.cap |= IEEE80211_VHT_CAP_RXLDPC;
+ if (rwnx_hw->mod_params->bfmee) {
+ band_5GHz->vht_cap.cap |= IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE;
+ #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
+ band_5GHz->vht_cap.cap |= 3 << IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT;
+ #else
+ band_5GHz->vht_cap.cap |= 3 << 13;
+ #endif
+ }
+ if (nss > 1)
+ band_5GHz->vht_cap.cap |= IEEE80211_VHT_CAP_TXSTBC;
+
+ // Update the AMSDU max RX size (not shifted as located at offset 0 of the VHT cap)
+ band_5GHz->vht_cap.cap |= rwnx_hw->mod_params->amsdu_rx_max;
+
+ if (rwnx_hw->mod_params->bfmer) {
+ band_5GHz->vht_cap.cap |= IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE;
+ /* Set number of sounding dimensions */
+ #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
+ band_5GHz->vht_cap.cap |= (nss - 1) << IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_SHIFT;
+ #else
+ band_5GHz->vht_cap.cap |= (nss - 1) << 16;
+ #endif
+ }
+ if (rwnx_hw->mod_params->murx)
+ band_5GHz->vht_cap.cap |= IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE;
+ if (rwnx_hw->mod_params->mutx)
+ band_5GHz->vht_cap.cap |= IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE;
+
+ /*
+ * MCS map:
+ * This capabilities are filled according to the mcs_map module parameter.
+ * However currently we have some limitations due to FPGA clock constraints
+ * that prevent always using the range of MCS that is defined by the
+ * parameter:
+ * - in RX, 2SS, we support up to MCS7
+ * - in TX, 2SS, we support up to MCS8
+ */
+ // Get max supported BW
+ if (rwnx_hw->mod_params->use_80)
+ bw_max = PHY_CHNL_BW_80;
+ else if (rwnx_hw->mod_params->use_2040)
+ bw_max = PHY_CHNL_BW_40;
+ else
+ bw_max = PHY_CHNL_BW_20;
+
+ // Check if MCS map should be limited to MCS0_8 due to the standard. Indeed in BW20,
+ // MCS9 is not supported in 1 and 2 SS
+ if (rwnx_hw->mod_params->use_2040)
+ mcs_map_max = IEEE80211_VHT_MCS_SUPPORT_0_9;
+ else
+ mcs_map_max = IEEE80211_VHT_MCS_SUPPORT_0_8;
+
+ mcs_map = min_t(int, rwnx_hw->mod_params->mcs_map, mcs_map_max);
+ band_5GHz->vht_cap.vht_mcs.rx_mcs_map = cpu_to_le16(0);
+ for (i = 0; i < nss; i++) {
+ band_5GHz->vht_cap.vht_mcs.rx_mcs_map |= cpu_to_le16(mcs_map << (i*2));
+ band_5GHz->vht_cap.vht_mcs.rx_highest = MAX_VHT_RATE(mcs_map, nss, bw_max);
+ mcs_map = IEEE80211_VHT_MCS_SUPPORT_0_7;
+ }
+ for (; i < 8; i++) {
+ band_5GHz->vht_cap.vht_mcs.rx_mcs_map |= cpu_to_le16(
+ IEEE80211_VHT_MCS_NOT_SUPPORTED << (i*2));
+ }
+
+ mcs_map = min_t(int, rwnx_hw->mod_params->mcs_map, mcs_map_max);
+ band_5GHz->vht_cap.vht_mcs.tx_mcs_map = cpu_to_le16(0);
+ for (i = 0; i < nss; i++) {
+ band_5GHz->vht_cap.vht_mcs.tx_mcs_map |= cpu_to_le16(mcs_map << (i*2));
+ band_5GHz->vht_cap.vht_mcs.tx_highest = MAX_VHT_RATE(mcs_map, nss, bw_max);
+ mcs_map = min_t(int, rwnx_hw->mod_params->mcs_map,
+ IEEE80211_VHT_MCS_SUPPORT_0_8);
+ }
+ for (; i < 8; i++) {
+ band_5GHz->vht_cap.vht_mcs.tx_mcs_map |= cpu_to_le16(
+ IEEE80211_VHT_MCS_NOT_SUPPORTED << (i*2));
+ }
+
+ if (!rwnx_hw->mod_params->use_80) {
+#ifdef CONFIG_VENDOR_RWNX_VHT_NO80
+ band_5GHz->vht_cap.cap |= IEEE80211_VHT_CAP_NOT_SUP_WIDTH_80;
+#endif
+ band_5GHz->vht_cap.cap &= ~IEEE80211_VHT_CAP_SHORT_GI_80;
+ }
+//#endif
+ }
+}
+
+static void rwnx_set_ht_capa(struct rwnx_hw *rwnx_hw, struct wiphy *wiphy)
+{
+ //#ifdef USE_5G
+ struct ieee80211_supported_band *band_5GHz = wiphy->bands[NL80211_BAND_5GHZ];
+ //#endif
+
+ struct ieee80211_supported_band *band_2GHz = wiphy->bands[NL80211_BAND_2GHZ];
+ int i;
+ int nss = rwnx_hw->mod_params->nss;
+
+ if (!rwnx_hw->mod_params->ht_on) {
+ band_2GHz->ht_cap.ht_supported = false;
+ //#ifdef USE_5G
+ if (rwnx_hw->band_5g_support){
+ band_5GHz->ht_cap.ht_supported = false;
+ }
+ //#endif
+ return;
+ }
+
+ if (rwnx_hw->mod_params->stbc_on)
+ band_2GHz->ht_cap.cap |= 1 << IEEE80211_HT_CAP_RX_STBC_SHIFT;
+ if (rwnx_hw->mod_params->ldpc_on)
+ band_2GHz->ht_cap.cap |= IEEE80211_HT_CAP_LDPC_CODING;
+ if (rwnx_hw->mod_params->use_2040) {
+ band_2GHz->ht_cap.mcs.rx_mask[4] = 0x1; /* MCS32 */
+ band_2GHz->ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
+ band_2GHz->ht_cap.mcs.rx_highest = cpu_to_le16(135 * nss);
+ } else {
+ band_2GHz->ht_cap.mcs.rx_highest = cpu_to_le16(65 * nss);
+ }
+ if (nss > 1)
+ band_2GHz->ht_cap.cap |= IEEE80211_HT_CAP_TX_STBC;
+
+ // Update the AMSDU max RX size
+ if (rwnx_hw->mod_params->amsdu_rx_max)
+ band_2GHz->ht_cap.cap |= IEEE80211_HT_CAP_MAX_AMSDU;
+
+ if (rwnx_hw->mod_params->sgi) {
+ band_2GHz->ht_cap.cap |= IEEE80211_HT_CAP_SGI_20;
+ if (rwnx_hw->mod_params->use_2040) {
+ band_2GHz->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40;
+ band_2GHz->ht_cap.mcs.rx_highest = cpu_to_le16(150 * nss);
+ } else
+ band_2GHz->ht_cap.mcs.rx_highest = cpu_to_le16(72 * nss);
+ }
+ if (rwnx_hw->mod_params->gf_rx_on)
+ band_2GHz->ht_cap.cap |= IEEE80211_HT_CAP_GRN_FLD;
+
+ for (i = 0; i < nss; i++) {
+ band_2GHz->ht_cap.mcs.rx_mask[i] = 0xFF;
+ }
+
+ //#ifdef USE_5G
+ if (rwnx_hw->band_5g_support){
+ band_5GHz->ht_cap = band_2GHz->ht_cap;
+ }
+ //#endif
+}
+
+#ifdef CONFIG_HE_FOR_OLD_KERNEL
+extern struct ieee80211_sband_iftype_data rwnx_he_capa;
+#endif
+static void rwnx_set_he_capa(struct rwnx_hw *rwnx_hw, struct wiphy *wiphy)
+{
+ #ifdef CONFIG_HE_FOR_OLD_KERNEL
+ struct ieee80211_sta_he_cap *he_cap;
+ int i;
+ int nss = rwnx_hw->mod_params->nss;
+ int mcs_map;
+
+ he_cap = (struct ieee80211_sta_he_cap *) &rwnx_he_capa.he_cap;
+ he_cap->has_he = true;
+ he_cap->he_cap_elem.mac_cap_info[2] |= IEEE80211_HE_MAC_CAP2_ALL_ACK;
+ if (rwnx_hw->mod_params->use_2040) {
+ he_cap->he_cap_elem.phy_cap_info[0] |=
+ IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G;
+ he_cap->ppe_thres[0] |= 0x10;
+ }
+ //if (rwnx_hw->mod_params->use_80)
+ {
+ he_cap->he_cap_elem.phy_cap_info[0] |=
+ IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G;
+ }
+ if (rwnx_hw->mod_params->ldpc_on) {
+ he_cap->he_cap_elem.phy_cap_info[1] |= IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD;
+ } else {
+ // If no LDPC is supported, we have to limit to MCS0_9, as LDPC is mandatory
+ // for MCS 10 and 11
+ rwnx_hw->mod_params->he_mcs_map = min_t(int, rwnx_hw->mod_params->mcs_map,
+ IEEE80211_HE_MCS_SUPPORT_0_9);
+ }
+ #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0)
+ he_cap->he_cap_elem.phy_cap_info[1] |= IEEE80211_HE_PHY_CAP1_HE_LTF_AND_GI_FOR_HE_PPDUS_0_8US
+ | IEEE80211_HE_PHY_CAP1_MIDAMBLE_RX_TX_MAX_NSTS;
+
+ he_cap->he_cap_elem.phy_cap_info[2] |= IEEE80211_HE_PHY_CAP2_MIDAMBLE_RX_TX_MAX_NSTS |
+ IEEE80211_HE_PHY_CAP2_NDP_4x_LTF_AND_3_2US |
+ IEEE80211_HE_PHY_CAP2_DOPPLER_RX;
+ #else
+ he_cap->he_cap_elem.phy_cap_info[1] |= IEEE80211_HE_PHY_CAP1_HE_LTF_AND_GI_FOR_HE_PPDUS_0_8US;
+ he_cap->he_cap_elem.phy_cap_info[2] |= IEEE80211_HE_PHY_CAP2_NDP_4x_LTF_AND_3_2US |
+ IEEE80211_HE_PHY_CAP2_DOPPLER_RX;
+ #endif
+ if (rwnx_hw->mod_params->stbc_on)
+ he_cap->he_cap_elem.phy_cap_info[2] |= IEEE80211_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ;
+ #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 13, 0)
+ he_cap->he_cap_elem.phy_cap_info[3] |= IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_16_QAM |
+ IEEE80211_HE_PHY_CAP3_DCM_MAX_RX_NSS_1 |
+ IEEE80211_HE_PHY_CAP3_RX_PARTIAL_BW_SU_IN_20MHZ_MU;
+ #else
+ he_cap->he_cap_elem.phy_cap_info[3] |= IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_16_QAM |
+ IEEE80211_HE_PHY_CAP3_DCM_MAX_RX_NSS_1 |
+ IEEE80211_HE_PHY_CAP3_RX_HE_MU_PPDU_FROM_NON_AP_STA;
+ #endif
+ if (rwnx_hw->mod_params->bfmee) {
+ he_cap->he_cap_elem.phy_cap_info[4] |= IEEE80211_HE_PHY_CAP4_SU_BEAMFORMEE;
+ he_cap->he_cap_elem.phy_cap_info[4] |=
+ IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_UNDER_80MHZ_4;
+ }
+ he_cap->he_cap_elem.phy_cap_info[5] |= IEEE80211_HE_PHY_CAP5_NG16_SU_FEEDBACK |
+ IEEE80211_HE_PHY_CAP5_NG16_MU_FEEDBACK;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 13, 0)
+ he_cap->he_cap_elem.phy_cap_info[6] |= IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_42_SU |
+ IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_75_MU |
+ IEEE80211_HE_PHY_CAP6_TRIG_SU_BEAMFORMING_FB |
+ IEEE80211_HE_PHY_CAP6_TRIG_MU_BEAMFORMING_PARTIAL_BW_FB |
+ IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT |
+ IEEE80211_HE_PHY_CAP6_PARTIAL_BANDWIDTH_DL_MUMIMO;
+#else
+ he_cap->he_cap_elem.phy_cap_info[6] |= IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_42_SU |
+ IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_75_MU |
+ IEEE80211_HE_PHY_CAP6_TRIG_SU_BEAMFORMER_FB |
+ IEEE80211_HE_PHY_CAP6_TRIG_MU_BEAMFORMER_FB |
+ IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT |
+ IEEE80211_HE_PHY_CAP6_PARTIAL_BANDWIDTH_DL_MUMIMO;
+#endif
+ he_cap->he_cap_elem.phy_cap_info[7] |= IEEE80211_HE_PHY_CAP7_HE_SU_MU_PPDU_4XLTF_AND_08_US_GI;
+ he_cap->he_cap_elem.phy_cap_info[8] |= IEEE80211_HE_PHY_CAP8_20MHZ_IN_40MHZ_HE_PPDU_IN_2G;
+ he_cap->he_cap_elem.phy_cap_info[9] |= IEEE80211_HE_PHY_CAP9_RX_FULL_BW_SU_USING_MU_WITH_COMP_SIGB |
+ IEEE80211_HE_PHY_CAP9_RX_FULL_BW_SU_USING_MU_WITH_NON_COMP_SIGB;
+ mcs_map = rwnx_hw->mod_params->he_mcs_map;
+ //mcs_map = min_t(int, rwnx_hw->mod_params->he_mcs_map, IEEE80211_HE_MCS_SUPPORT_0_9);
+ memset(&he_cap->he_mcs_nss_supp, 0, sizeof(he_cap->he_mcs_nss_supp));
+ for (i = 0; i < nss; i++) {
+ __le16 unsup_for_ss = cpu_to_le16(IEEE80211_HE_MCS_NOT_SUPPORTED << (i*2));
+ he_cap->he_mcs_nss_supp.rx_mcs_80 |= cpu_to_le16(mcs_map << (i*2));
+ he_cap->he_mcs_nss_supp.rx_mcs_160 |= unsup_for_ss;
+ he_cap->he_mcs_nss_supp.rx_mcs_80p80 |= unsup_for_ss;
+ mcs_map = IEEE80211_HE_MCS_SUPPORT_0_7;
+ }
+ for (; i < 8; i++) {
+ __le16 unsup_for_ss = cpu_to_le16(IEEE80211_HE_MCS_NOT_SUPPORTED << (i*2));
+ he_cap->he_mcs_nss_supp.rx_mcs_80 |= unsup_for_ss;
+ he_cap->he_mcs_nss_supp.rx_mcs_160 |= unsup_for_ss;
+ he_cap->he_mcs_nss_supp.rx_mcs_80p80 |= unsup_for_ss;
+ }
+ mcs_map = rwnx_hw->mod_params->he_mcs_map;
+ for (i = 0; i < nss; i++) {
+ __le16 unsup_for_ss = cpu_to_le16(IEEE80211_HE_MCS_NOT_SUPPORTED << (i*2));
+ he_cap->he_mcs_nss_supp.tx_mcs_80 |= cpu_to_le16(mcs_map << (i*2));
+ he_cap->he_mcs_nss_supp.tx_mcs_160 |= unsup_for_ss;
+ he_cap->he_mcs_nss_supp.tx_mcs_80p80 |= unsup_for_ss;
+ mcs_map = min_t(int, rwnx_hw->mod_params->he_mcs_map,
+ IEEE80211_HE_MCS_SUPPORT_0_7);
+ }
+ for (; i < 8; i++) {
+ __le16 unsup_for_ss = cpu_to_le16(IEEE80211_HE_MCS_NOT_SUPPORTED << (i*2));
+ he_cap->he_mcs_nss_supp.tx_mcs_80 |= unsup_for_ss;
+ he_cap->he_mcs_nss_supp.tx_mcs_160 |= unsup_for_ss;
+ he_cap->he_mcs_nss_supp.tx_mcs_80p80 |= unsup_for_ss;
+ }
+
+ return ;
+ #endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)
+ //#ifdef USE_5G
+ struct ieee80211_supported_band *band_5GHz = wiphy->bands[NL80211_BAND_5GHZ];
+ //#endif
+ struct ieee80211_supported_band *band_2GHz = wiphy->bands[NL80211_BAND_2GHZ];
+ int i;
+ int nss = rwnx_hw->mod_params->nss;
+ struct ieee80211_sta_he_cap *he_cap;
+ int mcs_map;
+ if (!rwnx_hw->mod_params->he_on) {
+ band_2GHz->iftype_data = NULL;
+ band_2GHz->n_iftype_data = 0;
+ //#ifdef USE_5G
+ if (rwnx_hw->band_5g_support) {
+ band_5GHz->iftype_data = NULL;
+ band_5GHz->n_iftype_data = 0;
+ }
+ //#endif
+ return;
+ }
+ he_cap = (struct ieee80211_sta_he_cap *) &band_2GHz->iftype_data->he_cap;
+ he_cap->has_he = true;
+ he_cap->he_cap_elem.mac_cap_info[2] |= IEEE80211_HE_MAC_CAP2_ALL_ACK;
+ if (rwnx_hw->mod_params->use_2040) {
+ he_cap->he_cap_elem.phy_cap_info[0] |=
+ IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G;
+ he_cap->ppe_thres[0] |= 0x10;
+ }
+ //if (rwnx_hw->mod_params->use_80)
+ {
+ he_cap->he_cap_elem.phy_cap_info[0] |=
+ IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G;
+ }
+ if (rwnx_hw->mod_params->ldpc_on) {
+ he_cap->he_cap_elem.phy_cap_info[1] |= IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD;
+ } else {
+ // If no LDPC is supported, we have to limit to MCS0_9, as LDPC is mandatory
+ // for MCS 10 and 11
+ rwnx_hw->mod_params->he_mcs_map = min_t(int, rwnx_hw->mod_params->mcs_map,
+ IEEE80211_HE_MCS_SUPPORT_0_9);
+ }
+ #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0)
+ he_cap->he_cap_elem.phy_cap_info[1] |= IEEE80211_HE_PHY_CAP1_HE_LTF_AND_GI_FOR_HE_PPDUS_0_8US
+ | IEEE80211_HE_PHY_CAP1_MIDAMBLE_RX_TX_MAX_NSTS;
+
+ he_cap->he_cap_elem.phy_cap_info[2] |= IEEE80211_HE_PHY_CAP2_MIDAMBLE_RX_TX_MAX_NSTS |
+ IEEE80211_HE_PHY_CAP2_NDP_4x_LTF_AND_3_2US |
+ IEEE80211_HE_PHY_CAP2_DOPPLER_RX;
+ #else
+ he_cap->he_cap_elem.phy_cap_info[1] |= IEEE80211_HE_PHY_CAP1_HE_LTF_AND_GI_FOR_HE_PPDUS_0_8US;
+ he_cap->he_cap_elem.phy_cap_info[2] |= IEEE80211_HE_PHY_CAP2_NDP_4x_LTF_AND_3_2US |
+ IEEE80211_HE_PHY_CAP2_DOPPLER_RX;
+ #endif
+ if (rwnx_hw->mod_params->stbc_on)
+ he_cap->he_cap_elem.phy_cap_info[2] |= IEEE80211_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 13, 0)
+ he_cap->he_cap_elem.phy_cap_info[3] |= IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_16_QAM |
+ IEEE80211_HE_PHY_CAP3_DCM_MAX_RX_NSS_1 |
+ IEEE80211_HE_PHY_CAP3_RX_PARTIAL_BW_SU_IN_20MHZ_MU;
+#else
+ he_cap->he_cap_elem.phy_cap_info[3] |= IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_16_QAM |
+ IEEE80211_HE_PHY_CAP3_DCM_MAX_RX_NSS_1 |
+ IEEE80211_HE_PHY_CAP3_RX_HE_MU_PPDU_FROM_NON_AP_STA;
+#endif
+ if (rwnx_hw->mod_params->bfmee) {
+ he_cap->he_cap_elem.phy_cap_info[4] |= IEEE80211_HE_PHY_CAP4_SU_BEAMFORMEE;
+ he_cap->he_cap_elem.phy_cap_info[4] |=
+ IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_UNDER_80MHZ_4;
+ }
+ he_cap->he_cap_elem.phy_cap_info[5] |= IEEE80211_HE_PHY_CAP5_NG16_SU_FEEDBACK |
+ IEEE80211_HE_PHY_CAP5_NG16_MU_FEEDBACK;
+ #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 13, 0)
+ he_cap->he_cap_elem.phy_cap_info[6] |= IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_42_SU |
+ IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_75_MU |
+ IEEE80211_HE_PHY_CAP6_TRIG_SU_BEAMFORMING_FB |
+ IEEE80211_HE_PHY_CAP6_TRIG_MU_BEAMFORMING_PARTIAL_BW_FB |
+ IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT |
+ IEEE80211_HE_PHY_CAP6_PARTIAL_BANDWIDTH_DL_MUMIMO;
+ #else
+ he_cap->he_cap_elem.phy_cap_info[6] |= IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_42_SU |
+ IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_75_MU |
+ IEEE80211_HE_PHY_CAP6_TRIG_SU_BEAMFORMER_FB |
+ IEEE80211_HE_PHY_CAP6_TRIG_MU_BEAMFORMER_FB |
+ IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT |
+ IEEE80211_HE_PHY_CAP6_PARTIAL_BANDWIDTH_DL_MUMIMO;
+ #endif
+ he_cap->he_cap_elem.phy_cap_info[7] |= IEEE80211_HE_PHY_CAP7_HE_SU_MU_PPDU_4XLTF_AND_08_US_GI;
+ he_cap->he_cap_elem.phy_cap_info[8] |= IEEE80211_HE_PHY_CAP8_20MHZ_IN_40MHZ_HE_PPDU_IN_2G;
+ #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0)
+ he_cap->he_cap_elem.phy_cap_info[9] |= IEEE80211_HE_PHY_CAP9_RX_FULL_BW_SU_USING_MU_WITH_COMP_SIGB |
+ IEEE80211_HE_PHY_CAP9_RX_FULL_BW_SU_USING_MU_WITH_NON_COMP_SIGB;
+ #endif
+ mcs_map = rwnx_hw->mod_params->he_mcs_map;
+ //mcs_map = min_t(int, rwnx_hw->mod_params->he_mcs_map, IEEE80211_HE_MCS_SUPPORT_0_9);
+ memset(&he_cap->he_mcs_nss_supp, 0, sizeof(he_cap->he_mcs_nss_supp));
+ for (i = 0; i < nss; i++) {
+ __le16 unsup_for_ss = cpu_to_le16(IEEE80211_HE_MCS_NOT_SUPPORTED << (i*2));
+ he_cap->he_mcs_nss_supp.rx_mcs_80 |= cpu_to_le16(mcs_map << (i*2));
+ he_cap->he_mcs_nss_supp.rx_mcs_160 |= unsup_for_ss;
+ he_cap->he_mcs_nss_supp.rx_mcs_80p80 |= unsup_for_ss;
+ mcs_map = IEEE80211_HE_MCS_SUPPORT_0_7;
+ }
+ for (; i < 8; i++) {
+ __le16 unsup_for_ss = cpu_to_le16(IEEE80211_HE_MCS_NOT_SUPPORTED << (i*2));
+ he_cap->he_mcs_nss_supp.rx_mcs_80 |= unsup_for_ss;
+ he_cap->he_mcs_nss_supp.rx_mcs_160 |= unsup_for_ss;
+ he_cap->he_mcs_nss_supp.rx_mcs_80p80 |= unsup_for_ss;
+ }
+ mcs_map = rwnx_hw->mod_params->he_mcs_map;
+ for (i = 0; i < nss; i++) {
+ __le16 unsup_for_ss = cpu_to_le16(IEEE80211_HE_MCS_NOT_SUPPORTED << (i*2));
+ he_cap->he_mcs_nss_supp.tx_mcs_80 |= cpu_to_le16(mcs_map << (i*2));
+ he_cap->he_mcs_nss_supp.tx_mcs_160 |= unsup_for_ss;
+ he_cap->he_mcs_nss_supp.tx_mcs_80p80 |= unsup_for_ss;
+ mcs_map = min_t(int, rwnx_hw->mod_params->he_mcs_map,
+ IEEE80211_HE_MCS_SUPPORT_0_7);
+ }
+ for (; i < 8; i++) {
+ __le16 unsup_for_ss = cpu_to_le16(IEEE80211_HE_MCS_NOT_SUPPORTED << (i*2));
+ he_cap->he_mcs_nss_supp.tx_mcs_80 |= unsup_for_ss;
+ he_cap->he_mcs_nss_supp.tx_mcs_160 |= unsup_for_ss;
+ he_cap->he_mcs_nss_supp.tx_mcs_80p80 |= unsup_for_ss;
+ }
+
+//#ifdef USE_5G
+ if(rwnx_hw->band_5g_support){
+ he_cap = (struct ieee80211_sta_he_cap *) &band_5GHz->iftype_data->he_cap;
+ he_cap->has_he = true;
+ he_cap->he_cap_elem.mac_cap_info[2] |= IEEE80211_HE_MAC_CAP2_ALL_ACK;
+ if (rwnx_hw->mod_params->use_2040) {
+ he_cap->he_cap_elem.phy_cap_info[0] |=
+ IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G;
+ he_cap->ppe_thres[0] |= 0x10;
+ }
+ //if (rwnx_hw->mod_params->use_80)
+ {
+ he_cap->he_cap_elem.phy_cap_info[0] |=
+ IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G;
+ }
+ if (rwnx_hw->mod_params->ldpc_on) {
+ he_cap->he_cap_elem.phy_cap_info[1] |= IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD;
+ } else {
+ // If no LDPC is supported, we have to limit to MCS0_9, as LDPC is mandatory
+ // for MCS 10 and 11
+ rwnx_hw->mod_params->he_mcs_map = min_t(int, rwnx_hw->mod_params->mcs_map,
+ IEEE80211_HE_MCS_SUPPORT_0_9);
+ }
+ #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0)
+ he_cap->he_cap_elem.phy_cap_info[1] |= IEEE80211_HE_PHY_CAP1_HE_LTF_AND_GI_FOR_HE_PPDUS_0_8US |
+ IEEE80211_HE_PHY_CAP1_MIDAMBLE_RX_TX_MAX_NSTS;
+ he_cap->he_cap_elem.phy_cap_info[2] |= IEEE80211_HE_PHY_CAP2_MIDAMBLE_RX_TX_MAX_NSTS |
+ IEEE80211_HE_PHY_CAP2_NDP_4x_LTF_AND_3_2US |
+ IEEE80211_HE_PHY_CAP2_DOPPLER_RX;
+ #else
+ he_cap->he_cap_elem.phy_cap_info[1] |= IEEE80211_HE_PHY_CAP1_HE_LTF_AND_GI_FOR_HE_PPDUS_0_8US;
+ he_cap->he_cap_elem.phy_cap_info[2] |= IEEE80211_HE_PHY_CAP2_NDP_4x_LTF_AND_3_2US |
+ IEEE80211_HE_PHY_CAP2_DOPPLER_RX;
+ #endif
+ if (rwnx_hw->mod_params->stbc_on)
+ he_cap->he_cap_elem.phy_cap_info[2] |= IEEE80211_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 13, 0)
+ he_cap->he_cap_elem.phy_cap_info[3] |= IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_16_QAM |
+ IEEE80211_HE_PHY_CAP3_DCM_MAX_RX_NSS_1 |
+ IEEE80211_HE_PHY_CAP3_RX_PARTIAL_BW_SU_IN_20MHZ_MU;
+#else
+ he_cap->he_cap_elem.phy_cap_info[3] |= IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_16_QAM |
+ IEEE80211_HE_PHY_CAP3_DCM_MAX_RX_NSS_1 |
+ IEEE80211_HE_PHY_CAP3_RX_HE_MU_PPDU_FROM_NON_AP_STA;
+#endif
+ if (rwnx_hw->mod_params->bfmee) {
+ he_cap->he_cap_elem.phy_cap_info[4] |= IEEE80211_HE_PHY_CAP4_SU_BEAMFORMEE;
+ he_cap->he_cap_elem.phy_cap_info[4] |=
+ IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_UNDER_80MHZ_4;
+ }
+ he_cap->he_cap_elem.phy_cap_info[5] |= IEEE80211_HE_PHY_CAP5_NG16_SU_FEEDBACK |
+ IEEE80211_HE_PHY_CAP5_NG16_MU_FEEDBACK;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 13, 0)
+ he_cap->he_cap_elem.phy_cap_info[6] |= IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_42_SU |
+ IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_75_MU |
+ IEEE80211_HE_PHY_CAP6_TRIG_SU_BEAMFORMING_FB |
+ IEEE80211_HE_PHY_CAP6_TRIG_MU_BEAMFORMING_PARTIAL_BW_FB |
+ IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT |
+ IEEE80211_HE_PHY_CAP6_PARTIAL_BANDWIDTH_DL_MUMIMO;
+#else
+ he_cap->he_cap_elem.phy_cap_info[6] |= IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_42_SU |
+ IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_75_MU |
+ IEEE80211_HE_PHY_CAP6_TRIG_SU_BEAMFORMER_FB |
+ IEEE80211_HE_PHY_CAP6_TRIG_MU_BEAMFORMER_FB |
+ IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT |
+ IEEE80211_HE_PHY_CAP6_PARTIAL_BANDWIDTH_DL_MUMIMO;
+#endif
+ he_cap->he_cap_elem.phy_cap_info[7] |= IEEE80211_HE_PHY_CAP7_HE_SU_MU_PPDU_4XLTF_AND_08_US_GI;
+ he_cap->he_cap_elem.phy_cap_info[8] |= IEEE80211_HE_PHY_CAP8_20MHZ_IN_40MHZ_HE_PPDU_IN_2G;
+ #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0)
+ he_cap->he_cap_elem.phy_cap_info[9] |= IEEE80211_HE_PHY_CAP9_RX_FULL_BW_SU_USING_MU_WITH_COMP_SIGB |
+ IEEE80211_HE_PHY_CAP9_RX_FULL_BW_SU_USING_MU_WITH_NON_COMP_SIGB;
+ #endif
+ mcs_map = rwnx_hw->mod_params->he_mcs_map;
+ //mcs_map = min_t(int, rwnx_hw->mod_params->he_mcs_map, IEEE80211_HE_MCS_SUPPORT_0_9);
+ memset(&he_cap->he_mcs_nss_supp, 0, sizeof(he_cap->he_mcs_nss_supp));
+ for (i = 0; i < nss; i++) {
+ __le16 unsup_for_ss = cpu_to_le16(IEEE80211_HE_MCS_NOT_SUPPORTED << (i*2));
+ he_cap->he_mcs_nss_supp.rx_mcs_80 |= cpu_to_le16(mcs_map << (i*2));
+ he_cap->he_mcs_nss_supp.rx_mcs_160 |= unsup_for_ss;
+ he_cap->he_mcs_nss_supp.rx_mcs_80p80 |= unsup_for_ss;
+ mcs_map = IEEE80211_HE_MCS_SUPPORT_0_7;
+ }
+ for (; i < 8; i++) {
+ __le16 unsup_for_ss = cpu_to_le16(IEEE80211_HE_MCS_NOT_SUPPORTED << (i*2));
+ he_cap->he_mcs_nss_supp.rx_mcs_80 |= unsup_for_ss;
+ he_cap->he_mcs_nss_supp.rx_mcs_160 |= unsup_for_ss;
+ he_cap->he_mcs_nss_supp.rx_mcs_80p80 |= unsup_for_ss;
+ }
+ mcs_map = rwnx_hw->mod_params->he_mcs_map;
+ for (i = 0; i < nss; i++) {
+ __le16 unsup_for_ss = cpu_to_le16(IEEE80211_HE_MCS_NOT_SUPPORTED << (i*2));
+ he_cap->he_mcs_nss_supp.tx_mcs_80 |= cpu_to_le16(mcs_map << (i*2));
+ he_cap->he_mcs_nss_supp.tx_mcs_160 |= unsup_for_ss;
+ he_cap->he_mcs_nss_supp.tx_mcs_80p80 |= unsup_for_ss;
+ mcs_map = min_t(int, rwnx_hw->mod_params->he_mcs_map,
+ IEEE80211_HE_MCS_SUPPORT_0_7);
+ }
+ for (; i < 8; i++) {
+ __le16 unsup_for_ss = cpu_to_le16(IEEE80211_HE_MCS_NOT_SUPPORTED << (i*2));
+ he_cap->he_mcs_nss_supp.tx_mcs_80 |= unsup_for_ss;
+ he_cap->he_mcs_nss_supp.tx_mcs_160 |= unsup_for_ss;
+ he_cap->he_mcs_nss_supp.tx_mcs_80p80 |= unsup_for_ss;
+ }
+ }
+//#endif
+#endif
+}
+
+static void rwnx_set_wiphy_params(struct rwnx_hw *rwnx_hw, struct wiphy *wiphy)
+{
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0))
+ struct ieee80211_regdomain *regdomain;
+#endif
+
+#ifdef CONFIG_RWNX_FULLMAC
+ /* FULLMAC specific parameters */
+ wiphy->flags |= WIPHY_FLAG_REPORTS_OBSS;
+ wiphy->max_scan_ssids = SCAN_SSID_MAX;
+ wiphy->max_scan_ie_len = SCANU_MAX_IE_LEN;
+#endif /* CONFIG_RWNX_FULLMAC */
+
+ if (rwnx_hw->mod_params->tdls) {
+ /* TDLS support */
+ wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS;
+#ifdef CONFIG_RWNX_FULLMAC
+ /* TDLS external setup support */
+ wiphy->flags |= WIPHY_FLAG_TDLS_EXTERNAL_SETUP;
+#endif
+ }
+
+ if (rwnx_hw->mod_params->ap_uapsd_on)
+ wiphy->flags |= WIPHY_FLAG_AP_UAPSD;
+
+#ifdef CONFIG_RWNX_FULLMAC
+ if (rwnx_hw->mod_params->ps_on)
+ wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT;
+ else
+ wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT;
+#endif
+
+ if (rwnx_hw->mod_params->custregd) {
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0)) && (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0))
+ // Apply custom regulatory. Note that for recent kernel versions we use instead the
+ // REGULATORY_WIPHY_SELF_MANAGED flag, along with the regulatory_set_wiphy_regd()
+ // function, that needs to be called after wiphy registration
+ memcpy(country_code, default_ccode, sizeof(default_ccode));
+ regdomain = getRegdomainFromRwnxDB(wiphy, default_ccode);
+ printk(KERN_CRIT
+ "\n\n%s: CAUTION: USING PERMISSIVE CUSTOM REGULATORY RULES\n\n",
+ __func__);
+ wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG;
+ wiphy->regulatory_flags |= REGULATORY_IGNORE_STALE_KICKOFF;
+ wiphy_apply_custom_regulatory(wiphy, regdomain);
+#elif (LINUX_VERSION_CODE < KERNEL_VERSION(3, 14, 0))
+ memcpy(country_code, default_ccode, sizeof(default_ccode));
+ regdomain = getRegdomainFromRwnxDB(wiphy, default_ccode);
+ printk(KERN_CRIT"%s: Registering custom regulatory\n", __func__);
+ wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY;
+ wiphy_apply_custom_regulatory(wiphy, regdomain);
+#endif
+ // Check if custom channel set shall be enabled. In such case only monitor mode is
+ // supported
+ if (rwnx_hw->mod_params->custchan) {
+ wiphy->interface_modes = BIT(NL80211_IFTYPE_MONITOR);
+
+ // Enable "extra" channels
+ wiphy->bands[NL80211_BAND_2GHZ]->n_channels += 13;
+ //#ifdef USE_5G
+ if(rwnx_hw->band_5g_support){
+ wiphy->bands[NL80211_BAND_5GHZ]->n_channels += 59;
+ }
+ //#endif
+ }
+ }
+}
+
+#if 0
+static void rwnx_set_rf_params(struct rwnx_hw *rwnx_hw, struct wiphy *wiphy)
+{
+#ifndef CONFIG_RWNX_SDM
+ #ifdef USE_5G
+ struct ieee80211_supported_band *band_5GHz = wiphy->bands[NL80211_BAND_5GHZ];
+ #endif
+ struct ieee80211_supported_band *band_2GHz = wiphy->bands[NL80211_BAND_2GHZ];
+ u32 mdm_phy_cfg = __MDM_PHYCFG_FROM_VERS(rwnx_hw->version_cfm.version_phy_1);
+
+ /*
+ * Get configuration file depending on the RF
+ */
+ if (mdm_phy_cfg == MDM_PHY_CONFIG_TRIDENT) {
+ struct rwnx_phy_conf_file phy_conf;
+ // Retrieve the Trident configuration
+ rwnx_parse_phy_configfile(rwnx_hw, RWNX_PHY_CONFIG_TRD_NAME,
+ &phy_conf, rwnx_hw->mod_params->phy_cfg);
+ memcpy(&rwnx_hw->phy.cfg, &phy_conf.trd, sizeof(phy_conf.trd));
+ } else if (mdm_phy_cfg == MDM_PHY_CONFIG_ELMA) {
+ } else if (mdm_phy_cfg == MDM_PHY_CONFIG_KARST) {
+ struct rwnx_phy_conf_file phy_conf;
+ // We use the NSS parameter as is
+ // Retrieve the Karst configuration
+ rwnx_parse_phy_configfile(rwnx_hw, RWNX_PHY_CONFIG_KARST_NAME,
+ &phy_conf, rwnx_hw->mod_params->phy_cfg);
+
+ memcpy(&rwnx_hw->phy.cfg, &phy_conf.karst, sizeof(phy_conf.karst));
+ } else {
+ WARN_ON(1);
+ }
+
+ /*
+ * adjust caps depending on the RF
+ */
+ switch (mdm_phy_cfg) {
+ case MDM_PHY_CONFIG_TRIDENT:
+ {
+ wiphy_dbg(wiphy, "found Trident phy .. limit BW to 40MHz\n");
+ rwnx_hw->phy.limit_bw = true;
+ #ifdef USE_5G
+#ifdef CONFIG_VENDOR_RWNX_VHT_NO80
+ band_5GHz->vht_cap.cap |= IEEE80211_VHT_CAP_NOT_SUP_WIDTH_80;
+#endif
+ band_5GHz->vht_cap.cap &= ~(IEEE80211_VHT_CAP_SHORT_GI_80 |
+ IEEE80211_VHT_CAP_RXSTBC_MASK);
+ #endif
+ break;
+ }
+ case MDM_PHY_CONFIG_ELMA:
+ wiphy_dbg(wiphy, "found ELMA phy .. disabling 2.4GHz and greenfield rx\n");
+ wiphy->bands[NL80211_BAND_2GHZ] = NULL;
+ band_2GHz->ht_cap.cap &= ~IEEE80211_HT_CAP_GRN_FLD;
+ #ifdef USE_5G
+ band_5GHz->ht_cap.cap &= ~IEEE80211_HT_CAP_GRN_FLD;
+ band_5GHz->vht_cap.cap &= ~IEEE80211_VHT_CAP_RXSTBC_MASK;
+ #endif
+ break;
+ case MDM_PHY_CONFIG_KARST:
+ {
+ wiphy_dbg(wiphy, "found KARST phy\n");
+ break;
+ }
+ default:
+ WARN_ON(1);
+ break;
+ }
+#endif /* CONFIG_RWNX_SDM */
+}
+#endif
+
+int rwnx_handle_dynparams(struct rwnx_hw *rwnx_hw, struct wiphy *wiphy)
+{
+#if 0
+ /* Check compatibility between requested parameters and HW/SW features */
+ int ret;
+
+ ret = rwnx_check_fw_hw_feature(rwnx_hw, wiphy);
+ if (ret)
+ return ret;
+
+ /* Allocate the RX buffers according to the maximum AMSDU RX size */
+ ret = rwnx_ipc_rxbuf_init(rwnx_hw,
+ (4 * (rwnx_hw->mod_params->amsdu_rx_max + 1) + 1) * 1024);
+ if (ret) {
+ wiphy_err(wiphy, "Cannot allocate the RX buffers\n");
+ return ret;
+ }
+#endif
+
+ //check he_mcs max
+ if(rwnx_hw->usbdev->chipid != PRODUCT_ID_AIC8800D81 &&
+ rwnx_hw->mod_params->he_mcs_map > IEEE80211_HE_MCS_SUPPORT_0_9){
+ rwnx_hw->mod_params->he_mcs_map = IEEE80211_HE_MCS_SUPPORT_0_9;
+ }
+
+ //check use_80 support
+ if(rwnx_hw->usbdev->chipid != PRODUCT_ID_AIC8800D81 &&
+ rwnx_hw->mod_params->use_80 == true){
+ rwnx_hw->mod_params->use_80 = false;
+ }
+
+ //check sgi80 support
+ if(rwnx_hw->usbdev->chipid != PRODUCT_ID_AIC8800D81 &&
+ rwnx_hw->mod_params->sgi80 == true){
+ rwnx_hw->mod_params->sgi80 = false;
+ }
+#ifdef CONFIG_5M10M
+ rwnx_hw->mod_params->he_mcs_map = IEEE80211_VHT_MCS_SUPPORT_0_7;
+ rwnx_hw->mod_params->he_mcs_map = IEEE80211_HE_MCS_SUPPORT_0_7;
+ rwnx_hw->mod_params->use_2040 = false;
+ rwnx_hw->mod_params->use_80 = false;
+ rwnx_hw->mod_params->sgi80 = false;
+#endif
+
+ /* Set wiphy parameters */
+ rwnx_set_wiphy_params(rwnx_hw, wiphy);
+ /* Set VHT capabilities */
+ rwnx_set_vht_capa(rwnx_hw, wiphy);
+ /* Set HE capabilities */
+ rwnx_set_he_capa(rwnx_hw, wiphy);
+ /* Set HT capabilities */
+ rwnx_set_ht_capa(rwnx_hw, wiphy);
+ /* Set RF specific parameters (shall be done last as it might change some
+ capabilities previously set) */
+#if 0
+ rwnx_set_rf_params(rwnx_hw, wiphy);
+#endif
+ return 0;
+}
+
+void rwnx_custregd(struct rwnx_hw *rwnx_hw, struct wiphy *wiphy)
+{
+// For older kernel version, the custom regulatory is applied before the wiphy
+// registration (in rwnx_set_wiphy_params()), so nothing has to be done here
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)
+ wiphy->regulatory_flags |= REGULATORY_WIPHY_SELF_MANAGED;
+
+ if (!rwnx_hw->mod_params->custregd)
+ return;
+
+ rtnl_lock();
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0)
+ if (regulatory_set_wiphy_regd_sync(wiphy, getRegdomainFromRwnxDB(wiphy, default_ccode))){
+ wiphy_err(wiphy, "Failed to set custom regdomain\n");
+ }
+#else
+ if (regulatory_set_wiphy_regd_sync_rtnl(wiphy, getRegdomainFromRwnxDB(wiphy, default_ccode))){
+ wiphy_err(wiphy, "Failed to set custom regdomain\n");
+ }
+#endif
+ else{
+ wiphy_err(wiphy,"\n"
+ "*******************************************************\n"
+ "** CAUTION: USING PERMISSIVE CUSTOM REGULATORY RULES **\n"
+ "*******************************************************\n");
+ }
+ rtnl_unlock();
+#endif
+}
diff --git a/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_mod_params.h b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_mod_params.h
new file mode 100644
index 000000000000..99d38839de96
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_mod_params.h
@@ -0,0 +1,70 @@
+/**
+ ******************************************************************************
+ *
+ * @file rwnx_mod_params.h
+ *
+ * @brief Declaration of module parameters
+ *
+ * Copyright (C) RivieraWaves 2012-2019
+ *
+ ******************************************************************************
+ */
+
+#ifndef _RWNX_MOD_PARAM_H_
+#define _RWNX_MOD_PARAM_H_
+
+struct rwnx_mod_params {
+ bool ht_on;
+ bool vht_on;
+ bool he_on;
+ int mcs_map;
+ int he_mcs_map;
+ bool he_ul_on;
+ bool ldpc_on;
+ bool stbc_on;
+ bool gf_rx_on;
+ int phy_cfg;
+ int uapsd_timeout;
+ bool ap_uapsd_on;
+ bool sgi;
+ bool sgi80;
+ bool use_2040;
+ bool use_80;
+ bool custregd;
+ bool custchan;
+ int nss;
+ int amsdu_rx_max;
+ bool bfmee;
+ bool bfmer;
+ bool mesh;
+ bool murx;
+ bool mutx;
+ bool mutx_on;
+ unsigned int roc_dur_max;
+ int listen_itv;
+ bool listen_bcmc;
+ int lp_clk_ppm;
+ bool ps_on;
+ int tx_lft;
+ int amsdu_maxnb;
+ int uapsd_queues;
+ bool tdls;
+ bool uf;
+ bool auto_reply;
+ char *ftl;
+ bool dpsm;
+#ifdef CONFIG_RWNX_FULLMAC
+ bool ant_div;
+#endif /* CONFIG_RWNX_FULLMAC */
+};
+
+extern struct rwnx_mod_params rwnx_mod_params;
+
+struct rwnx_hw;
+struct wiphy;
+int rwnx_handle_dynparams(struct rwnx_hw *rwnx_hw, struct wiphy *wiphy);
+void rwnx_custregd(struct rwnx_hw *rwnx_hw, struct wiphy *wiphy);
+void rwnx_enable_wapi(struct rwnx_hw *rwnx_hw);
+void rwnx_enable_mfp(struct rwnx_hw *rwnx_hw);
+
+#endif /* _RWNX_MOD_PARAM_H_ */
diff --git a/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_msg_rx.c b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_msg_rx.c
new file mode 100644
index 000000000000..4740022e3261
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_msg_rx.c
@@ -0,0 +1,1566 @@
+/**
+ ****************************************************************************************
+ *
+ * @file rwnx_msg_rx.c
+ *
+ * @brief RX function definitions
+ *
+ * Copyright (C) RivieraWaves 2012-2019
+ *
+ ****************************************************************************************
+ */
+
+#include <linux/vmalloc.h>
+#include "rwnx_defs.h"
+#include "rwnx_prof.h"
+#include "rwnx_tx.h"
+#ifdef CONFIG_RWNX_BFMER
+#include "rwnx_bfmer.h"
+#endif //(CONFIG_RWNX_BFMER)
+#ifdef CONFIG_RWNX_FULLMAC
+#include "rwnx_debugfs.h"
+#include "rwnx_msg_tx.h"
+#include "rwnx_tdls.h"
+#endif /* CONFIG_RWNX_FULLMAC */
+#include "rwnx_events.h"
+#include "rwnx_compat.h"
+#include "aicwf_txrxif.h"
+#ifdef CONFIG_USE_WIRELESS_EXT
+#include "aicwf_wext_linux.h"
+#endif
+
+static int rwnx_freq_to_idx(struct rwnx_hw *rwnx_hw, int freq)
+{
+ struct ieee80211_supported_band *sband;
+ int band, ch, idx = 0;
+
+ for (band = NL80211_BAND_2GHZ; band < NUM_NL80211_BANDS; band++) {
+#ifdef CONFIG_RWNX_FULLMAC
+ sband = rwnx_hw->wiphy->bands[band];
+#endif /* CONFIG_RWNX_FULLMAC */
+ if (!sband) {
+ continue;
+ }
+
+ for (ch = 0; ch < sband->n_channels; ch++, idx++) {
+ if (sband->channels[ch].center_freq == freq) {
+ goto exit;
+ }
+ }
+ }
+
+ BUG_ON(1);
+
+exit:
+ // Channel has been found, return the index
+ return idx;
+}
+
+/***************************************************************************
+ * Messages from MM task
+ **************************************************************************/
+static inline int rwnx_rx_chan_pre_switch_ind(struct rwnx_hw *rwnx_hw,
+ struct rwnx_cmd *cmd,
+ struct ipc_e2a_msg *msg)
+{
+ struct rwnx_vif *rwnx_vif;
+ int chan_idx = ((struct mm_channel_pre_switch_ind *)msg->param)->chan_index;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ REG_SW_SET_PROFILING_CHAN(rwnx_hw, SW_PROF_CHAN_CTXT_PSWTCH_BIT);
+
+#ifdef CONFIG_RWNX_FULLMAC
+ list_for_each_entry(rwnx_vif, &rwnx_hw->vifs, list) {
+ if (rwnx_vif->up && rwnx_vif->ch_index == chan_idx) {
+ AICWFDBG(LOGDEBUG, "rwnx_txq_vif_stop\r\n");
+ rwnx_txq_vif_stop(rwnx_vif, RWNX_TXQ_STOP_CHAN, rwnx_hw);
+ }
+ }
+#endif /* CONFIG_RWNX_FULLMAC */
+
+ REG_SW_CLEAR_PROFILING_CHAN(rwnx_hw, SW_PROF_CHAN_CTXT_PSWTCH_BIT);
+
+ return 0;
+}
+
+static inline int rwnx_rx_chan_switch_ind(struct rwnx_hw *rwnx_hw,
+ struct rwnx_cmd *cmd,
+ struct ipc_e2a_msg *msg)
+{
+ struct rwnx_vif *rwnx_vif;
+ int chan_idx = ((struct mm_channel_switch_ind *)msg->param)->chan_index;
+ bool roc = ((struct mm_channel_switch_ind *)msg->param)->roc;
+ bool roc_tdls = ((struct mm_channel_switch_ind *)msg->param)->roc_tdls;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ REG_SW_SET_PROFILING_CHAN(rwnx_hw, SW_PROF_CHAN_CTXT_SWTCH_BIT);
+
+#ifdef CONFIG_RWNX_FULLMAC
+ if (roc_tdls) {
+ u8 vif_index = ((struct mm_channel_switch_ind *)msg->param)->vif_index;
+ list_for_each_entry(rwnx_vif, &rwnx_hw->vifs, list) {
+ if (rwnx_vif->vif_index == vif_index) {
+ rwnx_vif->roc_tdls = true;
+ rwnx_txq_tdls_sta_start(rwnx_vif, RWNX_TXQ_STOP_CHAN, rwnx_hw);
+ }
+ }
+ } else if (!roc) {
+ list_for_each_entry(rwnx_vif, &rwnx_hw->vifs, list) {
+ if (rwnx_vif->up && rwnx_vif->ch_index == chan_idx) {
+ rwnx_txq_vif_start(rwnx_vif, RWNX_TXQ_STOP_CHAN, rwnx_hw);
+ }
+ }
+ } else {
+ /* Retrieve the allocated RoC element */
+ struct rwnx_roc_elem *roc_elem = rwnx_hw->roc_elem;
+
+ /* If mgmt_roc is true, remain on channel has been started by ourself */
+ if (!roc_elem->mgmt_roc) {
+ /* Inform the host that we have switch on the indicated off-channel */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0)
+ cfg80211_ready_on_channel(roc_elem->wdev->netdev, (u64)(rwnx_hw->roc_cookie_cnt),
+ roc_elem->chan, NL80211_CHAN_HT20, roc_elem->duration, GFP_ATOMIC);
+#elif LINUX_VERSION_CODE < KERNEL_VERSION(3, 8, 0)
+ cfg80211_ready_on_channel(roc_elem->wdev, (u64)(rwnx_hw->roc_cookie_cnt),
+ roc_elem->chan, NL80211_CHAN_HT20, roc_elem->duration, GFP_ATOMIC);
+#else
+ cfg80211_ready_on_channel(roc_elem->wdev, (u64)(rwnx_hw->roc_cookie_cnt),
+ roc_elem->chan, roc_elem->duration, GFP_ATOMIC);
+#endif
+ }
+
+ /* Keep in mind that we have switched on the channel */
+ roc_elem->on_chan = true;
+
+ // Enable traffic on OFF channel queue
+ rwnx_txq_offchan_start(rwnx_hw);
+ }
+
+ tasklet_schedule(&rwnx_hw->task);
+
+ rwnx_hw->cur_chanctx = chan_idx;
+ rwnx_radar_detection_enable_on_cur_channel(rwnx_hw);
+
+#endif /* CONFIG_RWNX_FULLMAC */
+
+ REG_SW_CLEAR_PROFILING_CHAN(rwnx_hw, SW_PROF_CHAN_CTXT_SWTCH_BIT);
+
+ return 0;
+}
+
+static inline int rwnx_rx_tdls_chan_switch_cfm(struct rwnx_hw *rwnx_hw,
+ struct rwnx_cmd *cmd,
+ struct ipc_e2a_msg *msg)
+{
+ return 0;
+}
+
+static inline int rwnx_rx_tdls_chan_switch_ind(struct rwnx_hw *rwnx_hw,
+ struct rwnx_cmd *cmd,
+ struct ipc_e2a_msg *msg)
+{
+#ifdef CONFIG_RWNX_FULLMAC
+ // Enable traffic on OFF channel queue
+ rwnx_txq_offchan_start(rwnx_hw);
+
+ return 0;
+#endif
+}
+
+static inline int rwnx_rx_tdls_chan_switch_base_ind(struct rwnx_hw *rwnx_hw,
+ struct rwnx_cmd *cmd,
+ struct ipc_e2a_msg *msg)
+{
+ struct rwnx_vif *rwnx_vif;
+ u8 vif_index = ((struct tdls_chan_switch_base_ind *)msg->param)->vif_index;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+#ifdef CONFIG_RWNX_FULLMAC
+ list_for_each_entry(rwnx_vif, &rwnx_hw->vifs, list) {
+ if (rwnx_vif->vif_index == vif_index) {
+ rwnx_vif->roc_tdls = false;
+ rwnx_txq_tdls_sta_stop(rwnx_vif, RWNX_TXQ_STOP_CHAN, rwnx_hw);
+ }
+ }
+ return 0;
+#endif
+}
+
+static inline int rwnx_rx_tdls_peer_ps_ind(struct rwnx_hw *rwnx_hw,
+ struct rwnx_cmd *cmd,
+ struct ipc_e2a_msg *msg)
+{
+ struct rwnx_vif *rwnx_vif;
+ u8 vif_index = ((struct tdls_peer_ps_ind *)msg->param)->vif_index;
+ bool ps_on = ((struct tdls_peer_ps_ind *)msg->param)->ps_on;
+
+#ifdef CONFIG_RWNX_FULLMAC
+ list_for_each_entry(rwnx_vif, &rwnx_hw->vifs, list) {
+ if (rwnx_vif->vif_index == vif_index) {
+ rwnx_vif->sta.tdls_sta->tdls.ps_on = ps_on;
+ // Update PS status for the TDLS station
+ rwnx_ps_bh_enable(rwnx_hw, rwnx_vif->sta.tdls_sta, ps_on);
+ }
+ }
+
+ return 0;
+#endif
+}
+
+static inline int rwnx_rx_remain_on_channel_exp_ind(struct rwnx_hw *rwnx_hw,
+ struct rwnx_cmd *cmd,
+ struct ipc_e2a_msg *msg)
+{
+#ifdef CONFIG_RWNX_FULLMAC
+ /* Retrieve the allocated RoC element */
+ struct rwnx_roc_elem *roc_elem = rwnx_hw->roc_elem;
+ /* Get VIF on which RoC has been started */
+ struct rwnx_vif *rwnx_vif = NULL;// = container_of(roc_elem->wdev, struct rwnx_vif, wdev);
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+ if(roc_elem == NULL){
+ AICWFDBG(LOGERROR,"%s roc_elem == NULL \r\n", __func__);
+ return 0;
+ }
+
+ rwnx_vif = container_of(roc_elem->wdev, struct rwnx_vif, wdev);
+
+
+#ifdef CREATE_TRACE_POINTS
+ /* For debug purpose (use ftrace kernel option) */
+ trace_roc_exp(rwnx_vif->vif_index);
+#endif
+ /* If mgmt_roc is true, remain on channel has been started by ourself */
+ /* If RoC has been cancelled before we switched on channel, do not call cfg80211 */
+ if (!roc_elem->mgmt_roc && roc_elem->on_chan) {
+ /* Inform the host that off-channel period has expired */
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0)
+ cfg80211_remain_on_channel_expired(roc_elem->wdev->netdev, (u64)(rwnx_hw->roc_cookie_cnt),
+ roc_elem->chan, NL80211_CHAN_HT20, GFP_ATOMIC);
+#elif LINUX_VERSION_CODE < KERNEL_VERSION(3, 8, 0)
+ cfg80211_remain_on_channel_expired(roc_elem->wdev, (u64)(rwnx_hw->roc_cookie_cnt),
+ roc_elem->chan, NL80211_CHAN_HT20, GFP_ATOMIC);
+#else
+ cfg80211_remain_on_channel_expired(roc_elem->wdev, (u64)(rwnx_hw->roc_cookie_cnt),
+ roc_elem->chan, GFP_ATOMIC);
+#endif
+ }
+
+ /* De-init offchannel TX queue */
+ rwnx_txq_offchan_deinit(rwnx_vif);
+
+ /* Increase the cookie counter cannot be zero */
+ rwnx_hw->roc_cookie_cnt++;
+
+ if (rwnx_hw->roc_cookie_cnt == 0) {
+ rwnx_hw->roc_cookie_cnt = 1;
+ }
+
+ /* Free the allocated RoC element */
+ kfree(roc_elem);
+ rwnx_hw->roc_elem = NULL;
+
+ AICWFDBG(LOGTRACE, "%s exit\r\n", __func__);
+#endif /* CONFIG_RWNX_FULLMAC */
+ return 0;
+}
+
+static inline int rwnx_rx_p2p_vif_ps_change_ind(struct rwnx_hw *rwnx_hw,
+ struct rwnx_cmd *cmd,
+ struct ipc_e2a_msg *msg)
+{
+ int vif_idx = ((struct mm_p2p_vif_ps_change_ind *)msg->param)->vif_index;
+ int ps_state = ((struct mm_p2p_vif_ps_change_ind *)msg->param)->ps_state;
+ struct rwnx_vif *vif_entry;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+#ifdef CONFIG_RWNX_FULLMAC
+ vif_entry = rwnx_hw->vif_table[vif_idx];
+
+ if (vif_entry) {
+ goto found_vif;
+ }
+#endif /* CONFIG_RWNX_FULLMAC */
+
+ goto exit;
+
+found_vif:
+
+#ifdef CONFIG_RWNX_FULLMAC
+ if (ps_state == MM_PS_MODE_OFF) {
+ // Start TX queues for provided VIF
+ rwnx_txq_vif_start(vif_entry, RWNX_TXQ_STOP_VIF_PS, rwnx_hw);
+ }
+ else {
+ // Stop TX queues for provided VIF
+ rwnx_txq_vif_stop(vif_entry, RWNX_TXQ_STOP_VIF_PS, rwnx_hw);
+ }
+#endif /* CONFIG_RWNX_FULLMAC */
+
+exit:
+ return 0;
+}
+
+static inline int rwnx_rx_channel_survey_ind(struct rwnx_hw *rwnx_hw,
+ struct rwnx_cmd *cmd,
+ struct ipc_e2a_msg *msg)
+{
+ struct mm_channel_survey_ind *ind = (struct mm_channel_survey_ind *)msg->param;
+ // Get the channel index
+ int idx = rwnx_freq_to_idx(rwnx_hw, ind->freq);
+ // Get the survey
+ struct rwnx_survey_info *rwnx_survey;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ if (idx > ARRAY_SIZE(rwnx_hw->survey))
+ return 0;
+
+ rwnx_survey = &rwnx_hw->survey[idx];
+
+ // Store the received parameters
+ rwnx_survey->chan_time_ms = ind->chan_time_ms;
+ rwnx_survey->chan_time_busy_ms = ind->chan_time_busy_ms;
+ rwnx_survey->noise_dbm = ind->noise_dbm;
+ rwnx_survey->filled = (SURVEY_INFO_TIME |
+ SURVEY_INFO_TIME_BUSY);
+
+ if (ind->noise_dbm != 0) {
+ rwnx_survey->filled |= SURVEY_INFO_NOISE_DBM;
+ }
+
+ return 0;
+}
+
+static inline int rwnx_rx_p2p_noa_upd_ind(struct rwnx_hw *rwnx_hw,
+ struct rwnx_cmd *cmd,
+ struct ipc_e2a_msg *msg)
+{
+ return 0;
+}
+
+static inline int rwnx_rx_rssi_status_ind(struct rwnx_hw *rwnx_hw,
+ struct rwnx_cmd *cmd,
+ struct ipc_e2a_msg *msg)
+{
+ struct mm_rssi_status_ind *ind = (struct mm_rssi_status_ind *)msg->param;
+ int vif_idx = ind->vif_index;
+ bool rssi_status = ind->rssi_status;
+
+ struct rwnx_vif *vif_entry;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+#ifdef CONFIG_RWNX_FULLMAC
+ vif_entry = rwnx_hw->vif_table[vif_idx];
+ if (vif_entry) {
+ cfg80211_cqm_rssi_notify(vif_entry->ndev,
+ rssi_status ? NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW :
+ NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH,
+ ind->rssi, GFP_ATOMIC);
+ }
+#endif /* CONFIG_RWNX_FULLMAC */
+
+ return 0;
+}
+
+static inline int rwnx_rx_pktloss_notify_ind(struct rwnx_hw *rwnx_hw,
+ struct rwnx_cmd *cmd,
+ struct ipc_e2a_msg *msg)
+{
+#ifdef CONFIG_RWNX_FULLMAC
+ struct mm_pktloss_ind *ind = (struct mm_pktloss_ind *)msg->param;
+ struct rwnx_vif *vif_entry;
+ int vif_idx = ind->vif_index;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ vif_entry = rwnx_hw->vif_table[vif_idx];
+ if (vif_entry) {
+ cfg80211_cqm_pktloss_notify(vif_entry->ndev, (const u8 *)ind->mac_addr.array,
+ ind->num_packets, GFP_ATOMIC);
+ }
+#endif /* CONFIG_RWNX_FULLMAC */
+
+ return 0;
+}
+
+static inline int rwnx_apm_staloss_ind(struct rwnx_hw *rwnx_hw,
+ struct rwnx_cmd *cmd,
+ struct ipc_e2a_msg *msg)
+{
+ struct mm_apm_staloss_ind *ind = (struct mm_apm_staloss_ind *)msg->param;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ memcpy(rwnx_hw->sta_mac_addr, ind->mac_addr, 6);
+ rwnx_hw->apm_vif_idx = ind->vif_idx;
+
+ queue_work(rwnx_hw->apmStaloss_wq, &rwnx_hw->apmStalossWork);
+
+ return 0;
+}
+
+
+static inline int rwnx_rx_csa_counter_ind(struct rwnx_hw *rwnx_hw,
+ struct rwnx_cmd *cmd,
+ struct ipc_e2a_msg *msg)
+{
+ struct mm_csa_counter_ind *ind = (struct mm_csa_counter_ind *)msg->param;
+ struct rwnx_vif *vif;
+ bool found = false;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ // Look for VIF entry
+ list_for_each_entry(vif, &rwnx_hw->vifs, list) {
+ if (vif->vif_index == ind->vif_index) {
+ found=true;
+ break;
+ }
+ }
+
+ if (found) {
+#ifdef CONFIG_RWNX_FULLMAC
+ if (vif->ap.csa)
+ vif->ap.csa->count = ind->csa_count;
+ else
+ netdev_err(vif->ndev, "CSA counter update but no active CSA");
+
+#endif
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_RWNX_FULLMAC
+static inline int rwnx_rx_csa_finish_ind(struct rwnx_hw *rwnx_hw,
+ struct rwnx_cmd *cmd,
+ struct ipc_e2a_msg *msg)
+{
+ struct mm_csa_finish_ind *ind = (struct mm_csa_finish_ind *)msg->param;
+ struct rwnx_vif *vif;
+ bool found = false;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ // Look for VIF entry
+ list_for_each_entry(vif, &rwnx_hw->vifs, list) {
+ if (vif->vif_index == ind->vif_index) {
+ found=true;
+ break;
+ }
+ }
+
+ if (found) {
+ if (RWNX_VIF_TYPE(vif) == NL80211_IFTYPE_AP ||
+ RWNX_VIF_TYPE(vif) == NL80211_IFTYPE_P2P_GO) {
+ if (vif->ap.csa) {
+ vif->ap.csa->status = ind->status;
+ vif->ap.csa->ch_idx = ind->chan_idx;
+ schedule_work(&vif->ap.csa->work);
+ } else
+ netdev_err(vif->ndev, "CSA finish indication but no active CSA");
+ } else {
+ if (ind->status == 0) {
+ rwnx_chanctx_unlink(vif);
+ rwnx_chanctx_link(vif, ind->chan_idx, NULL);
+ if (rwnx_hw->cur_chanctx == ind->chan_idx) {
+ rwnx_radar_detection_enable_on_cur_channel(rwnx_hw);
+ rwnx_txq_vif_start(vif, RWNX_TXQ_STOP_CHAN, rwnx_hw);
+ } else
+ rwnx_txq_vif_stop(vif, RWNX_TXQ_STOP_CHAN, rwnx_hw);
+ }
+ }
+ }
+
+ return 0;
+}
+
+static inline int rwnx_rx_csa_traffic_ind(struct rwnx_hw *rwnx_hw,
+ struct rwnx_cmd *cmd,
+ struct ipc_e2a_msg *msg)
+{
+ struct mm_csa_traffic_ind *ind = (struct mm_csa_traffic_ind *)msg->param;
+ struct rwnx_vif *vif;
+ bool found = false;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ // Look for VIF entry
+ list_for_each_entry(vif, &rwnx_hw->vifs, list) {
+ if (vif->vif_index == ind->vif_index) {
+ found=true;
+ break;
+ }
+ }
+
+ if (found) {
+ if (ind->enable)
+ rwnx_txq_vif_start(vif, RWNX_TXQ_STOP_CSA, rwnx_hw);
+ else
+ rwnx_txq_vif_stop(vif, RWNX_TXQ_STOP_CSA, rwnx_hw);
+ }
+
+ return 0;
+}
+
+static inline int rwnx_rx_ps_change_ind(struct rwnx_hw *rwnx_hw,
+ struct rwnx_cmd *cmd,
+ struct ipc_e2a_msg *msg)
+{
+ struct mm_ps_change_ind *ind = (struct mm_ps_change_ind *)msg->param;
+ struct rwnx_sta *sta = &rwnx_hw->sta_table[ind->sta_idx];
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+#if 1//2022-01-15 add for rwnx_hw->vif_table[sta->vif_idx] if null when rwnx_close
+ if (!rwnx_hw->vif_table[sta->vif_idx]){
+ wiphy_err(rwnx_hw->wiphy, "rwnx_hw->vif_table[sta->vif_idx] is null\n");
+ return 0;
+ }
+#endif
+
+ if (ind->sta_idx >= (NX_REMOTE_STA_MAX + NX_VIRT_DEV_MAX)) {
+ wiphy_err(rwnx_hw->wiphy, "Invalid sta index reported by fw %d\n",
+ ind->sta_idx);
+ return 1;
+ }
+
+ netdev_dbg(rwnx_hw->vif_table[sta->vif_idx]->ndev,
+ "Sta %d, change PS mode to %s", sta->sta_idx,
+ ind->ps_state ? "ON" : "OFF");
+
+ if (sta->valid) {
+ rwnx_ps_bh_enable(rwnx_hw, sta, ind->ps_state);
+ } else if (rwnx_hw->adding_sta) {
+ sta->ps.active = ind->ps_state ? true : false;
+ } else {
+ netdev_err(rwnx_hw->vif_table[sta->vif_idx]->ndev,
+ "Ignore PS mode change on invalid sta\n");
+ }
+
+
+ return 0;
+}
+
+
+static inline int rwnx_rx_traffic_req_ind(struct rwnx_hw *rwnx_hw,
+ struct rwnx_cmd *cmd,
+ struct ipc_e2a_msg *msg)
+{
+ struct mm_traffic_req_ind *ind = (struct mm_traffic_req_ind *)msg->param;
+ struct rwnx_sta *sta = &rwnx_hw->sta_table[ind->sta_idx];
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ netdev_dbg(rwnx_hw->vif_table[sta->vif_idx]->ndev,
+ "Sta %d, asked for %d pkt", sta->sta_idx, ind->pkt_cnt);
+
+ rwnx_ps_bh_traffic_req(rwnx_hw, sta, ind->pkt_cnt,
+ ind->uapsd ? UAPSD_ID : LEGACY_PS_ID);
+
+ return 0;
+}
+#endif /* CONFIG_RWNX_FULLMAC */
+
+/***************************************************************************
+ * Messages from SCAN task
+ **************************************************************************/
+#if 0
+static inline int rwnx_rx_scan_done_ind(struct rwnx_hw *rwnx_hw,
+ struct rwnx_cmd *cmd,
+ struct ipc_e2a_msg *msg)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0)
+ struct cfg80211_scan_info info = {
+ .aborted = false,
+ };
+#endif
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ rwnx_ipc_elem_var_deallocs(rwnx_hw, &rwnx_hw->scan_ie);
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0)
+ ieee80211_scan_completed(rwnx_hw->hw, &info);
+#else
+ ieee80211_scan_completed(rwnx_hw->hw, false);
+#endif
+
+ return 0;
+}
+#endif
+
+/***************************************************************************
+ * Messages from SCANU task
+ **************************************************************************/
+#ifdef CONFIG_RWNX_FULLMAC
+extern uint8_t scanning;
+static inline int rwnx_rx_scanu_start_cfm(struct rwnx_hw *rwnx_hw,
+ struct rwnx_cmd *cmd,
+ struct ipc_e2a_msg *msg)
+{
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0)
+ struct cfg80211_scan_info info = {
+ .aborted = false,
+ };
+#endif
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ if (rwnx_hw->scan_request
+#ifdef CONFIG_SCHED_SCAN
+ && !rwnx_hw->is_sched_scan
+#endif//CONFIG_SCHED_SCAN
+#ifdef CONFIG_USE_WIRELESS_EXT
+ && !rwnx_hw->wext_scan) {
+#else
+ ){
+#endif
+
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0)
+ cfg80211_scan_done(rwnx_hw->scan_request, &info);
+#else
+ cfg80211_scan_done(rwnx_hw->scan_request, false);
+#endif
+ }
+
+
+#ifdef CONFIG_USE_WIRELESS_EXT
+ else if(rwnx_hw->wext_scan){
+ rwnx_hw->wext_scan = 0;
+ AICWFDBG(LOGDEBUG, "%s rwnx_hw->wext_scan done!!\r\n", __func__);
+ if(rwnx_hw->scan_request){
+ vfree(rwnx_hw->scan_request);
+ }
+ complete(&rwnx_hw->wext_scan_com);
+ }
+#endif
+ else {
+ AICWFDBG(LOGERROR, "%s rwnx_hw->scan_request is NULL!!\r\n", __func__);
+ }
+
+#ifdef CONFIG_SCHED_SCAN
+ if(rwnx_hw->is_sched_scan){
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)
+ AICWFDBG(LOGINFO, "%s cfg80211_sched_scan_results \r\n", __func__);
+ cfg80211_sched_scan_results(rwnx_hw->scan_request->wiphy,
+ rwnx_hw->sched_scan_req->reqid);
+#else
+ cfg80211_sched_scan_results(rwnx_hw->sched_scan_req->wiphy);
+#endif
+ kfree(rwnx_hw->scan_request);
+ rwnx_hw->is_sched_scan = false;
+ }
+#endif//CONFIG_SCHED_SCAN
+
+ rwnx_hw->scan_request = NULL;
+ scanning = 0;
+
+ return 0;
+}
+
+static inline int rwnx_rx_scanu_result_ind(struct rwnx_hw *rwnx_hw,
+ struct rwnx_cmd *cmd,
+ struct ipc_e2a_msg *msg)
+{
+ struct cfg80211_bss *bss = NULL;
+ struct ieee80211_channel *chan;
+ struct scanu_result_ind *ind = (struct scanu_result_ind *)msg->param;
+ struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)ind->payload;
+
+#if 0
+ const u8 *ie = mgmt->u.beacon.variable;
+ char *ssid = NULL;
+ int ssid_len = 0;
+ int freq = 0;
+#endif
+
+#ifdef CONFIG_USE_WIRELESS_EXT
+ struct scanu_result_wext *scan_re_wext;
+#endif
+
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ chan = ieee80211_get_channel(rwnx_hw->wiphy, ind->center_freq);
+
+ if (chan != NULL) {
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0)
+ //ktime_t ts;
+ struct timespec ts;
+ get_monotonic_boottime(&ts);
+ //ts = ktime_get_real();
+ mgmt->u.probe_resp.timestamp = ((u64)ts.tv_sec*1000000) + ts.tv_nsec/1000;
+ #else
+ struct timespec64 ts;
+ ktime_get_real_ts64(&ts);
+ mgmt->u.probe_resp.timestamp = ((u64)ts.tv_sec*1000000) + ts.tv_nsec/1000;
+ #endif
+ bss = cfg80211_inform_bss_frame(rwnx_hw->wiphy, chan,
+ (struct ieee80211_mgmt *)ind->payload,
+ ind->length, ind->rssi * 100, GFP_ATOMIC);
+#if 0
+ //print scan result info start
+ if(ie != NULL){
+ ssid_len = ie[1];
+ ssid = (char *)vmalloc(sizeof(char)* (ssid_len + 1));
+ if(ssid != NULL){
+ memset(ssid, 0, ssid_len + 1);
+ memcpy(ssid, &ie[2], ssid_len);
+ freq = ind->center_freq;
+ AICWFDBG(LOGDEBUG, "%s %02x:%02x:%02x:%02x:%02x:%02x ssid:%s freq:%d timestamp:%ld\r\n", __func__,
+ bss->bssid[0],bss->bssid[1],bss->bssid[2],
+ bss->bssid[3],bss->bssid[4],bss->bssid[5],
+ ssid, freq, (long)mgmt->u.probe_resp.timestamp);
+ vfree(ssid);
+ ssid = NULL;
+ }else{
+ AICWFDBG(LOGERROR, "%s ssid vmalloc fail skip printk ssid info \r\n", __func__);
+ }
+ }
+ //print scan result info end
+#endif
+
+#ifdef CONFIG_USE_WIRELESS_EXT
+ if(rwnx_hw->wext_scan){
+
+ scan_re_wext = (struct scanu_result_wext *)vmalloc(sizeof(struct scanu_result_wext));
+ scan_re_wext->ind = (struct scanu_result_ind *)vmalloc(sizeof(struct scanu_result_ind));
+ scan_re_wext->payload = (u32_l *)vmalloc(sizeof(u32_l) * ind->length);
+
+ memset(scan_re_wext->ind, 0, sizeof(struct scanu_result_ind));
+ memset(scan_re_wext->payload, 0, ind->length);
+
+ memcpy(scan_re_wext->ind, ind, sizeof(struct scanu_result_ind));
+ memcpy(scan_re_wext->payload, ind->payload, ind->length);
+
+ scan_re_wext->bss = bss;
+
+ INIT_LIST_HEAD(&scan_re_wext->scanu_re_list);
+ list_add_tail(&scan_re_wext->scanu_re_list, &rwnx_hw->wext_scanre_list);
+ return 0;
+ }
+#endif
+
+ }
+
+ if (bss != NULL)
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 9, 0)
+ cfg80211_put_bss(bss);
+#else
+ cfg80211_put_bss(rwnx_hw->wiphy, bss);
+#endif
+
+ return 0;
+}
+#endif /* CONFIG_RWNX_FULLMAC */
+
+/***************************************************************************
+ * Messages from ME task
+ **************************************************************************/
+#ifdef CONFIG_RWNX_FULLMAC
+static inline int rwnx_rx_me_tkip_mic_failure_ind(struct rwnx_hw *rwnx_hw,
+ struct rwnx_cmd *cmd,
+ struct ipc_e2a_msg *msg)
+{
+ struct me_tkip_mic_failure_ind *ind = (struct me_tkip_mic_failure_ind *)msg->param;
+ struct rwnx_vif *rwnx_vif = rwnx_hw->vif_table[ind->vif_idx];
+ struct net_device *dev = rwnx_vif->ndev;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ cfg80211_michael_mic_failure(dev, (u8 *)&ind->addr, (ind->ga?NL80211_KEYTYPE_GROUP:
+ NL80211_KEYTYPE_PAIRWISE), ind->keyid,
+ (u8 *)&ind->tsc, GFP_ATOMIC);
+
+ return 0;
+}
+
+static inline int rwnx_rx_me_tx_credits_update_ind(struct rwnx_hw *rwnx_hw,
+ struct rwnx_cmd *cmd,
+ struct ipc_e2a_msg *msg)
+{
+ struct me_tx_credits_update_ind *ind = (struct me_tx_credits_update_ind *)msg->param;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ rwnx_txq_credit_update(rwnx_hw, ind->sta_idx, ind->tid, ind->credits);
+
+ return 0;
+}
+#endif /* CONFIG_RWNX_FULLMAC */
+
+/***************************************************************************
+ * Messages from SM task
+ **************************************************************************/
+#ifdef CONFIG_RWNX_FULLMAC
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 8, 0)
+static inline void cfg80211_chandef_create(struct cfg80211_chan_def *chandef,
+ struct ieee80211_channel *chan,
+ enum nl80211_channel_type chan_type)
+{
+ if (WARN_ON(!chan))
+ return;
+ chandef->chan = chan;
+ chandef->center_freq2 = 0;
+ switch (chan_type) {
+ case NL80211_CHAN_NO_HT:
+ chandef->width = NL80211_CHAN_WIDTH_20_NOHT;
+ chandef->center_freq1 = chan->center_freq;
+ break;
+ case NL80211_CHAN_HT20:
+ chandef->width = NL80211_CHAN_WIDTH_20;
+ chandef->center_freq1 = chan->center_freq;
+ break;
+ case NL80211_CHAN_HT40PLUS:
+ chandef->width = NL80211_CHAN_WIDTH_40;
+ chandef->center_freq1 = chan->center_freq + 10;
+ break;
+ case NL80211_CHAN_HT40MINUS:
+ chandef->width = NL80211_CHAN_WIDTH_40;
+ chandef->center_freq1 = chan->center_freq - 10;
+ break;
+ default:
+ WARN_ON(1);
+ }
+}
+#endif
+static inline int rwnx_rx_sm_connect_ind(struct rwnx_hw *rwnx_hw,
+ struct rwnx_cmd *cmd,
+ struct ipc_e2a_msg *msg)
+{
+ struct sm_connect_ind *ind = (struct sm_connect_ind *)msg->param;
+ struct rwnx_vif *rwnx_vif = rwnx_hw->vif_table[ind->vif_idx];
+ struct net_device *dev = NULL;
+ const u8 *req_ie, *rsp_ie;
+ const u8 *extcap_ie;
+ const struct ieee_types_extcap *extcap;
+ struct ieee80211_channel *chan;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ if(!rwnx_vif){
+ AICWFDBG(LOGERROR, "%s rwnx_vif is null \r\n", __func__);
+ return 0;
+ }
+ dev = rwnx_vif->ndev;
+
+
+ /* Retrieve IE addresses and lengths */
+ req_ie = (const u8 *)ind->assoc_ie_buf;
+ rsp_ie = req_ie + ind->assoc_req_ie_len;
+
+
+ if (rwnx_vif->sta.external_auth)
+ rwnx_vif->sta.external_auth = false;
+
+
+ // Fill-in the AP information
+ AICWFDBG(LOGINFO, "%s ind->status_code:%d \r\n", __func__, ind->status_code);
+ if (ind->status_code == 0)
+ {
+ struct rwnx_sta *sta = &rwnx_hw->sta_table[ind->ap_idx];
+ u8 txq_status;
+ struct cfg80211_chan_def chandef;
+
+ sta->valid = true;
+ sta->sta_idx = ind->ap_idx;
+ sta->ch_idx = ind->ch_idx;
+ sta->vif_idx = ind->vif_idx;
+ sta->vlan_idx = sta->vif_idx;
+ sta->qos = ind->qos;
+ sta->acm = ind->acm;
+ sta->ps.active = false;
+ sta->aid = ind->aid;
+ sta->band = ind->band;
+ sta->width = ind->width;
+ sta->center_freq = ind->center_freq;
+ sta->center_freq1 = ind->center_freq1;
+ sta->center_freq2 = ind->center_freq2;
+ rwnx_vif->sta.ap = sta;
+
+ chan = ieee80211_get_channel(rwnx_hw->wiphy, ind->center_freq);
+ cfg80211_chandef_create(&chandef, chan, NL80211_CHAN_NO_HT);
+ if (!rwnx_hw->mod_params->ht_on)
+ chandef.width = NL80211_CHAN_WIDTH_20_NOHT;
+ else
+ chandef.width = chnl2bw[ind->width];
+ chandef.center_freq1 = ind->center_freq1;
+ chandef.center_freq2 = ind->center_freq2;
+ rwnx_chanctx_link(rwnx_vif, ind->ch_idx, &chandef);
+ memcpy(sta->mac_addr, ind->bssid.array, ETH_ALEN);
+ if (ind->ch_idx == rwnx_hw->cur_chanctx) {
+ txq_status = 0;
+ } else {
+ txq_status = RWNX_TXQ_STOP_CHAN;
+ }
+ memcpy(sta->ac_param, ind->ac_param, sizeof(sta->ac_param));
+ rwnx_txq_sta_init(rwnx_hw, sta, txq_status);
+ rwnx_txq_tdls_vif_init(rwnx_vif);
+#ifdef CONFIG_DEBUG_FS
+ rwnx_dbgfs_register_rc_stat(rwnx_hw, sta);
+#endif
+ rwnx_mu_group_sta_init(sta, NULL);
+ /* Look for TDLS Channel Switch Prohibited flag in the Extended Capability
+ * Information Element*/
+ extcap_ie = cfg80211_find_ie(WLAN_EID_EXT_CAPABILITY, rsp_ie, ind->assoc_rsp_ie_len);
+ if (extcap_ie && extcap_ie[1] >= 5) {
+ extcap = (void *)(extcap_ie);
+ rwnx_vif->tdls_chsw_prohibited = extcap->ext_capab[4] & WLAN_EXT_CAPA5_TDLS_CH_SW_PROHIBITED;
+ }
+
+ if(atomic_read(&rwnx_hw->sta_flowctrl[sta->sta_idx].tx_pending_cnt) > 0) {
+ AICWFDBG(LOGDEBUG, "sta idx %d fc error %d\n", sta->sta_idx, atomic_read(&rwnx_hw->sta_flowctrl[sta->sta_idx].tx_pending_cnt));
+ }
+
+ if (rwnx_vif->wep_enabled)
+ rwnx_vif->wep_auth_err = false;
+
+#ifdef CONFIG_RWNX_BFMER
+ /* If Beamformer feature is activated, check if features can be used
+ * with the new peer device
+ */
+ if (rwnx_hw->mod_params->bfmer) {
+ const u8 *vht_capa_ie;
+ const struct ieee80211_vht_cap *vht_cap;
+
+ do {
+ /* Look for VHT Capability Information Element */
+ vht_capa_ie = cfg80211_find_ie(WLAN_EID_VHT_CAPABILITY, rsp_ie,
+ ind->assoc_rsp_ie_len);
+
+ /* Stop here if peer device does not support VHT */
+ if (!vht_capa_ie) {
+ break;
+ }
+
+ vht_cap = (const struct ieee80211_vht_cap *)(vht_capa_ie + 2);
+
+ /* Send MM_BFMER_ENABLE_REQ message if needed */
+ rwnx_send_bfmer_enable(rwnx_hw, sta, vht_cap);
+ } while (0);
+ }
+#endif //(CONFIG_RWNX_BFMER)
+
+#ifdef CONFIG_RWNX_MON_DATA
+ // If there are 1 sta and 1 monitor interface active at the same time then
+ // monitor interface channel context is always the same as the STA interface.
+ // This doesn't work with 2 STA interfaces but we don't want to support it.
+ if (rwnx_hw->monitor_vif != RWNX_INVALID_VIF) {
+ struct rwnx_vif *rwnx_mon_vif = rwnx_hw->vif_table[rwnx_hw->monitor_vif];
+ rwnx_chanctx_unlink(rwnx_mon_vif);
+ rwnx_chanctx_link(rwnx_mon_vif, ind->ch_idx, NULL);
+ }
+#endif
+ atomic_set(&rwnx_vif->drv_conn_state, (int)RWNX_DRV_STATUS_CONNECTED);
+
+ } else if (ind->status_code == WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG) {
+ if (rwnx_vif->wep_enabled) {
+ rwnx_vif->wep_auth_err = true;
+ printk("con ind wep_auth_err %d\n", rwnx_vif->wep_auth_err);
+ }
+ atomic_set(&rwnx_vif->drv_conn_state, (int)RWNX_DRV_STATUS_DISCONNECTED);
+ }else{
+ atomic_set(&rwnx_vif->drv_conn_state, (int)RWNX_DRV_STATUS_DISCONNECTED);
+ }
+
+ if (!ind->roamed) {
+ cfg80211_connect_result(dev, (const u8 *)ind->bssid.array, req_ie,
+ ind->assoc_req_ie_len, rsp_ie,
+ ind->assoc_rsp_ie_len, ind->status_code,
+ GFP_ATOMIC);
+ }
+ else {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)
+ struct cfg80211_roam_info info;
+ memset(&info, 0, sizeof(info));
+ if (rwnx_vif->ch_index < NX_CHAN_CTXT_CNT)
+#if LINUX_VERSION_CODE < HIGH_KERNEL_VERSION
+ info.channel = rwnx_hw->chanctx_table[rwnx_vif->ch_index].chan_def.chan;
+#else
+ info.links[0].channel = rwnx_hw->chanctx_table[rwnx_vif->ch_index].chan_def.chan;
+#endif//LINUX_VERSION_CODE < HIGH_KERNEL_VERSION
+
+#if LINUX_VERSION_CODE < HIGH_KERNEL_VERSION
+ info.bssid = (const u8 *)ind->bssid.array;
+#else
+ info.links[0].bssid = (const u8 *)ind->bssid.array;;
+#endif//LINUX_VERSION_CODE < HIGH_KERNEL_VERSION
+ info.req_ie = req_ie;
+ info.req_ie_len = ind->assoc_req_ie_len;
+ info.resp_ie = rsp_ie;
+ info.resp_ie_len = ind->assoc_rsp_ie_len;
+ cfg80211_roamed(dev, &info, GFP_ATOMIC);
+#else
+ chan = ieee80211_get_channel(rwnx_hw->wiphy, ind->center_freq);
+ cfg80211_roamed(dev
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 39) || defined(COMPAT_KERNEL_RELEASE)
+ , chan
+#endif
+ , (const u8 *)ind->bssid.array
+ , req_ie
+ , ind->assoc_req_ie_len
+ , rsp_ie
+ , ind->assoc_rsp_ie_len
+ , GFP_ATOMIC);
+#endif /*LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)*/
+ }
+ netif_tx_start_all_queues(dev);
+ netif_carrier_on(dev);
+
+ return 0;
+}
+
+void rwnx_cfg80211_unlink_bss(struct rwnx_hw *rwnx_hw, struct rwnx_vif *rwnx_vif){
+ struct wiphy *wiphy = rwnx_hw->wiphy;
+ struct cfg80211_bss *bss = NULL;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ bss = cfg80211_get_bss(wiphy, NULL/*notify_channel*/,
+ rwnx_vif->sta.bssid, rwnx_vif->sta.ssid,
+ rwnx_vif->sta.ssid_len,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0)
+ IEEE80211_BSS_TYPE_ESS,
+ IEEE80211_PRIVACY(true));//temp set true
+#else
+ WLAN_CAPABILITY_ESS,
+ WLAN_CAPABILITY_ESS);
+#endif
+
+ if (bss) {
+ cfg80211_unlink_bss(wiphy, bss);
+ AICWFDBG(LOGINFO, "%s(): cfg80211_unlink %s!!\n", __func__, rwnx_vif->sta.ssid);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
+ cfg80211_put_bss(wiphy, bss);
+#else
+ cfg80211_put_bss(bss);
+#endif
+ }else{
+ AICWFDBG(LOGERROR, "%s(): cfg80211_unlink error %s!!\n", __func__, rwnx_vif->sta.ssid);
+ }
+
+ memset(rwnx_vif->sta.ssid, 0, rwnx_vif->sta.ssid_len);
+ rwnx_vif->sta.ssid_len = 0;
+ memset(rwnx_vif->sta.bssid, 0, ETH_ALEN);
+}
+
+
+extern u8 dhcped;
+static inline int rwnx_rx_sm_disconnect_ind(struct rwnx_hw *rwnx_hw,
+ struct rwnx_cmd *cmd,
+ struct ipc_e2a_msg *msg)
+{
+ struct sm_disconnect_ind *ind = (struct sm_disconnect_ind *)msg->param;
+ struct rwnx_vif *rwnx_vif = rwnx_hw->vif_table[ind->vif_idx];
+ struct net_device *dev;
+#ifdef AICWF_RX_REORDER
+ struct reord_ctrl_info *reord_info, *tmp;
+ u8 *macaddr;
+ struct aicwf_rx_priv *rx_priv;
+#endif
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+ dhcped = 0;
+
+ if(!rwnx_vif){
+ AICWFDBG(LOGERROR, "%s rwnx_vif is null \r\n", __func__);
+ //atomic_set(&rwnx_vif->drv_conn_state, RWNX_DRV_STATUS_DISCONNECTED);
+ return 0;
+ }
+ dev = rwnx_vif->ndev;
+
+ rwnx_cfg80211_unlink_bss(rwnx_hw, rwnx_vif);
+
+
+#ifdef CONFIG_BR_SUPPORT
+ struct rwnx_vif *vif = netdev_priv(dev);
+ /* clear bridge database */
+ nat25_db_cleanup(rwnx_vif);
+#endif /* CONFIG_BR_SUPPORT */
+
+ if(rwnx_vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT)
+ rwnx_hw->is_p2p_connected = 0;
+ /* if vif is not up, rwnx_close has already been called */
+ if (rwnx_vif->up) {
+ if (!ind->ft_over_ds && !ind->reassoc) {
+ cfg80211_disconnected(dev, ind->reason_code, NULL, 0,
+ (ind->reason_code <= 1), GFP_ATOMIC);
+ }
+ netif_tx_stop_all_queues(dev);
+ netif_carrier_off(dev);
+ }
+
+#ifdef CONFIG_RWNX_BFMER
+ /* Disable Beamformer if supported */
+ rwnx_bfmer_report_del(rwnx_hw, rwnx_vif->sta.ap);
+#endif //(CONFIG_RWNX_BFMER)
+
+#ifdef AICWF_RX_REORDER
+#ifdef AICWF_SDIO_SUPPORT
+ rx_priv = rwnx_hw->sdiodev->rx_priv;
+#else
+ rx_priv = rwnx_hw->usbdev->rx_priv;
+#endif
+ if((rwnx_vif->wdev.iftype == NL80211_IFTYPE_STATION) || (rwnx_vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT)) {
+ macaddr = rwnx_vif->ndev->dev_addr;
+ AICWFDBG(LOGINFO, "deinit:macaddr:%x,%x,%x,%x,%x,%x\r\n", macaddr[0],macaddr[1],macaddr[2], \
+ macaddr[3],macaddr[4],macaddr[5]);
+ list_for_each_entry_safe(reord_info, tmp, &rx_priv->stas_reord_list, list) {
+ macaddr = rwnx_vif->ndev->dev_addr;
+ AICWFDBG(LOGINFO, "reord_mac:%x,%x,%x,%x,%x,%x\r\n", reord_info->mac_addr[0],reord_info->mac_addr[1],reord_info->mac_addr[2], \
+ reord_info->mac_addr[3],reord_info->mac_addr[4],reord_info->mac_addr[5]);
+ if (!memcmp(reord_info->mac_addr, macaddr, 6)) {
+ reord_deinit_sta(rx_priv, reord_info);
+ break;
+ }
+ }
+ }
+ else if((rwnx_vif->wdev.iftype == NL80211_IFTYPE_AP) || (rwnx_vif->wdev.iftype == NL80211_IFTYPE_P2P_GO))
+ BUG();//should be not here: del_sta function
+#endif
+
+ rwnx_txq_sta_deinit(rwnx_hw, rwnx_vif->sta.ap);
+ rwnx_txq_tdls_vif_deinit(rwnx_vif);
+ #if 0
+ rwnx_dbgfs_unregister_rc_stat(rwnx_hw, rwnx_vif->sta.ap);
+ #endif
+ rwnx_vif->sta.ap->valid = false;
+ rwnx_vif->sta.ap = NULL;
+ rwnx_external_auth_disable(rwnx_vif);
+ rwnx_chanctx_unlink(rwnx_vif);
+
+ //msleep(200);
+ atomic_set(&rwnx_vif->drv_conn_state, (int)RWNX_DRV_STATUS_DISCONNECTED);
+ return 0;
+}
+
+static inline int rwnx_rx_sm_external_auth_required_ind(struct rwnx_hw *rwnx_hw,
+ struct rwnx_cmd *cmd,
+ struct ipc_e2a_msg *msg)
+{
+ struct sm_external_auth_required_ind *ind =
+ (struct sm_external_auth_required_ind *)msg->param;
+ struct rwnx_vif *rwnx_vif = rwnx_hw->vif_table[ind->vif_idx];
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0) || defined(CONFIG_WPA3_FOR_OLD_KERNEL)
+ struct net_device *dev = rwnx_vif->ndev;
+ struct cfg80211_external_auth_params params;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ params.action = NL80211_EXTERNAL_AUTH_START;
+ memcpy(params.bssid, ind->bssid.array, ETH_ALEN);
+ params.ssid.ssid_len = ind->ssid.length;
+ memcpy(params.ssid.ssid, ind->ssid.array,
+ min_t(size_t, ind->ssid.length, sizeof(params.ssid.ssid)));
+ params.key_mgmt_suite = ind->akm;
+
+ if ((ind->vif_idx > NX_VIRT_DEV_MAX) || !rwnx_vif->up ||
+ (RWNX_VIF_TYPE(rwnx_vif) != NL80211_IFTYPE_STATION) ||
+ cfg80211_external_auth_request(dev, &params, GFP_ATOMIC)) {
+ wiphy_err(rwnx_hw->wiphy, "Failed to start external auth on vif %d",
+ ind->vif_idx);
+ rwnx_send_sm_external_auth_required_rsp(rwnx_hw, rwnx_vif,
+ WLAN_STATUS_UNSPECIFIED_FAILURE);
+ return 0;
+ }
+
+ rwnx_external_auth_enable(rwnx_vif);
+#else
+ rwnx_send_sm_external_auth_required_rsp(rwnx_hw, rwnx_vif,
+ WLAN_STATUS_UNSPECIFIED_FAILURE);
+#endif
+ return 0;
+}
+
+
+static inline int rwnx_rx_mesh_path_create_cfm(struct rwnx_hw *rwnx_hw,
+ struct rwnx_cmd *cmd,
+ struct ipc_e2a_msg *msg)
+{
+ struct mesh_path_create_cfm *cfm = (struct mesh_path_create_cfm *)msg->param;
+ struct rwnx_vif *rwnx_vif = rwnx_hw->vif_table[cfm->vif_idx];
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ /* Check we well have a Mesh Point Interface */
+ if (rwnx_vif && (RWNX_VIF_TYPE(rwnx_vif) == NL80211_IFTYPE_MESH_POINT)) {
+ rwnx_vif->ap.create_path = false;
+ }
+
+ return 0;
+}
+
+static inline int rwnx_rx_mesh_peer_update_ind(struct rwnx_hw *rwnx_hw,
+ struct rwnx_cmd *cmd,
+ struct ipc_e2a_msg *msg)
+{
+ struct mesh_peer_update_ind *ind = (struct mesh_peer_update_ind *)msg->param;
+ struct rwnx_vif *rwnx_vif = rwnx_hw->vif_table[ind->vif_idx];
+ struct rwnx_sta *rwnx_sta = &rwnx_hw->sta_table[ind->sta_idx];
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ if ((ind->vif_idx >= (NX_VIRT_DEV_MAX + NX_REMOTE_STA_MAX)) ||
+ (rwnx_vif && (RWNX_VIF_TYPE(rwnx_vif) != NL80211_IFTYPE_MESH_POINT)) ||
+ (ind->sta_idx >= NX_REMOTE_STA_MAX))
+ return 1;
+
+ /* Check we well have a Mesh Point Interface */
+ if (!rwnx_vif->user_mpm)
+ {
+ /* Check if peer link has been established or lost */
+ if (ind->estab) {
+ if (!rwnx_sta->valid) {
+ u8 txq_status;
+
+ rwnx_sta->valid = true;
+ rwnx_sta->sta_idx = ind->sta_idx;
+ rwnx_sta->ch_idx = rwnx_vif->ch_index;
+ rwnx_sta->vif_idx = ind->vif_idx;
+ rwnx_sta->vlan_idx = rwnx_sta->vif_idx;
+ rwnx_sta->ps.active = false;
+ rwnx_sta->qos = true;
+ rwnx_sta->aid = ind->sta_idx + 1;
+ //rwnx_sta->acm = ind->acm;
+ memcpy(rwnx_sta->mac_addr, ind->peer_addr.array, ETH_ALEN);
+
+ rwnx_chanctx_link(rwnx_vif, rwnx_sta->ch_idx, NULL);
+
+ /* Add the station in the list of VIF's stations */
+ INIT_LIST_HEAD(&rwnx_sta->list);
+ list_add_tail(&rwnx_sta->list, &rwnx_vif->ap.sta_list);
+
+ /* Initialize the TX queues */
+ if (rwnx_sta->ch_idx == rwnx_hw->cur_chanctx) {
+ txq_status = 0;
+ } else {
+ txq_status = RWNX_TXQ_STOP_CHAN;
+ }
+
+ rwnx_txq_sta_init(rwnx_hw, rwnx_sta, txq_status);
+#ifdef CONFIG_DEBUG_FS
+ rwnx_dbgfs_register_rc_stat(rwnx_hw, rwnx_sta);
+#endif
+#ifdef CONFIG_RWNX_BFMER
+ // TODO: update indication to contains vht capabilties
+ if (rwnx_hw->mod_params->bfmer)
+ rwnx_send_bfmer_enable(rwnx_hw, rwnx_sta, NULL);
+
+ rwnx_mu_group_sta_init(rwnx_sta, NULL);
+#endif /* CONFIG_RWNX_BFMER */
+
+ } else {
+ WARN_ON(0);
+ }
+ } else {
+ if (rwnx_sta->valid) {
+ rwnx_sta->ps.active = false;
+ rwnx_sta->valid = false;
+
+ /* Remove the station from the list of VIF's station */
+ list_del_init(&rwnx_sta->list);
+
+ rwnx_txq_sta_deinit(rwnx_hw, rwnx_sta);
+#ifdef CONFIG_DEBUG_FS
+ rwnx_dbgfs_unregister_rc_stat(rwnx_hw, rwnx_sta);
+#endif
+ } else {
+ WARN_ON(0);
+ }
+ }
+ } else {
+ if (!ind->estab && rwnx_sta->valid) {
+ /* There is no way to inform upper layer for lost of peer, still
+ clean everything in the driver */
+ rwnx_sta->ps.active = false;
+ rwnx_sta->valid = false;
+
+ /* Remove the station from the list of VIF's station */
+ list_del_init(&rwnx_sta->list);
+
+ rwnx_txq_sta_deinit(rwnx_hw, rwnx_sta);
+#ifdef CONFIG_DEBUG_FS
+ rwnx_dbgfs_unregister_rc_stat(rwnx_hw, rwnx_sta);
+#endif
+ } else {
+ WARN_ON(0);
+ }
+ }
+
+ return 0;
+}
+
+static inline int rwnx_rx_mesh_path_update_ind(struct rwnx_hw *rwnx_hw,
+ struct rwnx_cmd *cmd,
+ struct ipc_e2a_msg *msg)
+{
+ struct mesh_path_update_ind *ind = (struct mesh_path_update_ind *)msg->param;
+ struct rwnx_vif *rwnx_vif = rwnx_hw->vif_table[ind->vif_idx];
+ struct rwnx_mesh_path *mesh_path;
+ bool found = false;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ if (ind->vif_idx >= (NX_VIRT_DEV_MAX + NX_REMOTE_STA_MAX))
+ return 1;
+
+ if (!rwnx_vif || (RWNX_VIF_TYPE(rwnx_vif) != NL80211_IFTYPE_MESH_POINT))
+ return 0;
+
+ /* Look for path with provided target address */
+ list_for_each_entry(mesh_path, &rwnx_vif->ap.mpath_list, list) {
+ if (mesh_path->path_idx == ind->path_idx) {
+ found = true;
+ break;
+ }
+ }
+
+ /* Check if element has been deleted */
+ if (ind->delete) {
+ if (found) {
+#ifdef CREATE_TRACE_POINTS
+ trace_mesh_delete_path(mesh_path);
+#endif
+ /* Remove element from list */
+ list_del_init(&mesh_path->list);
+ /* Free the element */
+ kfree(mesh_path);
+ }
+ }
+ else {
+ if (found) {
+ // Update the Next Hop STA
+ mesh_path->p_nhop_sta = &rwnx_hw->sta_table[ind->nhop_sta_idx];
+#ifdef CREATE_TRACE_POINTS
+ trace_mesh_update_path(mesh_path);
+#endif
+ } else {
+ // Allocate a Mesh Path structure
+ mesh_path = (struct rwnx_mesh_path *)kmalloc(sizeof(struct rwnx_mesh_path), GFP_ATOMIC);
+
+ if (mesh_path) {
+ INIT_LIST_HEAD(&mesh_path->list);
+
+ mesh_path->path_idx = ind->path_idx;
+ mesh_path->p_nhop_sta = &rwnx_hw->sta_table[ind->nhop_sta_idx];
+ memcpy(&mesh_path->tgt_mac_addr, &ind->tgt_mac_addr, MAC_ADDR_LEN);
+
+ // Insert the path in the list of path
+ list_add_tail(&mesh_path->list, &rwnx_vif->ap.mpath_list);
+#ifdef CREATE_TRACE_POINTS
+ trace_mesh_create_path(mesh_path);
+#endif
+ }
+ }
+ }
+
+ return 0;
+}
+
+static inline int rwnx_rx_mesh_proxy_update_ind(struct rwnx_hw *rwnx_hw,
+ struct rwnx_cmd *cmd,
+ struct ipc_e2a_msg *msg)
+{
+ struct mesh_proxy_update_ind *ind = (struct mesh_proxy_update_ind *)msg->param;
+ struct rwnx_vif *rwnx_vif = rwnx_hw->vif_table[ind->vif_idx];
+ struct rwnx_mesh_proxy *mesh_proxy;
+ bool found = false;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ if (ind->vif_idx >= (NX_VIRT_DEV_MAX + NX_REMOTE_STA_MAX))
+ return 1;
+
+ if (!rwnx_vif || (RWNX_VIF_TYPE(rwnx_vif) != NL80211_IFTYPE_MESH_POINT))
+ return 0;
+
+ /* Look for path with provided external STA address */
+ list_for_each_entry(mesh_proxy, &rwnx_vif->ap.proxy_list, list) {
+ if (!memcmp(&ind->ext_sta_addr, &mesh_proxy->ext_sta_addr, ETH_ALEN)) {
+ found = true;
+ break;
+ }
+ }
+
+ if (ind->delete && found) {
+ /* Delete mesh path */
+ list_del_init(&mesh_proxy->list);
+ kfree(mesh_proxy);
+ } else if (!ind->delete && !found) {
+ /* Allocate a Mesh Path structure */
+ mesh_proxy = (struct rwnx_mesh_proxy *)kmalloc(sizeof(*mesh_proxy),
+ GFP_ATOMIC);
+
+ if (mesh_proxy) {
+ INIT_LIST_HEAD(&mesh_proxy->list);
+
+ memcpy(&mesh_proxy->ext_sta_addr, &ind->ext_sta_addr, MAC_ADDR_LEN);
+ mesh_proxy->local = ind->local;
+
+ if (!ind->local) {
+ memcpy(&mesh_proxy->proxy_addr, &ind->proxy_mac_addr, MAC_ADDR_LEN);
+ }
+
+ /* Insert the path in the list of path */
+ list_add_tail(&mesh_proxy->list, &rwnx_vif->ap.proxy_list);
+ }
+ }
+
+ return 0;
+}
+#endif /* CONFIG_RWNX_FULLMAC */
+
+/***************************************************************************
+ * Messages from APM task
+ **************************************************************************/
+
+
+/***************************************************************************
+ * Messages from DEBUG task
+ **************************************************************************/
+static inline int rwnx_rx_dbg_error_ind(struct rwnx_hw *rwnx_hw,
+ struct rwnx_cmd *cmd,
+ struct ipc_e2a_msg *msg)
+{
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ return 0;
+}
+
+#ifdef CONFIG_RWNX_FULLMAC
+
+static msg_cb_fct mm_hdlrs[MSG_I(MM_MAX)] = {
+ [MSG_I(MM_CHANNEL_SWITCH_IND)] = rwnx_rx_chan_switch_ind,
+ [MSG_I(MM_CHANNEL_PRE_SWITCH_IND)] = rwnx_rx_chan_pre_switch_ind,
+ [MSG_I(MM_REMAIN_ON_CHANNEL_EXP_IND)] = rwnx_rx_remain_on_channel_exp_ind,
+ [MSG_I(MM_PS_CHANGE_IND)] = rwnx_rx_ps_change_ind,
+ [MSG_I(MM_TRAFFIC_REQ_IND)] = rwnx_rx_traffic_req_ind,
+ [MSG_I(MM_P2P_VIF_PS_CHANGE_IND)] = rwnx_rx_p2p_vif_ps_change_ind,
+ [MSG_I(MM_CSA_COUNTER_IND)] = rwnx_rx_csa_counter_ind,
+ [MSG_I(MM_CSA_FINISH_IND)] = rwnx_rx_csa_finish_ind,
+ [MSG_I(MM_CSA_TRAFFIC_IND)] = rwnx_rx_csa_traffic_ind,
+ [MSG_I(MM_CHANNEL_SURVEY_IND)] = rwnx_rx_channel_survey_ind,
+ [MSG_I(MM_P2P_NOA_UPD_IND)] = rwnx_rx_p2p_noa_upd_ind,
+ [MSG_I(MM_RSSI_STATUS_IND)] = rwnx_rx_rssi_status_ind,
+ [MSG_I(MM_PKTLOSS_IND)] = rwnx_rx_pktloss_notify_ind,
+ [MSG_I(MM_APM_STALOSS_IND)] = rwnx_apm_staloss_ind,
+};
+
+static msg_cb_fct scan_hdlrs[MSG_I(SCANU_MAX)] = {
+ [MSG_I(SCANU_START_CFM)] = rwnx_rx_scanu_start_cfm,
+ [MSG_I(SCANU_RESULT_IND)] = rwnx_rx_scanu_result_ind,
+};
+
+static msg_cb_fct me_hdlrs[MSG_I(ME_MAX)] = {
+ [MSG_I(ME_TKIP_MIC_FAILURE_IND)] = rwnx_rx_me_tkip_mic_failure_ind,
+ [MSG_I(ME_TX_CREDITS_UPDATE_IND)] = rwnx_rx_me_tx_credits_update_ind,
+};
+
+static msg_cb_fct sm_hdlrs[MSG_I(SM_MAX)] = {
+ [MSG_I(SM_CONNECT_IND)] = rwnx_rx_sm_connect_ind,
+ [MSG_I(SM_DISCONNECT_IND)] = rwnx_rx_sm_disconnect_ind,
+ [MSG_I(SM_EXTERNAL_AUTH_REQUIRED_IND)] = rwnx_rx_sm_external_auth_required_ind,
+};
+
+static msg_cb_fct apm_hdlrs[MSG_I(APM_MAX)] = {
+};
+
+static msg_cb_fct mesh_hdlrs[MSG_I(MESH_MAX)] = {
+ [MSG_I(MESH_PATH_CREATE_CFM)] = rwnx_rx_mesh_path_create_cfm,
+ [MSG_I(MESH_PEER_UPDATE_IND)] = rwnx_rx_mesh_peer_update_ind,
+ [MSG_I(MESH_PATH_UPDATE_IND)] = rwnx_rx_mesh_path_update_ind,
+ [MSG_I(MESH_PROXY_UPDATE_IND)] = rwnx_rx_mesh_proxy_update_ind,
+};
+
+#endif /* CONFIG_RWNX_FULLMAC */
+
+static msg_cb_fct dbg_hdlrs[MSG_I(DBG_MAX)] = {
+ [MSG_I(DBG_ERROR_IND)] = rwnx_rx_dbg_error_ind,
+};
+
+static msg_cb_fct tdls_hdlrs[MSG_I(TDLS_MAX)] = {
+ [MSG_I(TDLS_CHAN_SWITCH_CFM)] = rwnx_rx_tdls_chan_switch_cfm,
+ [MSG_I(TDLS_CHAN_SWITCH_IND)] = rwnx_rx_tdls_chan_switch_ind,
+ [MSG_I(TDLS_CHAN_SWITCH_BASE_IND)] = rwnx_rx_tdls_chan_switch_base_ind,
+ [MSG_I(TDLS_PEER_PS_IND)] = rwnx_rx_tdls_peer_ps_ind,
+};
+
+static msg_cb_fct *msg_hdlrs[] = {
+ [TASK_MM] = mm_hdlrs,
+ [TASK_DBG] = dbg_hdlrs,
+#ifdef CONFIG_RWNX_FULLMAC
+ [TASK_TDLS] = tdls_hdlrs,
+ [TASK_SCANU] = scan_hdlrs,
+ [TASK_ME] = me_hdlrs,
+ [TASK_SM] = sm_hdlrs,
+ [TASK_APM] = apm_hdlrs,
+ [TASK_MESH] = mesh_hdlrs,
+#endif /* CONFIG_RWNX_FULLMAC */
+};
+
+/**
+ *
+ */
+void rwnx_rx_handle_msg(struct rwnx_hw *rwnx_hw, struct ipc_e2a_msg *msg)
+{
+ //printk("%s(%d) MSG_T(msg->id):%d MSG_I(msg->id):%d cmd:%s\r\n", __func__,
+ // msg->id,
+ // MSG_T(msg->id),
+ // MSG_I(msg->id),
+ // rwnx_id2str[MSG_T(msg->id)][MSG_I(msg->id)]);
+
+ rwnx_hw->cmd_mgr->msgind(rwnx_hw->cmd_mgr, msg,
+ msg_hdlrs[MSG_T(msg->id)][MSG_I(msg->id)]);
+}
+
+void rwnx_rx_handle_print(struct rwnx_hw *rwnx_hw, u8 *msg, u32 len)
+{
+ u8 *data_end = NULL;
+ (void)data_end;
+
+ if (!rwnx_hw || !rwnx_hw->fwlog_en) {
+ pr_err("FWLOG-OVFL: %s", msg);
+ return;
+ }
+
+ printk("FWLOG: %s", msg);
+
+#ifdef CONFIG_RWNX_DEBUGFS
+ data_end = rwnx_hw->debugfs.fw_log.buf.dataend;
+
+ if (!rwnx_hw->debugfs.fw_log.buf.data)
+ return ;
+
+ //printk("end=%lx, len=%d\n", (unsigned long)rwnx_hw->debugfs.fw_log.buf.end, len);
+
+ spin_lock_bh(&rwnx_hw->debugfs.fw_log.lock);
+
+ if (rwnx_hw->debugfs.fw_log.buf.end + len > data_end) {
+ int rem = data_end - rwnx_hw->debugfs.fw_log.buf.end;
+ memcpy(rwnx_hw->debugfs.fw_log.buf.end, msg, rem);
+ memcpy(rwnx_hw->debugfs.fw_log.buf.data, &msg[rem], len - rem);
+ rwnx_hw->debugfs.fw_log.buf.end = rwnx_hw->debugfs.fw_log.buf.data + (len - rem);
+ } else {
+ memcpy(rwnx_hw->debugfs.fw_log.buf.end, msg, len);
+ rwnx_hw->debugfs.fw_log.buf.end += len;
+ }
+
+ rwnx_hw->debugfs.fw_log.buf.size += len;
+ if (rwnx_hw->debugfs.fw_log.buf.size > FW_LOG_SIZE)
+ rwnx_hw->debugfs.fw_log.buf.size = FW_LOG_SIZE;
+
+ spin_unlock_bh(&rwnx_hw->debugfs.fw_log.lock);
+#endif
+}
+
diff --git a/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_msg_rx.h b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_msg_rx.h
new file mode 100644
index 000000000000..b5556d160ff0
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_msg_rx.h
@@ -0,0 +1,19 @@
+/**
+ ****************************************************************************************
+ *
+ * @file rwnx_msg_rx.h
+ *
+ * @brief RX function declarations
+ *
+ * Copyright (C) RivieraWaves 2012-2019
+ *
+ ****************************************************************************************
+ */
+
+#ifndef _RWNX_MSG_RX_H_
+#define _RWNX_MSG_RX_H_
+
+void rwnx_rx_handle_msg(struct rwnx_hw *rwnx_hw, struct ipc_e2a_msg *msg);
+void rwnx_rx_handle_print(struct rwnx_hw *rwnx_hw, u8 *msg, u32 len);
+
+#endif /* _RWNX_MSG_RX_H_ */
diff --git a/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_msg_tx.c b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_msg_tx.c
new file mode 100644
index 000000000000..1573ec593411
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_msg_tx.c
@@ -0,0 +1,3753 @@
+/**
+ ******************************************************************************
+ *
+ * @file rwnx_msg_tx.c
+ *
+ * @brief TX function definitions
+ *
+ * Copyright (C) RivieraWaves 2012-2019
+ *
+ ******************************************************************************
+ */
+
+#include "rwnx_msg_tx.h"
+#include "rwnx_mod_params.h"
+#include "reg_access.h"
+#ifdef CONFIG_RWNX_BFMER
+#include "rwnx_bfmer.h"
+#endif //(CONFIG_RWNX_BFMER)
+#include "rwnx_compat.h"
+#include "rwnx_cmds.h"
+#include "rwnx_main.h"
+#include "aicwf_txrxif.h"
+#include "rwnx_strs.h"
+
+
+const struct mac_addr mac_addr_bcst = {{0xFFFF, 0xFFFF, 0xFFFF}};
+
+/* Default MAC Rx filters that can be changed by mac80211
+ * (via the configure_filter() callback) */
+#define RWNX_MAC80211_CHANGEABLE ( \
+ NXMAC_ACCEPT_BA_BIT | \
+ NXMAC_ACCEPT_BAR_BIT | \
+ NXMAC_ACCEPT_OTHER_DATA_FRAMES_BIT | \
+ NXMAC_ACCEPT_PROBE_REQ_BIT | \
+ NXMAC_ACCEPT_PS_POLL_BIT \
+ )
+
+/* Default MAC Rx filters that cannot be changed by mac80211 */
+#define RWNX_MAC80211_NOT_CHANGEABLE ( \
+ NXMAC_ACCEPT_QO_S_NULL_BIT | \
+ NXMAC_ACCEPT_Q_DATA_BIT | \
+ NXMAC_ACCEPT_DATA_BIT | \
+ NXMAC_ACCEPT_OTHER_MGMT_FRAMES_BIT | \
+ NXMAC_ACCEPT_MY_UNICAST_BIT | \
+ NXMAC_ACCEPT_BROADCAST_BIT | \
+ NXMAC_ACCEPT_BEACON_BIT | \
+ NXMAC_ACCEPT_PROBE_RESP_BIT \
+ )
+
+/* Default MAC Rx filter */
+#define RWNX_DEFAULT_RX_FILTER (RWNX_MAC80211_CHANGEABLE | RWNX_MAC80211_NOT_CHANGEABLE)
+
+const int bw2chnl[] = {
+ [NL80211_CHAN_WIDTH_20_NOHT] = PHY_CHNL_BW_20,
+ [NL80211_CHAN_WIDTH_20] = PHY_CHNL_BW_20,
+ [NL80211_CHAN_WIDTH_40] = PHY_CHNL_BW_40,
+ [NL80211_CHAN_WIDTH_80] = PHY_CHNL_BW_80,
+ [NL80211_CHAN_WIDTH_160] = PHY_CHNL_BW_160,
+ [NL80211_CHAN_WIDTH_80P80] = PHY_CHNL_BW_80P80,
+};
+
+const int chnl2bw[] = {
+ [PHY_CHNL_BW_20] = NL80211_CHAN_WIDTH_20,
+ [PHY_CHNL_BW_40] = NL80211_CHAN_WIDTH_40,
+ [PHY_CHNL_BW_80] = NL80211_CHAN_WIDTH_80,
+ [PHY_CHNL_BW_160] = NL80211_CHAN_WIDTH_160,
+ [PHY_CHNL_BW_80P80] = NL80211_CHAN_WIDTH_80P80,
+};
+
+#define RWNX_CMD_ARRAY_SIZE 20
+#define RWNX_CMD_HIGH_WATER_SIZE RWNX_CMD_ARRAY_SIZE/2
+//#define RWNX_MSG_ARRAY_SIZE 20
+
+struct rwnx_cmd cmd_array[RWNX_CMD_ARRAY_SIZE];
+//struct lmac_msg msg_array[RWNX_MSG_ARRAY_SIZE];
+
+static spinlock_t cmd_array_lock;
+//static spinlock_t msg_array_lock;
+
+//int msg_array_index = 0;
+int cmd_array_index = 0;
+
+
+
+/*****************************************************************************/
+/*
+ * Parse the ampdu density to retrieve the value in usec, according to the
+ * values defined in ieee80211.h
+ */
+static inline u8 rwnx_ampdudensity2usec(u8 ampdudensity)
+{
+ switch (ampdudensity) {
+ case IEEE80211_HT_MPDU_DENSITY_NONE:
+ return 0;
+ /* 1 microsecond is our granularity */
+ case IEEE80211_HT_MPDU_DENSITY_0_25:
+ case IEEE80211_HT_MPDU_DENSITY_0_5:
+ case IEEE80211_HT_MPDU_DENSITY_1:
+ return 1;
+ case IEEE80211_HT_MPDU_DENSITY_2:
+ return 2;
+ case IEEE80211_HT_MPDU_DENSITY_4:
+ return 4;
+ case IEEE80211_HT_MPDU_DENSITY_8:
+ return 8;
+ case IEEE80211_HT_MPDU_DENSITY_16:
+ return 16;
+ default:
+ return 0;
+ }
+}
+
+static inline bool use_pairwise_key(struct cfg80211_crypto_settings *crypto)
+{
+ if ((crypto->cipher_group == WLAN_CIPHER_SUITE_WEP40) ||
+ (crypto->cipher_group == WLAN_CIPHER_SUITE_WEP104))
+ return false;
+
+ return true;
+}
+
+static inline bool is_non_blocking_msg(int id)
+{
+ return ((id == MM_TIM_UPDATE_REQ) || (id == ME_RC_SET_RATE_REQ) ||
+ (id == MM_BFMER_ENABLE_REQ) || (id == ME_TRAFFIC_IND_REQ) ||
+ (id == TDLS_PEER_TRAFFIC_IND_REQ) ||
+ (id == MESH_PATH_CREATE_REQ) || (id == MESH_PROXY_ADD_REQ) ||
+ (id == SM_EXTERNAL_AUTH_REQUIRED_RSP));
+}
+
+static inline u8_l get_chan_flags(uint32_t flags)
+{
+ u8_l chan_flags = 0;
+#ifdef RADAR_OR_IR_DETECT
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 14, 0)
+ if (flags & IEEE80211_CHAN_PASSIVE_SCAN)
+ #else
+ if (flags & IEEE80211_CHAN_NO_IR)
+ #endif
+ chan_flags |= CHAN_NO_IR;
+ if (flags & IEEE80211_CHAN_RADAR)
+ chan_flags |= CHAN_RADAR;
+#endif
+ return chan_flags;
+}
+
+static inline s8_l chan_to_fw_pwr(int power)
+{
+ return power>127?127:(s8_l)power;
+}
+
+static inline void limit_chan_bw(u8_l *bw, u16_l primary, u16_l *center1)
+{
+ int oft, new_oft = 10;
+
+ if (*bw <= PHY_CHNL_BW_40)
+ return;
+
+ oft = *center1 - primary;
+ *bw = PHY_CHNL_BW_40;
+
+ if (oft < 0)
+ new_oft = new_oft * -1;
+ if (abs(oft) == 10 || abs(oft) == 50)
+ new_oft = new_oft * -1;
+
+ *center1 = primary + new_oft;
+}
+
+struct rwnx_cmd *rwnx_cmd_malloc(void){
+ struct rwnx_cmd *cmd = NULL;
+ unsigned long flags = 0;
+
+ spin_lock_irqsave(&cmd_array_lock, flags);
+
+ for(cmd_array_index = 0; cmd_array_index < RWNX_CMD_ARRAY_SIZE; cmd_array_index++){
+ if(cmd_array[cmd_array_index].used == 0){
+ AICWFDBG(LOGTRACE, "%s get cmd_array[%d]:%p \r\n", __func__, cmd_array_index,&cmd_array[cmd_array_index]);
+ cmd = &cmd_array[cmd_array_index];
+ cmd_array[cmd_array_index].used = 1;
+ break;
+ }
+ }
+
+ if(cmd_array_index >= RWNX_CMD_HIGH_WATER_SIZE){
+ AICWFDBG(LOGERROR, "%s cmd(%d) was pending...\r\n", __func__, cmd_array_index);
+ mdelay(100);
+ }
+
+ if(!cmd){
+ AICWFDBG(LOGERROR, "%s array is empty...\r\n", __func__);
+ }
+
+ spin_unlock_irqrestore(&cmd_array_lock, flags);
+
+ return cmd;
+}
+
+void rwnx_cmd_free(struct rwnx_cmd *cmd){
+ unsigned long flags = 0;
+
+ spin_lock_irqsave(&cmd_array_lock, flags);
+ cmd->used = 0;
+ AICWFDBG(LOGTRACE, "%s cmd_array[%d]:%p \r\n", __func__, cmd->array_id, cmd);
+ spin_unlock_irqrestore(&cmd_array_lock, flags);
+}
+
+
+int rwnx_init_cmd_array(void){
+
+ AICWFDBG(LOGTRACE, "%s Enter \r\n", __func__);
+ spin_lock_init(&cmd_array_lock);
+
+ for(cmd_array_index = 0; cmd_array_index < RWNX_CMD_ARRAY_SIZE; cmd_array_index++){
+ AICWFDBG(LOGTRACE, "%s cmd_queue[%d]:%p \r\n", __func__, cmd_array_index, &cmd_array[cmd_array_index]);
+ cmd_array[cmd_array_index].used = 0;
+ cmd_array[cmd_array_index].array_id = cmd_array_index;
+ }
+ AICWFDBG(LOGTRACE, "%s Exit \r\n", __func__);
+
+ return 0;
+}
+
+void rwnx_free_cmd_array(void){
+
+ AICWFDBG(LOGTRACE, "%s Enter \r\n", __func__);
+
+ for(cmd_array_index = 0; cmd_array_index < RWNX_CMD_ARRAY_SIZE; cmd_array_index++){
+ cmd_array[cmd_array_index].used = 0;
+ }
+
+ AICWFDBG(LOGTRACE, "%s Exit \r\n", __func__);
+}
+
+
+#if 0
+int rwnx_init_msg_array(void){
+
+ printk("%s enter\r\n", __func__);
+ spin_lock_init(&msg_array_lock);
+
+ for(msg_array_index = 0; msg_array_index < RWNX_MSG_ARRAY_SIZE; msg_array_index++){
+ printk("%s msg_queue[%d]:%p \r\n", __func__, msg_array_index, &msg_array[msg_array_index]);
+ }
+ printk("%s exit\r\n", __func__);
+
+ return 0;
+
+}
+
+
+void rwnx_free_msg_array(void){
+
+ printk("%s enter\r\n", __func__);
+#if 1
+ for(msg_array_index = 0; msg_array_index < RWNX_MSG_ARRAY_SIZE; msg_array_index++){
+ msg_array[msg_array_index].param_len = 0;
+ }
+#endif
+ printk("%s exit\r\n", __func__);
+}
+
+struct lmac_msg *rwnx_msg_malloc_(void){
+ struct lmac_msg *msg = NULL;
+
+
+ spin_lock(&msg_array_lock);
+ printk("%s enter\r\n", __func__);
+ for(msg_array_index = 0; msg_array_index < RWNX_MSG_ARRAY_SIZE; msg_array_index++){
+ if(msg_array[msg_array_index].param_len== 0){
+ printk("%s get msg_array[%d]:%p \r\n", __func__, msg_array_index, &msg_array[msg_array_index]);
+ msg = &msg_array[msg_array_index];
+ break;
+ }else{
+ printk("%s msg_array[%d] in used param_len= %d \r\n",
+ __func__,
+ msg_array_index,
+ msg_array[msg_array_index].param_len);
+ }
+ }
+
+ if(!msg){
+ printk("%s array is empty...\r\n", __func__);
+ }
+ spin_unlock(&msg_array_lock);
+
+ return msg;
+}
+
+void rwnx_msg_free_(struct lmac_msg *msg){
+
+ spin_lock(&msg_array_lock);
+ printk("%s enter \r\n", __func__);
+
+ for(msg_array_index = 0; msg_array_index < RWNX_MSG_ARRAY_SIZE; msg_array_index++){
+ if(msg == &msg_array[msg_array_index]){
+ break;
+ }
+ }
+
+ memset(msg->param, 0, msg->param_len);
+ msg->id = 0;
+ msg->dest_id = 0;
+ msg->src_id = 0;
+ msg->param_len = 0;
+
+ printk("%s msg_array[%d]:%p \r\n", __func__, msg_array_index, msg);
+ spin_unlock(&msg_array_lock);
+}
+#endif
+
+
+/**
+ ******************************************************************************
+ * @brief Allocate memory for a message
+ *
+ * This primitive allocates memory for a message that has to be sent. The memory
+ * is allocated dynamically on the heap and the length of the variable parameter
+ * structure has to be provided in order to allocate the correct size.
+ *
+ * Several additional parameters are provided which will be preset in the message
+ * and which may be used internally to choose the kind of memory to allocate.
+ *
+ * The memory allocated will be automatically freed by the kernel, after the
+ * pointer has been sent to ke_msg_send(). If the message is not sent, it must
+ * be freed explicitly with ke_msg_free().
+ *
+ * Allocation failure is considered critical and should not happen.
+ *
+ * @param[in] id Message identifier
+ * @param[in] dest_id Destination Task Identifier
+ * @param[in] src_id Source Task Identifier
+ * @param[in] param_len Size of the message parameters to be allocated
+ *
+ * @return Pointer to the parameter member of the ke_msg. If the parameter
+ * structure is empty, the pointer will point to the end of the message
+ * and should not be used (except to retrieve the message pointer or to
+ * send the message)
+ ******************************************************************************
+ */
+static inline void *rwnx_msg_zalloc(lmac_msg_id_t const id,
+ lmac_task_id_t const dest_id,
+ lmac_task_id_t const src_id,
+ uint16_t const param_len)
+{
+ struct lmac_msg *msg;
+ gfp_t flags;
+
+ //if (is_non_blocking_msg(id) && in_softirq())
+ flags = GFP_ATOMIC;
+ //else
+ // flags = GFP_KERNEL;
+
+ msg = (struct lmac_msg *)kzalloc(sizeof(struct lmac_msg) + param_len,
+ flags);
+ if (msg == NULL) {
+ printk(KERN_CRIT "%s: msg allocation failed\n", __func__);
+ return NULL;
+ }
+ msg->id = id;
+ msg->dest_id = dest_id;
+ msg->src_id = src_id;
+ msg->param_len = param_len;
+ //printk("rwnx_msg_zalloc size=%d id=%d\n",msg->param_len,msg->id);
+
+ return msg->param;
+}
+
+static void rwnx_msg_free(struct rwnx_hw *rwnx_hw, const void *msg_params)
+{
+ struct lmac_msg *msg = container_of((void *)msg_params,
+ struct lmac_msg, param);
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ /* Free the message */
+ kfree(msg);
+}
+
+
+
+static int rwnx_send_msg(struct rwnx_hw *rwnx_hw, const void *msg_params,
+ int reqcfm, lmac_msg_id_t reqid, void *cfm)
+{
+ struct lmac_msg *msg;
+ struct rwnx_cmd *cmd;
+ bool nonblock;
+ int ret = 0;
+ u8_l empty = 0;
+
+ //RWNX_DBG(RWNX_FN_ENTRY_STR);
+ AICWFDBG(LOGDEBUG, "%s (%d)%s reqcfm:%d in_softirq:%d in_atomic:%d\r\n",
+ __func__, reqid, RWNX_ID2STR(reqid), reqcfm, (int)in_softirq(), (int)in_atomic());
+
+#ifdef AICWF_USB_SUPPORT
+ if (rwnx_hw->usbdev->state == USB_DOWN_ST) {
+ rwnx_msg_free(rwnx_hw, msg_params);
+ AICWFDBG(LOGERROR, "%s bus is down\n", __func__);
+ return 0;
+ }
+#endif
+#ifdef AICWF_SDIO_SUPPORT
+ if(rwnx_hw->sdiodev->bus_if->state == BUS_DOWN_ST) {
+ rwnx_msg_free(rwnx_hw, msg_params);
+ sdio_err("bus is down\n");
+ return 0;
+ }
+#endif
+
+ msg = container_of((void *)msg_params, struct lmac_msg, param);
+
+ #if 0
+ if (!test_bit(RWNX_DEV_STARTED, &rwnx_hw->drv_flags) &&
+ reqid != DBG_MEM_READ_CFM && reqid != DBG_MEM_WRITE_CFM &&
+ reqid != DBG_MEM_BLOCK_WRITE_CFM && reqid != DBG_START_APP_CFM &&
+ reqid != MM_SET_RF_CALIB_CFM && reqid != MM_SET_RF_CONFIG_CFM &&
+ reqid != MM_RESET_CFM && reqid != MM_VERSION_CFM &&
+ reqid != MM_START_CFM && reqid != MM_SET_IDLE_CFM &&
+ reqid != ME_CONFIG_CFM && reqid != MM_SET_PS_MODE_CFM &&
+ reqid != ME_CHAN_CONFIG_CFM) {
+ printk(KERN_CRIT "%s: bypassing (RWNX_DEV_RESTARTING set) 0x%02x\n",
+ __func__, reqid);
+ kfree(msg);
+ return -EBUSY;
+ }
+ #endif
+#if 0
+ else if (!rwnx_hw->ipc_env) {
+ printk(KERN_CRIT "%s: bypassing (restart must have failed)\n", __func__);
+ kfree(msg);
+ return -EBUSY;
+ }
+#endif
+
+ //nonblock = is_non_blocking_msg(msg->id);
+ nonblock = 0;//AIDEN
+ cmd = rwnx_cmd_malloc();//kzalloc(sizeof(struct rwnx_cmd), nonblock ? GFP_ATOMIC : GFP_KERNEL);
+ cmd->result = -EINTR;
+ cmd->id = msg->id;
+ cmd->reqid = reqid;
+ cmd->a2e_msg = msg;
+ cmd->e2a_msg = cfm;
+ if (nonblock)
+ cmd->flags = RWNX_CMD_FLAG_NONBLOCK;
+ if (reqcfm)
+ cmd->flags |= RWNX_CMD_FLAG_REQ_CFM;
+
+ if(cfm != NULL) {
+ do {
+ if(rwnx_hw->cmd_mgr->state == RWNX_CMD_MGR_STATE_CRASHED)
+ break;
+ spin_lock_bh(&rwnx_hw->cmd_mgr->lock);
+ empty = list_empty(&rwnx_hw->cmd_mgr->cmds);
+ spin_unlock_bh(&rwnx_hw->cmd_mgr->lock);
+ if(!empty) {
+ if(in_softirq()) {
+ #ifdef CONFIG_RWNX_DBG
+ AICWFDBG(LOGDEBUG, "in_softirq:check cmdqueue empty\n");
+ #endif
+ mdelay(10);
+ }
+ else {
+ #ifdef CONFIG_RWNX_DBG
+ AICWFDBG(LOGDEBUG, "check cmdqueue empty\n");
+ #endif
+ msleep(50);
+ }
+ }
+ } while(!empty);//wait for cmd queue empty
+ }
+
+ if(reqcfm) {
+ cmd->flags &= ~RWNX_CMD_FLAG_WAIT_ACK; // we don't need ack any more
+ ret = rwnx_hw->cmd_mgr->queue(rwnx_hw->cmd_mgr, cmd);
+ } else {
+#ifdef AICWF_SDIO_SUPPORT
+ aicwf_set_cmd_tx((void *)(rwnx_hw->sdiodev), cmd->a2e_msg, sizeof(struct lmac_msg) + cmd->a2e_msg->param_len);
+#else
+ aicwf_set_cmd_tx((void *)(rwnx_hw->usbdev), cmd->a2e_msg, sizeof(struct lmac_msg) + cmd->a2e_msg->param_len);
+#endif
+ }
+
+ if(!reqcfm || ret)
+ rwnx_cmd_free(cmd);//kfree(cmd);
+
+ return ret;//0;
+}
+
+
+static int rwnx_send_msg1(struct rwnx_hw *rwnx_hw, const void *msg_params,
+ int reqcfm, lmac_msg_id_t reqid, void *cfm, bool defer)
+{
+ struct lmac_msg *msg;
+ struct rwnx_cmd *cmd;
+ bool nonblock;
+ int ret = 0;
+
+ //RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ AICWFDBG(LOGDEBUG,"%s (%d)%s reqcfm:%d in_softirq:%d in_atomic:%d\r\n",
+ __func__, reqid, RWNX_ID2STR(reqid), reqcfm, (int)in_softirq(), (int)in_atomic());
+
+ if (rwnx_hw->usbdev->state == USB_DOWN_ST) {
+ rwnx_msg_free(rwnx_hw, msg_params);
+ AICWFDBG(LOGERROR, "%s bus is down\n", __func__);
+ return 0;
+ }
+
+
+ msg = container_of((void *)msg_params, struct lmac_msg, param);
+
+ //nonblock = is_non_blocking_msg(msg->id);
+ nonblock = 0;
+ cmd = rwnx_cmd_malloc();//kzalloc(sizeof(struct rwnx_cmd), nonblock ? GFP_ATOMIC : GFP_KERNEL);
+ cmd->result = -EINTR;
+ cmd->id = msg->id;
+ cmd->reqid = reqid;
+ cmd->a2e_msg = msg;
+ cmd->e2a_msg = cfm;
+ if (nonblock)
+ cmd->flags = RWNX_CMD_FLAG_NONBLOCK;
+ if (reqcfm)
+ cmd->flags |= RWNX_CMD_FLAG_REQ_CFM;
+
+ if(reqcfm) {
+ cmd->flags &= ~RWNX_CMD_FLAG_WAIT_ACK; // we don't need ack any more
+ if(!defer)
+ ret = rwnx_hw->cmd_mgr->queue(rwnx_hw->cmd_mgr, cmd);
+ else
+ ret = cmd_mgr_queue_force_defer(rwnx_hw->cmd_mgr, cmd);
+ }
+
+ if (!reqcfm || ret) {
+ rwnx_cmd_free(cmd);//kfree(cmd);
+ }
+
+ if (!ret) {
+ ret = cmd->result;
+ }
+
+ //return ret;
+ return 0;
+}
+
+/******************************************************************************
+ * Control messages handling functions (FULLMAC)
+ *****************************************************************************/
+int rwnx_send_reset(struct rwnx_hw *rwnx_hw)
+{
+ void *void_param;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ /* RESET REQ has no parameter */
+ void_param = rwnx_msg_zalloc(MM_RESET_REQ, TASK_MM, DRV_TASK_ID, 0);
+ if (!void_param)
+ return -ENOMEM;
+
+ return rwnx_send_msg(rwnx_hw, void_param, 1, MM_RESET_CFM, NULL);
+}
+
+int rwnx_send_start(struct rwnx_hw *rwnx_hw)
+{
+ struct mm_start_req *start_req_param;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ /* Build the START REQ message */
+ start_req_param = rwnx_msg_zalloc(MM_START_REQ, TASK_MM, DRV_TASK_ID,
+ sizeof(struct mm_start_req));
+ if (!start_req_param)
+ return -ENOMEM;
+
+ /* Set parameters for the START message */
+ memcpy(&start_req_param->phy_cfg, &rwnx_hw->phy.cfg, sizeof(rwnx_hw->phy.cfg));
+ start_req_param->uapsd_timeout = (u32_l)rwnx_hw->mod_params->uapsd_timeout;
+ start_req_param->lp_clk_accuracy = (u16_l)rwnx_hw->mod_params->lp_clk_ppm;
+
+ /* Send the START REQ message to LMAC FW */
+ return rwnx_send_msg(rwnx_hw, start_req_param, 1, MM_START_CFM, NULL);
+}
+
+int rwnx_send_version_req(struct rwnx_hw *rwnx_hw, struct mm_version_cfm *cfm)
+{
+ void *void_param;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ /* VERSION REQ has no parameter */
+ void_param = rwnx_msg_zalloc(MM_VERSION_REQ, TASK_MM, DRV_TASK_ID, 0);
+ if (!void_param)
+ return -ENOMEM;
+
+ return rwnx_send_msg(rwnx_hw, void_param, 1, MM_VERSION_CFM, cfm);
+}
+
+int rwnx_send_add_if(struct rwnx_hw *rwnx_hw, const unsigned char *mac,
+ enum nl80211_iftype iftype, bool p2p, struct mm_add_if_cfm *cfm)
+{
+ struct mm_add_if_req *add_if_req_param;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ /* Build the ADD_IF_REQ message */
+ add_if_req_param = rwnx_msg_zalloc(MM_ADD_IF_REQ, TASK_MM, DRV_TASK_ID,
+ sizeof(struct mm_add_if_req));
+ if (!add_if_req_param)
+ return -ENOMEM;
+
+ /* Set parameters for the ADD_IF_REQ message */
+ memcpy(&(add_if_req_param->addr.array[0]), mac, ETH_ALEN);
+ switch (iftype) {
+ #ifdef CONFIG_RWNX_FULLMAC
+ //case NL80211_IFTYPE_P2P_DEVICE:
+ case NL80211_IFTYPE_P2P_CLIENT:
+ add_if_req_param->p2p = true;
+ // no break
+ #endif /* CONFIG_RWNX_FULLMAC */
+ case NL80211_IFTYPE_STATION:
+ add_if_req_param->type = MM_STA;
+ break;
+
+ case NL80211_IFTYPE_ADHOC:
+ add_if_req_param->type = MM_IBSS;
+ break;
+
+ #ifdef CONFIG_RWNX_FULLMAC
+ case NL80211_IFTYPE_P2P_GO:
+ add_if_req_param->p2p = true;
+ // no break
+ #endif /* CONFIG_RWNX_FULLMAC */
+ case NL80211_IFTYPE_AP:
+ add_if_req_param->type = MM_AP;
+ break;
+ case NL80211_IFTYPE_MESH_POINT:
+ add_if_req_param->type = MM_MESH_POINT;
+ break;
+ case NL80211_IFTYPE_AP_VLAN:
+ return -1;
+ case NL80211_IFTYPE_MONITOR:
+ add_if_req_param->type = MM_MONITOR;
+ break;
+ default:
+ add_if_req_param->type = MM_STA;
+ break;
+ }
+
+
+ /* Send the ADD_IF_REQ message to LMAC FW */
+ return rwnx_send_msg(rwnx_hw, add_if_req_param, 1, MM_ADD_IF_CFM, cfm);
+}
+
+int rwnx_send_remove_if(struct rwnx_hw *rwnx_hw, u8 vif_index, bool defer)
+{
+ struct mm_remove_if_req *remove_if_req;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ /* Build the MM_REMOVE_IF_REQ message */
+ remove_if_req = rwnx_msg_zalloc(MM_REMOVE_IF_REQ, TASK_MM, DRV_TASK_ID,
+ sizeof(struct mm_remove_if_req));
+ if (!remove_if_req)
+ return -ENOMEM;
+
+ /* Set parameters for the MM_REMOVE_IF_REQ message */
+ remove_if_req->inst_nbr = vif_index;
+
+ /* Send the MM_REMOVE_IF_REQ message to LMAC FW */
+ return rwnx_send_msg1(rwnx_hw, remove_if_req, 1, MM_REMOVE_IF_CFM, NULL, defer);
+}
+
+int rwnx_send_set_channel(struct rwnx_hw *rwnx_hw, int phy_idx,
+ struct mm_set_channel_cfm *cfm)
+{
+ struct mm_set_channel_req *req;
+ enum nl80211_chan_width width;
+ u16 center_freq, center_freq1, center_freq2;
+ s8 tx_power = 0;
+ u8 flags;
+ enum nl80211_band band;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ if (phy_idx >= rwnx_hw->phy.cnt)
+ return -ENOTSUPP;
+
+ req = rwnx_msg_zalloc(MM_SET_CHANNEL_REQ, TASK_MM, DRV_TASK_ID,
+ sizeof(struct mm_set_channel_req));
+ if (!req)
+ return -ENOMEM;
+
+ if (phy_idx == 0) {
+#ifdef CONFIG_RWNX_FULLMAC
+ /* On FULLMAC only setting channel of secondary chain */
+ wiphy_err(rwnx_hw->wiphy, "Trying to set channel of primary chain");
+ return 0;
+#endif /* CONFIG_RWNX_FULLMAC */
+ } else {
+ struct rwnx_sec_phy_chan *chan = &rwnx_hw->phy.sec_chan;
+
+ width = chnl2bw[chan->type];
+ band = chan->band;
+ center_freq = chan->prim20_freq;
+ center_freq1 = chan->center_freq1;
+ center_freq2 = chan->center_freq2;
+ flags = 0;
+ }
+
+ req->chan.band = band;
+ req->chan.type = bw2chnl[width];
+ req->chan.prim20_freq = center_freq;
+ req->chan.center1_freq = center_freq1;
+ req->chan.center2_freq = center_freq2;
+ req->chan.tx_power = tx_power;
+ req->chan.flags = flags;
+ req->index = phy_idx;
+
+ if (rwnx_hw->phy.limit_bw)
+ limit_chan_bw(&req->chan.type, req->chan.prim20_freq, &req->chan.center1_freq);
+
+ RWNX_DBG("mac80211: freq=%d(c1:%d - c2:%d)/width=%d - band=%d\n"
+ " hw(%d): prim20=%d(c1:%d - c2:%d)/ type=%d - band=%d\n",
+ center_freq, center_freq1, center_freq2, width, band,
+ phy_idx, req->chan.prim20_freq, req->chan.center1_freq,
+ req->chan.center2_freq, req->chan.type, req->chan.band);
+
+ /* Send the MM_SET_CHANNEL_REQ REQ message to LMAC FW */
+ return rwnx_send_msg(rwnx_hw, req, 1, MM_SET_CHANNEL_CFM, cfm);
+}
+
+int rwnx_send_key_add(struct rwnx_hw *rwnx_hw, u8 vif_idx, u8 sta_idx, bool pairwise,
+ u8 *key, u8 key_len, u8 key_idx, u8 cipher_suite,
+ struct mm_key_add_cfm *cfm)
+{
+ struct mm_key_add_req *key_add_req;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ /* Build the MM_KEY_ADD_REQ message */
+ key_add_req = rwnx_msg_zalloc(MM_KEY_ADD_REQ, TASK_MM, DRV_TASK_ID,
+ sizeof(struct mm_key_add_req));
+ if (!key_add_req)
+ return -ENOMEM;
+
+ /* Set parameters for the MM_KEY_ADD_REQ message */
+ if (sta_idx != 0xFF) {
+ /* Pairwise key */
+ key_add_req->sta_idx = sta_idx;
+ } else {
+ /* Default key */
+ key_add_req->sta_idx = sta_idx;
+ key_add_req->key_idx = (u8_l)key_idx; /* only useful for default keys */
+ }
+ key_add_req->pairwise = pairwise;
+ key_add_req->inst_nbr = vif_idx;
+ key_add_req->key.length = key_len;
+ memcpy(&(key_add_req->key.array[0]), key, key_len);
+
+ key_add_req->cipher_suite = cipher_suite;
+
+ RWNX_DBG("%s: sta_idx:%d key_idx:%d inst_nbr:%d cipher:%d key_len:%d\n", __func__,
+ key_add_req->sta_idx, key_add_req->key_idx, key_add_req->inst_nbr,
+ key_add_req->cipher_suite, key_add_req->key.length);
+#if defined(CONFIG_RWNX_DBG) || defined(CONFIG_DYNAMIC_DEBUG)
+ print_hex_dump_bytes("key: ", DUMP_PREFIX_OFFSET, key_add_req->key.array, key_add_req->key.length);
+#endif
+
+ /* Send the MM_KEY_ADD_REQ message to LMAC FW */
+ return rwnx_send_msg(rwnx_hw, key_add_req, 1, MM_KEY_ADD_CFM, cfm);
+}
+
+int rwnx_send_key_del(struct rwnx_hw *rwnx_hw, uint8_t hw_key_idx)
+{
+ struct mm_key_del_req *key_del_req;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ /* Build the MM_KEY_DEL_REQ message */
+ key_del_req = rwnx_msg_zalloc(MM_KEY_DEL_REQ, TASK_MM, DRV_TASK_ID,
+ sizeof(struct mm_key_del_req));
+ if (!key_del_req)
+ return -ENOMEM;
+
+ /* Set parameters for the MM_KEY_DEL_REQ message */
+ key_del_req->hw_key_idx = hw_key_idx;
+
+ /* Send the MM_KEY_DEL_REQ message to LMAC FW */
+ return rwnx_send_msg(rwnx_hw, key_del_req, 1, MM_KEY_DEL_CFM, NULL);
+}
+
+int rwnx_send_bcn(struct rwnx_hw *rwnx_hw,u8 *buf, u8 vif_idx, u16 bcn_len)
+{
+ struct apm_set_bcn_ie_req *bcn_ie_req;
+ bcn_ie_req = rwnx_msg_zalloc(APM_SET_BEACON_IE_REQ, TASK_APM, DRV_TASK_ID,
+ sizeof(struct apm_set_bcn_ie_req));
+ if (!bcn_ie_req)
+ return -ENOMEM;
+
+ bcn_ie_req->vif_idx = vif_idx;
+ bcn_ie_req->bcn_ie_len = bcn_len;
+ memcpy(bcn_ie_req->bcn_ie, (u8 *)buf, bcn_len);
+ kfree(buf);
+
+ return rwnx_send_msg(rwnx_hw, bcn_ie_req, 1, APM_SET_BEACON_IE_CFM, NULL);
+}
+
+int rwnx_send_bcn_change(struct rwnx_hw *rwnx_hw, u8 vif_idx, u32 bcn_addr,
+ u16 bcn_len, u16 tim_oft, u16 tim_len, u16 *csa_oft)
+{
+ struct mm_bcn_change_req *req;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ /* Build the MM_BCN_CHANGE_REQ message */
+ req = rwnx_msg_zalloc(MM_BCN_CHANGE_REQ, TASK_MM, DRV_TASK_ID,
+ sizeof(struct mm_bcn_change_req));
+ if (!req)
+ return -ENOMEM;
+
+ /* Set parameters for the MM_BCN_CHANGE_REQ message */
+ req->bcn_ptr = bcn_addr;
+ req->bcn_len = bcn_len;
+ req->tim_oft = tim_oft;
+ req->tim_len = tim_len;
+ req->inst_nbr = vif_idx;
+
+ if (csa_oft) {
+ int i;
+ for (i = 0; i < BCN_MAX_CSA_CPT; i++) {
+ req->csa_oft[i] = csa_oft[i];
+ }
+ }
+
+ /* Send the MM_BCN_CHANGE_REQ message to LMAC FW */
+ return rwnx_send_msg(rwnx_hw, req, 1, MM_BCN_CHANGE_CFM, NULL);
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 8, 0)
+static inline void cfg80211_chandef_create(struct cfg80211_chan_def *chandef,
+ struct ieee80211_channel *chan,
+ enum nl80211_channel_type chan_type)
+{
+ if (WARN_ON(!chan))
+ return;
+ chandef->chan = chan;
+ chandef->center_freq2 = 0;
+ switch (chan_type) {
+ case NL80211_CHAN_NO_HT:
+ chandef->width = NL80211_CHAN_WIDTH_20_NOHT;
+ chandef->center_freq1 = chan->center_freq;
+ break;
+ case NL80211_CHAN_HT20:
+ chandef->width = NL80211_CHAN_WIDTH_20;
+ chandef->center_freq1 = chan->center_freq;
+ break;
+ case NL80211_CHAN_HT40PLUS:
+ chandef->width = NL80211_CHAN_WIDTH_40;
+ chandef->center_freq1 = chan->center_freq + 10;
+ break;
+ case NL80211_CHAN_HT40MINUS:
+ chandef->width = NL80211_CHAN_WIDTH_40;
+ chandef->center_freq1 = chan->center_freq - 10;
+ break;
+ default:
+ WARN_ON(1);
+ }
+}
+#endif
+
+int rwnx_send_roc(struct rwnx_hw *rwnx_hw, struct rwnx_vif *vif,
+ struct ieee80211_channel *chan, unsigned int duration,
+ struct mm_remain_on_channel_cfm *roc_cfm)
+{
+ struct mm_remain_on_channel_req *req;
+ struct cfg80211_chan_def chandef;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ /* Create channel definition structure */
+ cfg80211_chandef_create(&chandef, chan, NL80211_CHAN_NO_HT);
+
+ /* Build the MM_REMAIN_ON_CHANNEL_REQ message */
+ req = rwnx_msg_zalloc(MM_REMAIN_ON_CHANNEL_REQ, TASK_MM, DRV_TASK_ID,
+ sizeof(struct mm_remain_on_channel_req));
+ if (!req)
+ return -ENOMEM;
+
+ /* Set parameters for the MM_REMAIN_ON_CHANNEL_REQ message */
+ req->op_code = MM_ROC_OP_START;
+ req->vif_index = vif->vif_index;
+ req->duration_ms = duration;
+ req->band = chan->band;
+ req->type = bw2chnl[chandef.width];
+ req->prim20_freq = chan->center_freq;
+ req->center1_freq = chandef.center_freq1;
+ req->center2_freq = chandef.center_freq2;
+ req->tx_power = chan_to_fw_pwr(chan->max_power);
+
+ /* Send the MM_REMAIN_ON_CHANNEL_REQ message to LMAC FW */
+ return rwnx_send_msg(rwnx_hw, req, 1, MM_REMAIN_ON_CHANNEL_CFM, roc_cfm);
+}
+
+int rwnx_send_cancel_roc(struct rwnx_hw *rwnx_hw)
+{
+ struct mm_remain_on_channel_req *req;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ /* Build the MM_REMAIN_ON_CHANNEL_REQ message */
+ req = rwnx_msg_zalloc(MM_REMAIN_ON_CHANNEL_REQ, TASK_MM, DRV_TASK_ID,
+ sizeof(struct mm_remain_on_channel_req));
+ if (!req)
+ return -ENOMEM;
+
+ /* Set parameters for the MM_REMAIN_ON_CHANNEL_REQ message */
+ req->op_code = MM_ROC_OP_CANCEL;
+
+ /* Send the MM_REMAIN_ON_CHANNEL_REQ message to LMAC FW */
+ return rwnx_send_msg(rwnx_hw, req, 1, MM_REMAIN_ON_CHANNEL_CFM, NULL);
+}
+
+int rwnx_send_set_power(struct rwnx_hw *rwnx_hw, u8 vif_idx, s8 pwr,
+ struct mm_set_power_cfm *cfm)
+{
+ struct mm_set_power_req *req;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ /* Build the MM_SET_POWER_REQ message */
+ req = rwnx_msg_zalloc(MM_SET_POWER_REQ, TASK_MM, DRV_TASK_ID,
+ sizeof(struct mm_set_power_req));
+ if (!req)
+ return -ENOMEM;
+
+ /* Set parameters for the MM_SET_POWER_REQ message */
+ req->inst_nbr = vif_idx;
+ req->power = pwr;
+
+ /* Send the MM_SET_POWER_REQ message to LMAC FW */
+ return rwnx_send_msg(rwnx_hw, req, 1, MM_SET_POWER_CFM, cfm);
+}
+
+int rwnx_send_set_edca(struct rwnx_hw *rwnx_hw, u8 hw_queue, u32 param,
+ bool uapsd, u8 inst_nbr)
+{
+ struct mm_set_edca_req *set_edca_req;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ /* Build the MM_SET_EDCA_REQ message */
+ set_edca_req = rwnx_msg_zalloc(MM_SET_EDCA_REQ, TASK_MM, DRV_TASK_ID,
+ sizeof(struct mm_set_edca_req));
+ if (!set_edca_req)
+ return -ENOMEM;
+
+ /* Set parameters for the MM_SET_EDCA_REQ message */
+ set_edca_req->ac_param = param;
+ set_edca_req->uapsd = uapsd;
+ set_edca_req->hw_queue = hw_queue;
+ set_edca_req->inst_nbr = inst_nbr;
+
+ /* Send the MM_SET_EDCA_REQ message to LMAC FW */
+ return rwnx_send_msg(rwnx_hw, set_edca_req, 1, MM_SET_EDCA_CFM, NULL);
+}
+
+#ifdef CONFIG_RWNX_P2P_DEBUGFS
+int rwnx_send_p2p_oppps_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *rwnx_vif,
+ u8 ctw, struct mm_set_p2p_oppps_cfm *cfm)
+{
+ struct mm_set_p2p_oppps_req *p2p_oppps_req;
+ int error;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ /* Build the MM_SET_P2P_OPPPS_REQ message */
+ p2p_oppps_req = rwnx_msg_zalloc(MM_SET_P2P_OPPPS_REQ, TASK_MM, DRV_TASK_ID,
+ sizeof(struct mm_set_p2p_oppps_req));
+
+ if (!p2p_oppps_req) {
+ return -ENOMEM;
+ }
+
+ /* Fill the message parameters */
+ p2p_oppps_req->vif_index = rwnx_vif->vif_index;
+ p2p_oppps_req->ctwindow = ctw;
+
+ /* Send the MM_P2P_OPPPS_REQ message to LMAC FW */
+ error = rwnx_send_msg(rwnx_hw, p2p_oppps_req, 1, MM_SET_P2P_OPPPS_CFM, cfm);
+
+ return (error);
+}
+
+int rwnx_send_p2p_noa_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *rwnx_vif,
+ int count, int interval, int duration, bool dyn_noa,
+ struct mm_set_p2p_noa_cfm *cfm)
+{
+ struct mm_set_p2p_noa_req *p2p_noa_req;
+ int error;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ /* Param check */
+ if (count > 255)
+ count = 255;
+
+ if (duration >= interval) {
+ dev_err(rwnx_hw->dev, "Invalid p2p NOA config: interval=%d <= duration=%d\n",
+ interval, duration);
+ return -EINVAL;
+ }
+
+ /* Build the MM_SET_P2P_NOA_REQ message */
+ p2p_noa_req = rwnx_msg_zalloc(MM_SET_P2P_NOA_REQ, TASK_MM, DRV_TASK_ID,
+ sizeof(struct mm_set_p2p_noa_req));
+
+ if (!p2p_noa_req) {
+ return -ENOMEM;
+ }
+
+ /* Fill the message parameters */
+ p2p_noa_req->vif_index = rwnx_vif->vif_index;
+ p2p_noa_req->noa_inst_nb = 0;
+ p2p_noa_req->count = count;
+
+ if (count) {
+ p2p_noa_req->duration_us = duration * 1024;
+ p2p_noa_req->interval_us = interval * 1024;
+ p2p_noa_req->start_offset = (interval - duration - 10) * 1024;
+ p2p_noa_req->dyn_noa = dyn_noa;
+ }
+
+ /* Send the MM_SET_2P_NOA_REQ message to LMAC FW */
+ error = rwnx_send_msg(rwnx_hw, p2p_noa_req, 1, MM_SET_P2P_NOA_CFM, cfm);
+
+ return (error);
+}
+#endif /* CONFIG_RWNX_P2P_DEBUGFS */
+
+#ifdef AICWF_ARP_OFFLOAD
+int rwnx_send_arpoffload_en_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *rwnx_vif,
+ u32_l ipaddr, u8_l enable)
+{
+ struct mm_set_arpoffload_en_req *arp_offload_req;
+ int error;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ /* Build the MM_SET_ARPOFFLOAD_REQ message */
+ arp_offload_req = rwnx_msg_zalloc(MM_SET_ARPOFFLOAD_REQ, TASK_MM, DRV_TASK_ID,
+ sizeof(struct mm_set_arpoffload_en_req));
+
+ if (!arp_offload_req) {
+ return -ENOMEM;
+ }
+
+ /* Fill the message parameters */
+ arp_offload_req->enable = enable;
+ arp_offload_req->vif_idx = rwnx_vif->vif_index;
+ arp_offload_req->ipaddr = ipaddr;
+
+ /* Send the MM_ARPOFFLOAD_EN_REQ message to UMAC FW */
+ error = rwnx_send_msg(rwnx_hw, arp_offload_req, 1, MM_SET_ARPOFFLOAD_CFM, NULL);
+
+ return (error);
+}
+#endif
+
+int rwnx_send_coex_req(struct rwnx_hw *rwnx_hw, u8_l disable_coexnull, u8_l enable_nullcts)
+{
+ struct mm_set_coex_req *coex_req;
+ int error;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+
+ /* Build the MM_SET_COEX_REQ message */
+ coex_req = rwnx_msg_zalloc(MM_SET_COEX_REQ, TASK_MM, DRV_TASK_ID,
+ sizeof(struct mm_set_coex_req));
+
+ if (!coex_req) {
+ return -ENOMEM;
+ }
+
+ coex_req->bt_on = 1;
+ coex_req->disable_coexnull = disable_coexnull;
+ coex_req->enable_nullcts = enable_nullcts;
+ coex_req->enable_periodic_timer = 0;
+ coex_req->coex_timeslot_set = 0;
+
+ /* Send the MM_SET_COEX_REQ message to UMAC FW */
+ error = rwnx_send_msg(rwnx_hw, coex_req, 1, MM_SET_COEX_CFM, NULL);
+
+ return (error);
+};
+
+
+int rwnx_send_rf_config_req(struct rwnx_hw *rwnx_hw, u8_l ofst, u8_l sel, u8_l *tbl, u16_l len)
+{
+ struct mm_set_rf_config_req *rf_config_req;
+ int error;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ /* Build the MM_SET_RF_CONFIG_REQ message */
+ rf_config_req = rwnx_msg_zalloc(MM_SET_RF_CONFIG_REQ, TASK_MM, DRV_TASK_ID,
+ sizeof(struct mm_set_rf_config_req));
+
+ if (!rf_config_req) {
+ return -ENOMEM;
+ }
+
+ rf_config_req->table_sel = sel;
+ rf_config_req->table_ofst = ofst;
+ rf_config_req->table_num = 16;
+ rf_config_req->deft_page = 0;
+
+ memcpy(rf_config_req->data, tbl, len);
+
+ /* Send the MM_SET_RF_CONFIG_REQ message to UMAC FW */
+ error = rwnx_send_msg(rwnx_hw, rf_config_req, 1, MM_SET_RF_CONFIG_CFM, NULL);
+
+ return (error);
+}
+
+extern void get_userconfig_xtal_cap(xtal_cap_conf_t *xtal_cap);
+
+int rwnx_send_rf_calib_req(struct rwnx_hw *rwnx_hw, struct mm_set_rf_calib_cfm *cfm)
+{
+ struct mm_set_rf_calib_req *rf_calib_req;
+ xtal_cap_conf_t xtal_cap = {0,};
+ int error;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ /* Build the MM_SET_RF_CALIB_REQ message */
+ rf_calib_req = rwnx_msg_zalloc(MM_SET_RF_CALIB_REQ, TASK_MM, DRV_TASK_ID,
+ sizeof(struct mm_set_rf_calib_req));
+
+ if (!rf_calib_req) {
+ return -ENOMEM;
+ }
+
+ if(rwnx_hw->usbdev->chipid == PRODUCT_ID_AIC8801){
+ rf_calib_req->cal_cfg_24g = 0xbf;
+ rf_calib_req->cal_cfg_5g = 0x3f;
+ }else if(rwnx_hw->usbdev->chipid == PRODUCT_ID_AIC8800DC ||
+ rwnx_hw->usbdev->chipid == PRODUCT_ID_AIC8800DW){
+ rf_calib_req->cal_cfg_24g = 0x0f8f;
+ rf_calib_req->cal_cfg_5g = 0;
+ }else if(rwnx_hw->usbdev->chipid == PRODUCT_ID_AIC8800D81){
+ rf_calib_req->cal_cfg_24g = 0x0f8f;
+ rf_calib_req->cal_cfg_5g = 0x0f0f;
+ }
+
+ rf_calib_req->param_alpha = 0x0c34c008;
+ rf_calib_req->bt_calib_en = 0;
+ rf_calib_req->bt_calib_param = 0x264203;
+
+ get_userconfig_xtal_cap(&xtal_cap);
+
+ if (xtal_cap.enable) {
+ AICWFDBG(LOGINFO, "user xtal cap: %d, cap_fine: %d\n", xtal_cap.xtal_cap, xtal_cap.xtal_cap_fine);
+ rf_calib_req->xtal_cap = xtal_cap.xtal_cap;
+ rf_calib_req->xtal_cap_fine = xtal_cap.xtal_cap_fine;
+ } else {
+ rf_calib_req->xtal_cap = 0;
+ rf_calib_req->xtal_cap_fine = 0;
+ }
+
+ /* Send the MM_SET_RF_CALIB_REQ message to UMAC FW */
+ error = rwnx_send_msg(rwnx_hw, rf_calib_req, 1, MM_SET_RF_CALIB_CFM, cfm);
+
+ return (error);
+};
+
+int rwnx_send_get_macaddr_req(struct rwnx_hw *rwnx_hw, struct mm_get_mac_addr_cfm *cfm)
+{
+ struct mm_get_mac_addr_req *get_macaddr_req;
+ int error;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ /* Build the MM_GET_MAC_ADDR_REQ message */
+ get_macaddr_req = rwnx_msg_zalloc(MM_GET_MAC_ADDR_REQ, TASK_MM, DRV_TASK_ID,
+ sizeof(struct mm_get_mac_addr_req));
+
+ if (!get_macaddr_req) {
+ return -ENOMEM;
+ }
+
+ get_macaddr_req->get = 1;
+
+ /* Send the MM_GET_MAC_ADDR_REQ message to UMAC FW */
+ error = rwnx_send_msg(rwnx_hw, get_macaddr_req, 1, MM_GET_MAC_ADDR_CFM, cfm);
+
+ return (error);
+};
+
+
+int rwnx_send_get_sta_info_req(struct rwnx_hw *rwnx_hw, u8_l sta_idx, struct mm_get_sta_info_cfm *cfm)
+{
+ struct mm_get_sta_info_req *get_info_req;
+ int error;
+
+ /* Build the MM_GET_STA_INFO_REQ message */
+ get_info_req = rwnx_msg_zalloc(MM_GET_STA_INFO_REQ, TASK_MM, DRV_TASK_ID,
+ sizeof(struct mm_get_sta_info_req));
+
+ if (!get_info_req) {
+ return -ENOMEM;
+ }
+
+ get_info_req->sta_idx = sta_idx;
+
+ /* Send the MM_GET_STA_INFO_REQ message to UMAC FW */
+ error = rwnx_send_msg(rwnx_hw, get_info_req, 1, MM_GET_STA_INFO_CFM, cfm);
+
+ return error;
+};
+
+
+#if 0
+int rwnx_send_get_sta_txinfo_req(struct rwnx_hw *rwnx_hw, u8_l sta_idx, struct mm_get_sta_txinfo_cfm *cfm)
+{
+ struct mm_get_sta_txinfo_req *get_txinfo_req;
+ int error;
+
+
+ /* Build the MM_GET_STA_TXINFO_REQ message */
+ get_txinfo_req = rwnx_msg_zalloc(MM_GET_STA_TXINFO_REQ, TASK_MM, DRV_TASK_ID,
+ sizeof(struct mm_get_sta_txinfo_req));
+
+ if (!get_txinfo_req) {
+ return -ENOMEM;
+ }
+
+ get_txinfo_req->sta_idx = 1;
+
+ /* Send the MM_GET_STA_TXINFO_REQ message to UMAC FW */
+ error = rwnx_send_msg(rwnx_hw, get_txinfo_req, 1, MM_GET_STA_TXINFO_CFM, cfm);
+
+ return (error);
+}
+#endif
+
+int rwnx_send_set_stack_start_req(struct rwnx_hw *rwnx_hw, u8_l on, u8_l efuse_valid, u8_l set_vendor_info,
+ u8_l fwtrace_redir_en, struct mm_set_stack_start_cfm *cfm)
+{
+ struct mm_set_stack_start_req *req;
+ int error;
+
+ /* Build the MM_SET_STACK_START_REQ message */
+ req = rwnx_msg_zalloc(MM_SET_STACK_START_REQ, TASK_MM, DRV_TASK_ID, sizeof(struct mm_set_stack_start_req));
+
+ if (!req) {
+ return -ENOMEM;
+ }
+
+ req->is_stack_start = on;
+ req->efuse_valid = efuse_valid;
+ req->set_vendor_info = set_vendor_info;
+ req->fwtrace_redir = fwtrace_redir_en;
+ /* Send the MM_GET_STA_TXINFO_REQ message to UMAC FW */
+ error = rwnx_send_msg(rwnx_hw, req, 1, MM_SET_STACK_START_CFM, cfm);
+
+ return error;
+}
+
+#if 0
+int rwnx_send_txop_req(struct rwnx_hw *rwnx_hw, uint16_t *txop, u8_l long_nav_en, u8_l cfe_en)
+{
+ struct mm_set_txop_req *req;
+ int error;
+
+ /* Build the MM_SET_TXOP_REQ message */
+ req = rwnx_msg_zalloc(MM_SET_TXOP_REQ, TASK_MM, DRV_TASK_ID, sizeof(struct mm_set_txop_req));
+
+ if (!req) {
+ return -ENOMEM;
+ }
+
+ req->txop_bk = txop[0];
+ req->txop_be = txop[1];
+ req->txop_vi = txop[2];
+ req->txop_vo = txop[3];
+ req->long_nav_en = long_nav_en;
+ req->cfe_en = cfe_en;
+
+ /* Send the MM_SET_TXOP_REQ message to UMAC FW */
+ error = rwnx_send_msg(rwnx_hw, req, 1, MM_SET_TXOP_CFM, NULL);
+
+ return error;
+}
+
+int rwnx_send_vendor_trx_param_req(struct rwnx_hw *rwnx_hw, uint32_t *edca, uint8_t vif_idx, uint8_t retry_cnt)
+{
+ struct mm_set_vendor_trx_param_req *req;
+ int error;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ /* Build the MM_SET_VENDOR_TRX_PARAM_REQ message */
+ req = rwnx_msg_zalloc(MM_SET_VENDOR_TRX_PARAM_REQ, TASK_MM, DRV_TASK_ID, sizeof(struct mm_set_vendor_trx_param_req));
+ if (!req) {
+ return -ENOMEM;
+ }
+
+ req->edca[0] = edca[0];
+ req->edca[1] = edca[1];
+ req->edca[2] = edca[2];
+ req->edca[3] = edca[3];
+ req->vif_idx = vif_idx;
+ req->retry_cnt = retry_cnt;
+
+ /* Send the MM_SET_VENDOR_TRX_PARAM_REQ message to UMAC FW */
+ error = rwnx_send_msg(rwnx_hw, req, 1, MM_SET_VENDOR_TRX_PARAM_CFM, NULL);
+
+ return error;
+}
+
+#endif
+int rwnx_send_vendor_hwconfig_req(struct rwnx_hw *rwnx_hw, uint32_t hwconfig_id, int32_t *param)
+{
+ struct mm_set_acs_txop_req *req0;
+ struct mm_set_channel_access_req *req1;
+ struct mm_set_mac_timescale_req *req2;
+ struct mm_set_cca_threshold_req *req3;
+ struct mm_set_bwmode_req *req4;
+
+ int error;
+
+ switch (hwconfig_id)
+ {
+ case ACS_TXOP_REQ:
+ /* Build the ACS_TXOP_REQ message */
+ req0= rwnx_msg_zalloc(MM_SET_VENDOR_HWCONFIG_REQ, TASK_MM, DRV_TASK_ID, sizeof(struct mm_set_acs_txop_req) );
+ if (!req0)
+ return -ENOMEM;
+ req0->hwconfig_id = hwconfig_id;
+ req0->txop_be = param[0];
+ req0->txop_bk = param[1];
+ req0->txop_vi = param[2];
+ req0->txop_vo = param[3];
+ printk("set_acs_txop_req: be: %x,bk: %x,vi: %x,vo: %x\n",
+ req0->txop_be, req0->txop_bk, req0->txop_vi, req0->txop_vo);
+ /* Send the MM_SET_VENDOR_HWCONFIG_CFM message to UMAC FW */
+ error = rwnx_send_msg(rwnx_hw, req0, 1, MM_SET_VENDOR_HWCONFIG_CFM, NULL);
+ break;
+
+ case CHANNEL_ACCESS_REQ:
+ /* Build the CHANNEL_ACCESS_REQ message */
+ req1 = rwnx_msg_zalloc(MM_SET_VENDOR_HWCONFIG_REQ, TASK_MM, DRV_TASK_ID, sizeof(struct mm_set_channel_access_req));
+ if (!req1)
+ return -ENOMEM;
+ req1->hwconfig_id = hwconfig_id;
+ req1->edca[0] = param[0];
+ req1->edca[1] = param[1];
+ req1->edca[2] = param[2];
+ req1->edca[3] = param[3];
+ req1->vif_idx = param[4];
+ req1->retry_cnt = param[5];
+ req1->rts_en = param[6];
+ req1->long_nav_en = param[7];
+ req1->cfe_en = param[8];
+ req1->rc_retry_cnt[0] = param[9];
+ req1->rc_retry_cnt[1] = param[10];
+ req1->rc_retry_cnt[2] = param[11];
+ printk("set_channel_access_req:edca[]= %x %x %x %x\nvif_idx: %x, retry_cnt: %x, rts_en: %x, long_nav_en: %x, cfe_en: %x, rc_retry_cnt: %x:%x:%x\n",
+ req1->edca[0], req1->edca[1], req1->edca[2], req1->edca[3], req1->vif_idx, req1->retry_cnt, req1->rts_en, req1->long_nav_en, req1->cfe_en, req1->rc_retry_cnt[0],req1->rc_retry_cnt[1], req1->rc_retry_cnt[2]);
+ /* Send the MM_SET_VENDOR_HWCONFIG_CFM message to UMAC FW */
+ error = rwnx_send_msg(rwnx_hw, req1, 1, MM_SET_VENDOR_HWCONFIG_CFM, NULL);
+ break;
+
+ case MAC_TIMESCALE_REQ:
+ /* Build the MAC_TIMESCALE_REQ message */
+ req2 = rwnx_msg_zalloc(MM_SET_VENDOR_HWCONFIG_REQ, TASK_MM, DRV_TASK_ID, sizeof(struct mm_set_mac_timescale_req));
+ if (!req2)
+ return -ENOMEM;
+ req2->hwconfig_id = hwconfig_id;
+ req2->sifsA_time = param[0];
+ req2->sifsB_time = param[1];
+ req2->slot_time = param[2];
+ req2->rx_startdelay_ofdm = param[3];
+ req2->rx_startdelay_long = param[4];
+ req2->rx_startdelay_short = param[5];
+ printk("set_mac_timescale_req:sifsA_time: %x, sifsB_time: %x, slot_time: %x, rx_startdelay ofdm:%x long %x short %x\n",
+ req2->sifsA_time, req2->sifsB_time, req2->slot_time, req2->rx_startdelay_ofdm, req2->rx_startdelay_long, req2->rx_startdelay_short);
+ /* Send the MM_SET_VENDOR_HWCONFIG_CFM message to UMAC FW */
+ error = rwnx_send_msg(rwnx_hw, req2, 1, MM_SET_VENDOR_HWCONFIG_CFM, NULL);
+ break;
+
+ case CCA_THRESHOLD_REQ:
+ /* Build the CCA_THRESHOLD_REQ message */
+ req3 = rwnx_msg_zalloc(MM_SET_VENDOR_HWCONFIG_REQ, TASK_MM, DRV_TASK_ID, sizeof(struct mm_set_cca_threshold_req));
+ if (!req3)
+ return -ENOMEM;
+ req3->hwconfig_id = hwconfig_id;
+ req3->auto_cca_en = param[0];
+ req3->cca20p_rise_th = param[1];
+ req3->cca20s_rise_th = param[2];
+ req3->cca20p_fall_th = param[3];
+ req3->cca20s_fall_th = param[4];
+ printk("cca_threshold_req: auto_cca_en:%d\ncca20p_rise_th = %d\ncca20s_rise_th = %d\ncca20p_fall_th = %d\ncca20s_fall_th = %d\n",
+ req3->auto_cca_en, req3->cca20p_rise_th, req3->cca20s_rise_th, req3->cca20p_fall_th, req3->cca20s_fall_th);
+ /* Send the MM_SET_VENDOR_HWCONFIG_CFM message to UMAC FW */
+ error = rwnx_send_msg(rwnx_hw, req3, 1, MM_SET_VENDOR_HWCONFIG_CFM, NULL);
+ break;
+ case BWMODE_REQ:
+ /* Build the SET_BWMODE_REQ message */
+ req4 = rwnx_msg_zalloc(MM_SET_VENDOR_HWCONFIG_REQ, TASK_MM, DRV_TASK_ID, sizeof(struct mm_set_bwmode_req));
+ if (!req4)
+ return -ENOMEM;
+ req4->hwconfig_id = hwconfig_id;
+ req4->bwmode = param[0];
+ printk("bwmode :%d\n", req4->bwmode);
+ /* Send the MM_SET_VENDOR_HWCONFIG_CFM message to UMAC FW */
+ error = rwnx_send_msg(rwnx_hw, req4, 1, MM_SET_VENDOR_HWCONFIG_CFM, NULL);
+ break;
+ default:
+ return -ENOMEM;
+ }
+ return error;
+}
+
+int rwnx_send_get_fw_version_req(struct rwnx_hw *rwnx_hw, struct mm_get_fw_version_cfm *cfm)
+{
+ void *req;
+ int error;
+
+ /* Build the MM_GET_FW_VERSION_REQ message */
+ req = rwnx_msg_zalloc(MM_GET_FW_VERSION_REQ, TASK_MM, DRV_TASK_ID, sizeof(u8));
+
+ if (!req) {
+ return -ENOMEM;
+ }
+
+ /* Send the MM_GET_FW_VERSION_REQ message to UMAC FW */
+ error = rwnx_send_msg(rwnx_hw, req, 1, MM_GET_FW_VERSION_CFM, cfm);
+
+ return error;
+}
+
+
+extern void get_userconfig_txpwr_idx(txpwr_idx_conf_t *txpwr_idx);
+
+int rwnx_send_txpwr_idx_req(struct rwnx_hw *rwnx_hw)
+{
+ struct mm_set_txpwr_idx_req *txpwr_idx_req;
+ txpwr_idx_conf_t *txpwr_idx;
+ int error;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ /* Build the MM_SET_TXPWR_IDX_REQ message */
+ txpwr_idx_req = rwnx_msg_zalloc(MM_SET_TXPWR_IDX_LVL_REQ, TASK_MM, DRV_TASK_ID,
+ sizeof(struct mm_set_txpwr_idx_req));
+
+ if (!txpwr_idx_req) {
+ return -ENOMEM;
+ }
+
+ txpwr_idx = &txpwr_idx_req->txpwr_idx;
+ txpwr_idx->enable = 1;
+ txpwr_idx->dsss=9;
+ txpwr_idx->ofdmlowrate_2g4=8;
+ txpwr_idx->ofdm64qam_2g4=8;
+ txpwr_idx->ofdm256qam_2g4=8;
+ txpwr_idx->ofdm1024qam_2g4=8;
+ txpwr_idx->ofdmlowrate_5g=11;
+ txpwr_idx->ofdm64qam_5g=10;
+ txpwr_idx->ofdm256qam_5g=9;
+ txpwr_idx->ofdm1024qam_5g=9;
+
+ get_userconfig_txpwr_idx(txpwr_idx);
+
+ AICWFDBG(LOGINFO, "%s:enable:%d\r\n", __func__, txpwr_idx->enable);
+ AICWFDBG(LOGINFO, "%s:dsss:%d\r\n", __func__, txpwr_idx->dsss);
+ AICWFDBG(LOGINFO, "%s:ofdmlowrate_2g4:%d\r\n", __func__, txpwr_idx->ofdmlowrate_2g4);
+ AICWFDBG(LOGINFO, "%s:ofdm64qam_2g4:%d\r\n", __func__, txpwr_idx->ofdm64qam_2g4);
+ AICWFDBG(LOGINFO, "%s:ofdm256qam_2g4:%d\r\n", __func__, txpwr_idx->ofdm256qam_2g4);
+ AICWFDBG(LOGINFO, "%s:ofdm1024qam_2g4:%d\r\n", __func__, txpwr_idx->ofdm1024qam_2g4);
+ AICWFDBG(LOGINFO, "%s:ofdmlowrate_5g:%d\r\n", __func__, txpwr_idx->ofdmlowrate_5g);
+ AICWFDBG(LOGINFO, "%s:ofdm64qam_5g:%d\r\n", __func__, txpwr_idx->ofdm64qam_5g);
+ AICWFDBG(LOGINFO, "%s:ofdm256qam_5g:%d\r\n", __func__, txpwr_idx->ofdm256qam_5g);
+ AICWFDBG(LOGINFO, "%s:ofdm1024qam_5g:%d\r\n", __func__, txpwr_idx->ofdm1024qam_5g);
+
+ /* Send the MM_SET_TXPWR_IDX_REQ message to UMAC FW */
+ error = rwnx_send_msg(rwnx_hw, txpwr_idx_req, 1, MM_SET_TXPWR_IDX_LVL_CFM, NULL);
+
+ return (error);
+}
+
+int rwnx_send_txpwr_lvl_req(struct rwnx_hw *rwnx_hw)
+{
+ struct mm_set_txpwr_lvl_req *txpwr_lvl_req;
+ txpwr_lvl_conf_v2_t txpwr_lvl_v2_tmp;
+ txpwr_lvl_conf_v2_t *txpwr_lvl_v2;
+ txpwr_loss_conf_t txpwr_loss_tmp;
+ txpwr_loss_conf_t *txpwr_loss;
+ int error;
+ int i;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ /* Build the MM_SET_TXPWR_LVL_REQ message */
+ txpwr_lvl_req = rwnx_msg_zalloc(MM_SET_TXPWR_IDX_LVL_REQ, TASK_MM, DRV_TASK_ID,
+ sizeof(struct mm_set_txpwr_lvl_req));
+
+ if (!txpwr_lvl_req) {
+ return -ENOMEM;
+ }
+
+ txpwr_lvl_v2 = &txpwr_lvl_v2_tmp;
+ txpwr_loss = &txpwr_loss_tmp;
+ txpwr_loss->loss_enable = 0;
+
+ get_userconfig_txpwr_lvl_v2_in_fdrv(txpwr_lvl_v2);
+ get_userconfig_txpwr_loss(txpwr_loss);
+ if (txpwr_lvl_v2->enable == 0) {
+ rwnx_msg_free(rwnx_hw, txpwr_lvl_req);
+ return 0;
+ } else {
+ AICWFDBG(LOGINFO, "%s:enable:%d\r\n", __func__, txpwr_lvl_v2->enable);
+ AICWFDBG(LOGINFO, "%s:lvl_11b_11ag_1m_2g4:%d\r\n", __func__, txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[0]);
+ AICWFDBG(LOGINFO, "%s:lvl_11b_11ag_2m_2g4:%d\r\n", __func__, txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[1]);
+ AICWFDBG(LOGINFO, "%s:lvl_11b_11ag_5m5_2g4:%d\r\n", __func__, txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[2]);
+ AICWFDBG(LOGINFO, "%s:lvl_11b_11ag_11m_2g4:%d\r\n", __func__, txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[3]);
+ AICWFDBG(LOGINFO, "%s:lvl_11b_11ag_6m_2g4:%d\r\n", __func__, txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[4]);
+ AICWFDBG(LOGINFO, "%s:lvl_11b_11ag_9m_2g4:%d\r\n", __func__, txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[5]);
+ AICWFDBG(LOGINFO, "%s:lvl_11b_11ag_12m_2g4:%d\r\n", __func__, txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[6]);
+ AICWFDBG(LOGINFO, "%s:lvl_11b_11ag_18m_2g4:%d\r\n", __func__, txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[7]);
+ AICWFDBG(LOGINFO, "%s:lvl_11b_11ag_24m_2g4:%d\r\n", __func__, txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[8]);
+ AICWFDBG(LOGINFO, "%s:lvl_11b_11ag_36m_2g4:%d\r\n", __func__, txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[9]);
+ AICWFDBG(LOGINFO, "%s:lvl_11b_11ag_48m_2g4:%d\r\n", __func__, txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[10]);
+ AICWFDBG(LOGINFO, "%s:lvl_11b_11ag_54m_2g4:%d\r\n", __func__, txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[11]);
+ AICWFDBG(LOGINFO, "%s:lvl_11n_11ac_mcs0_2g4:%d\r\n",__func__, txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[0]);
+ AICWFDBG(LOGINFO, "%s:lvl_11n_11ac_mcs1_2g4:%d\r\n",__func__, txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[1]);
+ AICWFDBG(LOGINFO, "%s:lvl_11n_11ac_mcs2_2g4:%d\r\n",__func__, txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[2]);
+ AICWFDBG(LOGINFO, "%s:lvl_11n_11ac_mcs3_2g4:%d\r\n",__func__, txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[3]);
+ AICWFDBG(LOGINFO, "%s:lvl_11n_11ac_mcs4_2g4:%d\r\n",__func__, txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[4]);
+ AICWFDBG(LOGINFO, "%s:lvl_11n_11ac_mcs5_2g4:%d\r\n",__func__, txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[5]);
+ AICWFDBG(LOGINFO, "%s:lvl_11n_11ac_mcs6_2g4:%d\r\n",__func__, txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[6]);
+ AICWFDBG(LOGINFO, "%s:lvl_11n_11ac_mcs7_2g4:%d\r\n",__func__, txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[7]);
+ AICWFDBG(LOGINFO, "%s:lvl_11n_11ac_mcs8_2g4:%d\r\n",__func__, txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[8]);
+ AICWFDBG(LOGINFO, "%s:lvl_11n_11ac_mcs9_2g4:%d\r\n",__func__, txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[9]);
+ AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs0_2g4:%d\r\n", __func__, txpwr_lvl_v2->pwrlvl_11ax_2g4[0]);
+ AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs1_2g4:%d\r\n", __func__, txpwr_lvl_v2->pwrlvl_11ax_2g4[1]);
+ AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs2_2g4:%d\r\n", __func__, txpwr_lvl_v2->pwrlvl_11ax_2g4[2]);
+ AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs3_2g4:%d\r\n", __func__, txpwr_lvl_v2->pwrlvl_11ax_2g4[3]);
+ AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs4_2g4:%d\r\n", __func__, txpwr_lvl_v2->pwrlvl_11ax_2g4[4]);
+ AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs5_2g4:%d\r\n", __func__, txpwr_lvl_v2->pwrlvl_11ax_2g4[5]);
+ AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs6_2g4:%d\r\n", __func__, txpwr_lvl_v2->pwrlvl_11ax_2g4[6]);
+ AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs7_2g4:%d\r\n", __func__, txpwr_lvl_v2->pwrlvl_11ax_2g4[7]);
+ AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs8_2g4:%d\r\n", __func__, txpwr_lvl_v2->pwrlvl_11ax_2g4[8]);
+ AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs9_2g4:%d\r\n", __func__, txpwr_lvl_v2->pwrlvl_11ax_2g4[9]);
+ AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs10_2g4:%d\r\n", __func__, txpwr_lvl_v2->pwrlvl_11ax_2g4[10]);
+ AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs11_2g4:%d\r\n", __func__, txpwr_lvl_v2->pwrlvl_11ax_2g4[11]);
+
+ if (txpwr_loss->loss_enable == 1) {
+ AICWFDBG(LOGINFO, "%s:loss_value:%d\r\n", __func__, txpwr_loss->loss_value);
+
+ for (i = 0; i <= 11; i++)
+ txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[i] += txpwr_loss->loss_value;
+ for (i = 0; i <= 9; i++)
+ txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[i] += txpwr_loss->loss_value;
+ for (i = 0; i <= 11; i++)
+ txpwr_lvl_v2->pwrlvl_11ax_2g4[i] += txpwr_loss->loss_value;
+ }
+ if ((testmode == 0) && (chip_sub_id == 0)) {
+ txpwr_lvl_req->txpwr_lvl.enable = txpwr_lvl_v2->enable;
+ txpwr_lvl_req->txpwr_lvl.dsss = txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[3]; // 11M
+ txpwr_lvl_req->txpwr_lvl.ofdmlowrate_2g4= txpwr_lvl_v2->pwrlvl_11ax_2g4[4]; // MCS4
+ txpwr_lvl_req->txpwr_lvl.ofdm64qam_2g4 = txpwr_lvl_v2->pwrlvl_11ax_2g4[7]; // MCS7
+ txpwr_lvl_req->txpwr_lvl.ofdm256qam_2g4 = txpwr_lvl_v2->pwrlvl_11ax_2g4[9]; // MCS9
+ txpwr_lvl_req->txpwr_lvl.ofdm1024qam_2g4= txpwr_lvl_v2->pwrlvl_11ax_2g4[11]; // MCS11
+ txpwr_lvl_req->txpwr_lvl.ofdmlowrate_5g = 13; // unused
+ txpwr_lvl_req->txpwr_lvl.ofdm64qam_5g = 13; // unused
+ txpwr_lvl_req->txpwr_lvl.ofdm256qam_5g = 13; // unused
+ txpwr_lvl_req->txpwr_lvl.ofdm1024qam_5g = 13; // unused
+ } else {
+ txpwr_lvl_req->txpwr_lvl_v2 = *txpwr_lvl_v2;
+ }
+
+ /* Send the MM_SET_TXPWR_LVL_REQ message to UMAC FW */
+ error = rwnx_send_msg(rwnx_hw, txpwr_lvl_req, 1, MM_SET_TXPWR_IDX_LVL_CFM, NULL);
+
+ return (error);
+ }
+}
+
+int rwnx_send_txpwr_lvl_v3_req(struct rwnx_hw *rwnx_hw)
+{
+ struct mm_set_txpwr_lvl_req *txpwr_lvl_req;
+ txpwr_lvl_conf_v3_t txpwr_lvl_v3_tmp;
+ txpwr_lvl_conf_v3_t *txpwr_lvl_v3;
+ int error;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ /* Build the MM_SET_TXPWR_LVL_REQ message */
+ txpwr_lvl_req = rwnx_msg_zalloc(MM_SET_TXPWR_IDX_LVL_REQ, TASK_MM, DRV_TASK_ID,
+ sizeof(struct mm_set_txpwr_lvl_req));
+
+ if (!txpwr_lvl_req) {
+ return -ENOMEM;
+ }
+
+ txpwr_lvl_v3 = &txpwr_lvl_v3_tmp;
+
+ get_userconfig_txpwr_lvl_v3_in_fdrv(txpwr_lvl_v3);
+
+ if (txpwr_lvl_v3->enable == 0) {
+ rwnx_msg_free(rwnx_hw, txpwr_lvl_req);
+ return 0;
+ } else {
+ AICWFDBG(LOGINFO, "%s:enable:%d\r\n", __func__, txpwr_lvl_v3->enable);
+ AICWFDBG(LOGINFO, "%s:lvl_11b_11ag_1m_2g4:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11b_11ag_2g4[0]);
+ AICWFDBG(LOGINFO, "%s:lvl_11b_11ag_2m_2g4:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11b_11ag_2g4[1]);
+ AICWFDBG(LOGINFO, "%s:lvl_11b_11ag_5m5_2g4:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11b_11ag_2g4[2]);
+ AICWFDBG(LOGINFO, "%s:lvl_11b_11ag_11m_2g4:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11b_11ag_2g4[3]);
+ AICWFDBG(LOGINFO, "%s:lvl_11b_11ag_6m_2g4:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11b_11ag_2g4[4]);
+ AICWFDBG(LOGINFO, "%s:lvl_11b_11ag_9m_2g4:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11b_11ag_2g4[5]);
+ AICWFDBG(LOGINFO, "%s:lvl_11b_11ag_12m_2g4:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11b_11ag_2g4[6]);
+ AICWFDBG(LOGINFO, "%s:lvl_11b_11ag_18m_2g4:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11b_11ag_2g4[7]);
+ AICWFDBG(LOGINFO, "%s:lvl_11b_11ag_24m_2g4:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11b_11ag_2g4[8]);
+ AICWFDBG(LOGINFO, "%s:lvl_11b_11ag_36m_2g4:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11b_11ag_2g4[9]);
+ AICWFDBG(LOGINFO, "%s:lvl_11b_11ag_48m_2g4:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11b_11ag_2g4[10]);
+ AICWFDBG(LOGINFO, "%s:lvl_11b_11ag_54m_2g4:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11b_11ag_2g4[11]);
+ AICWFDBG(LOGINFO, "%s:lvl_11n_11ac_mcs0_2g4:%d\r\n",__func__, txpwr_lvl_v3->pwrlvl_11n_11ac_2g4[0]);
+ AICWFDBG(LOGINFO, "%s:lvl_11n_11ac_mcs1_2g4:%d\r\n",__func__, txpwr_lvl_v3->pwrlvl_11n_11ac_2g4[1]);
+ AICWFDBG(LOGINFO, "%s:lvl_11n_11ac_mcs2_2g4:%d\r\n",__func__, txpwr_lvl_v3->pwrlvl_11n_11ac_2g4[2]);
+ AICWFDBG(LOGINFO, "%s:lvl_11n_11ac_mcs3_2g4:%d\r\n",__func__, txpwr_lvl_v3->pwrlvl_11n_11ac_2g4[3]);
+ AICWFDBG(LOGINFO, "%s:lvl_11n_11ac_mcs4_2g4:%d\r\n",__func__, txpwr_lvl_v3->pwrlvl_11n_11ac_2g4[4]);
+ AICWFDBG(LOGINFO, "%s:lvl_11n_11ac_mcs5_2g4:%d\r\n",__func__, txpwr_lvl_v3->pwrlvl_11n_11ac_2g4[5]);
+ AICWFDBG(LOGINFO, "%s:lvl_11n_11ac_mcs6_2g4:%d\r\n",__func__, txpwr_lvl_v3->pwrlvl_11n_11ac_2g4[6]);
+ AICWFDBG(LOGINFO, "%s:lvl_11n_11ac_mcs7_2g4:%d\r\n",__func__, txpwr_lvl_v3->pwrlvl_11n_11ac_2g4[7]);
+ AICWFDBG(LOGINFO, "%s:lvl_11n_11ac_mcs8_2g4:%d\r\n",__func__, txpwr_lvl_v3->pwrlvl_11n_11ac_2g4[8]);
+ AICWFDBG(LOGINFO, "%s:lvl_11n_11ac_mcs9_2g4:%d\r\n",__func__, txpwr_lvl_v3->pwrlvl_11n_11ac_2g4[9]);
+ AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs0_2g4:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11ax_2g4[0]);
+ AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs1_2g4:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11ax_2g4[1]);
+ AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs2_2g4:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11ax_2g4[2]);
+ AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs3_2g4:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11ax_2g4[3]);
+ AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs4_2g4:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11ax_2g4[4]);
+ AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs5_2g4:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11ax_2g4[5]);
+ AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs6_2g4:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11ax_2g4[6]);
+ AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs7_2g4:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11ax_2g4[7]);
+ AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs8_2g4:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11ax_2g4[8]);
+ AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs9_2g4:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11ax_2g4[9]);
+ AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs10_2g4:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11ax_2g4[10]);
+ AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs11_2g4:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11ax_2g4[11]);
+
+ AICWFDBG(LOGINFO, "%s:lvl_11a_1m_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11a_5g[0]);
+ AICWFDBG(LOGINFO, "%s:lvl_11a_2m_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11a_5g[1]);
+ AICWFDBG(LOGINFO, "%s:lvl_11a_5m5_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11a_5g[2]);
+ AICWFDBG(LOGINFO, "%s:lvl_11a_11m_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11a_5g[3]);
+ AICWFDBG(LOGINFO, "%s:lvl_11a_6m_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11a_5g[4]);
+ AICWFDBG(LOGINFO, "%s:lvl_11a_9m_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11a_5g[5]);
+ AICWFDBG(LOGINFO, "%s:lvl_11a_12m_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11a_5g[6]);
+ AICWFDBG(LOGINFO, "%s:lvl_11a_18m_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11a_5g[7]);
+ AICWFDBG(LOGINFO, "%s:lvl_11a_24m_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11a_5g[8]);
+ AICWFDBG(LOGINFO, "%s:lvl_11a_36m_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11a_5g[9]);
+ AICWFDBG(LOGINFO, "%s:lvl_11a_48m_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11a_5g[10]);
+ AICWFDBG(LOGINFO, "%s:lvl_11a_54m_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11a_5g[11]);
+ AICWFDBG(LOGINFO, "%s:lvl_11n_11ac_mcs0_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11n_11ac_5g[0]);
+ AICWFDBG(LOGINFO, "%s:lvl_11n_11ac_mcs1_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11n_11ac_5g[1]);
+ AICWFDBG(LOGINFO, "%s:lvl_11n_11ac_mcs2_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11n_11ac_5g[2]);
+ AICWFDBG(LOGINFO, "%s:lvl_11n_11ac_mcs3_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11n_11ac_5g[3]);
+ AICWFDBG(LOGINFO, "%s:lvl_11n_11ac_mcs4_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11n_11ac_5g[4]);
+ AICWFDBG(LOGINFO, "%s:lvl_11n_11ac_mcs5_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11n_11ac_5g[5]);
+ AICWFDBG(LOGINFO, "%s:lvl_11n_11ac_mcs6_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11n_11ac_5g[6]);
+ AICWFDBG(LOGINFO, "%s:lvl_11n_11ac_mcs7_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11n_11ac_5g[7]);
+ AICWFDBG(LOGINFO, "%s:lvl_11n_11ac_mcs8_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11n_11ac_5g[8]);
+ AICWFDBG(LOGINFO, "%s:lvl_11n_11ac_mcs9_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11n_11ac_5g[9]);
+ AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs0_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11ax_5g[0]);
+ AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs1_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11ax_5g[1]);
+ AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs2_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11ax_5g[2]);
+ AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs3_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11ax_5g[3]);
+ AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs4_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11ax_5g[4]);
+ AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs5_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11ax_5g[5]);
+ AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs6_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11ax_5g[6]);
+ AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs7_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11ax_5g[7]);
+ AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs8_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11ax_5g[8]);
+ AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs9_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11ax_5g[9]);
+ AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs10_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11ax_5g[10]);
+ AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs11_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11ax_5g[11]);
+
+ txpwr_lvl_req->txpwr_lvl_v3 = *txpwr_lvl_v3;
+
+ /* Send the MM_SET_TXPWR_LVL_REQ message to UMAC FW */
+ error = rwnx_send_msg(rwnx_hw, txpwr_lvl_req, 1, MM_SET_TXPWR_IDX_LVL_CFM, NULL);
+
+ return (error);
+ }
+}
+
+extern void get_userconfig_txpwr_ofst(txpwr_ofst_conf_t *txpwr_ofst);
+
+int rwnx_send_txpwr_ofst_req(struct rwnx_hw *rwnx_hw)
+{
+ struct mm_set_txpwr_ofst_req *txpwr_ofst_req;
+ txpwr_ofst_conf_t *txpwr_ofst;
+ int error = 0;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ /* Build the MM_SET_TXPWR_OFST_REQ message */
+ txpwr_ofst_req = rwnx_msg_zalloc(MM_SET_TXPWR_OFST_REQ, TASK_MM, DRV_TASK_ID,
+ sizeof(struct mm_set_txpwr_ofst_req));
+
+ if (!txpwr_ofst_req) {
+ return -ENOMEM;
+ }
+
+ txpwr_ofst = &txpwr_ofst_req->txpwr_ofst;
+ txpwr_ofst->enable = 0;
+ txpwr_ofst->chan_1_4 = 0;
+ txpwr_ofst->chan_5_9 = 0;
+ txpwr_ofst->chan_10_13 = 0;
+ txpwr_ofst->chan_36_64 = 0;
+ txpwr_ofst->chan_100_120 = 0;
+ txpwr_ofst->chan_122_140 = 0;
+ txpwr_ofst->chan_142_165 = 0;
+ if(rwnx_hw->usbdev->chipid == PRODUCT_ID_AIC8801){
+ get_userconfig_txpwr_ofst(txpwr_ofst);
+ }else if(rwnx_hw->usbdev->chipid == PRODUCT_ID_AIC8800DC ||
+ rwnx_hw->usbdev->chipid == PRODUCT_ID_AIC8800DW ||
+ rwnx_hw->usbdev->chipid == PRODUCT_ID_AIC8800D81){
+ get_userconfig_txpwr_ofst_in_fdrv(txpwr_ofst);
+ }
+ if(txpwr_ofst->enable){
+
+ AICWFDBG(LOGINFO, "%s:enable:%d\r\n", __func__, txpwr_ofst->enable);
+ AICWFDBG(LOGINFO, "%s:chan_1_4:%d\r\n", __func__, txpwr_ofst->chan_1_4);
+ AICWFDBG(LOGINFO, "%s:chan_5_9:%d\r\n", __func__, txpwr_ofst->chan_5_9);
+ AICWFDBG(LOGINFO, "%s:chan_10_13:%d\r\n", __func__, txpwr_ofst->chan_10_13);
+ AICWFDBG(LOGINFO, "%s:chan_36_64:%d\r\n", __func__, txpwr_ofst->chan_36_64);
+ AICWFDBG(LOGINFO, "%s:chan_100_120:%d\r\n", __func__, txpwr_ofst->chan_100_120);
+ AICWFDBG(LOGINFO, "%s:chan_122_140:%d\r\n", __func__, txpwr_ofst->chan_122_140);
+ AICWFDBG(LOGINFO, "%s:chan_142_165:%d\r\n", __func__, txpwr_ofst->chan_142_165);
+
+ /* Send the MM_SET_TXPWR_OFST_REQ message to UMAC FW */
+ error = rwnx_send_msg(rwnx_hw, txpwr_ofst_req, 1, MM_SET_TXPWR_OFST_CFM, NULL);
+ }else{
+ AICWFDBG(LOGINFO, "%s:Do not use txpwr_ofst\r\n", __func__);
+ rwnx_msg_free(rwnx_hw, txpwr_ofst_req);
+ }
+
+ return (error);
+}
+
+int rwnx_send_set_filter(struct rwnx_hw *rwnx_hw, uint32_t filter)
+{
+ struct mm_set_filter_req *set_filter_req_param;
+ uint32_t rx_filter = 0;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ /* Build the MM_SET_FILTER_REQ message */
+ set_filter_req_param =
+ rwnx_msg_zalloc(MM_SET_FILTER_REQ, TASK_MM, DRV_TASK_ID,
+ sizeof(struct mm_set_filter_req));
+ if (!set_filter_req_param)
+ return -ENOMEM;
+
+ /* Set parameters for the MM_SET_FILTER_REQ message */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 2, 0)
+ if (filter & FIF_PROMISC_IN_BSS)
+ rx_filter |= NXMAC_ACCEPT_UNICAST_BIT;
+#endif
+ if (filter & FIF_ALLMULTI)
+ rx_filter |= NXMAC_ACCEPT_MULTICAST_BIT;
+
+ if (filter & (FIF_FCSFAIL | FIF_PLCPFAIL))
+ rx_filter |= NXMAC_ACCEPT_ERROR_FRAMES_BIT;
+
+ if (filter & FIF_BCN_PRBRESP_PROMISC)
+ rx_filter |= NXMAC_ACCEPT_OTHER_BSSID_BIT;
+
+ if (filter & FIF_CONTROL)
+ rx_filter |= NXMAC_ACCEPT_OTHER_CNTRL_FRAMES_BIT |
+ NXMAC_ACCEPT_CF_END_BIT |
+ NXMAC_ACCEPT_ACK_BIT |
+ NXMAC_ACCEPT_CTS_BIT |
+ NXMAC_ACCEPT_RTS_BIT |
+ NXMAC_ACCEPT_BA_BIT | NXMAC_ACCEPT_BAR_BIT;
+
+ if (filter & FIF_OTHER_BSS)
+ rx_filter |= NXMAC_ACCEPT_OTHER_BSSID_BIT;
+
+ if (filter & FIF_PSPOLL) {
+ /* TODO: check if the MAC filters apply to our BSSID or is general */
+ rx_filter |= NXMAC_ACCEPT_PS_POLL_BIT;
+ }
+
+ if (filter & FIF_PROBE_REQ) {
+ rx_filter |= NXMAC_ACCEPT_PROBE_REQ_BIT;
+ rx_filter |= NXMAC_ACCEPT_ALL_BEACON_BIT;
+ }
+
+ /* Add the filter flags that are set by default and cannot be changed here */
+ rx_filter |= RWNX_MAC80211_NOT_CHANGEABLE;
+
+ /* XXX */
+ //if (ieee80211_hw_check(rwnx_hw->hw, AMPDU_AGGREGATION))
+ rx_filter |= NXMAC_ACCEPT_BA_BIT;
+
+ /* Now copy all the flags into the message parameter */
+ set_filter_req_param->filter = rx_filter;
+
+ RWNX_DBG("new total_flags = 0x%08x\nrx filter set to 0x%08x\n",
+ filter, rx_filter);
+
+ /* Send the MM_SET_FILTER_REQ message to LMAC FW */
+ return rwnx_send_msg(rwnx_hw, set_filter_req_param, 1, MM_SET_FILTER_CFM, NULL);
+}
+
+/******************************************************************************
+ * Control messages handling functions (FULLMAC only)
+ *****************************************************************************/
+#ifdef CONFIG_RWNX_FULLMAC
+#ifdef CONFIG_HE_FOR_OLD_KERNEL
+extern struct ieee80211_sband_iftype_data rwnx_he_capa;
+#endif
+#ifdef CONFIG_VHT_FOR_OLD_KERNEL
+static struct ieee80211_sta_vht_cap* rwnx_vht_capa;
+#endif
+int rwnx_send_me_config_req(struct rwnx_hw *rwnx_hw)
+{
+ struct me_config_req *req;
+ struct wiphy *wiphy = rwnx_hw->wiphy;
+
+ //#ifdef USE_5G
+ //struct ieee80211_sta_ht_cap *ht_cap = &wiphy->bands[NL80211_BAND_5GHZ]->ht_cap;
+ //struct ieee80211_sta_vht_cap *vht_cap = &wiphy->bands[NL80211_BAND_5GHZ]->vht_cap;
+ //#else
+ //struct ieee80211_sta_ht_cap *ht_cap = &wiphy->bands[NL80211_BAND_2GHZ]->ht_cap;
+ //struct ieee80211_sta_vht_cap *vht_cap = &wiphy->bands[NL80211_BAND_2GHZ]->vht_cap;
+ //#endif
+ struct ieee80211_sta_ht_cap *ht_cap;
+ #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0) || defined(CONFIG_VHT_FOR_OLD_KERNEL)
+ struct ieee80211_sta_vht_cap *vht_cap;
+ #endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)
+ struct ieee80211_sta_he_cap const *he_cap;
+#else
+ #ifdef CONFIG_HE_FOR_OLD_KERNEL
+ struct ieee80211_sta_he_cap const *he_cap;
+ #endif
+#endif
+ //uint8_t *ht_mcs = (uint8_t *)&ht_cap->mcs;
+ uint8_t *ht_mcs;
+ int i;
+
+ if (rwnx_hw->band_5g_support) {
+ ht_cap = &wiphy->bands[NL80211_BAND_5GHZ]->ht_cap;
+ #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0) || defined(CONFIG_VHT_FOR_OLD_KERNEL)
+ vht_cap = &wiphy->bands[NL80211_BAND_5GHZ]->vht_cap;
+ #endif
+ } else {
+ ht_cap = &wiphy->bands[NL80211_BAND_2GHZ]->ht_cap;
+ #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0) || defined(CONFIG_VHT_FOR_OLD_KERNEL)
+ vht_cap = &wiphy->bands[NL80211_BAND_2GHZ]->vht_cap;
+ #endif
+ }
+ #ifdef CONFIG_VHT_FOR_OLD_KERNEL
+ rwnx_vht_capa = vht_cap;
+ #endif
+
+ ht_mcs = (uint8_t *)&ht_cap->mcs;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ /* Build the ME_CONFIG_REQ message */
+ req = rwnx_msg_zalloc(ME_CONFIG_REQ, TASK_ME, DRV_TASK_ID,
+ sizeof(struct me_config_req));
+ if (!req)
+ return -ENOMEM;
+
+ /* Set parameters for the ME_CONFIG_REQ message */
+ req->ht_supp = ht_cap->ht_supported;
+ #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0) || defined(CONFIG_VHT_FOR_OLD_KERNEL)
+ req->vht_supp = vht_cap->vht_supported;
+ #endif
+ req->ht_cap.ht_capa_info = cpu_to_le16(ht_cap->cap | IEEE80211_HT_CAP_LDPC_CODING);
+ req->ht_cap.a_mpdu_param = ht_cap->ampdu_factor |
+ (ht_cap->ampdu_density <<
+ IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT);
+ for (i = 0; i < sizeof(ht_cap->mcs); i++)
+ req->ht_cap.mcs_rate[i] = ht_mcs[i];
+ req->ht_cap.ht_extended_capa = 0;
+ req->ht_cap.tx_beamforming_capa = 0;
+ req->ht_cap.asel_capa = 0;
+
+ #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0) || defined(CONFIG_VHT_FOR_OLD_KERNEL)
+ if(req->vht_supp) {
+ req->vht_cap.vht_capa_info = cpu_to_le32(vht_cap->cap);
+ req->vht_cap.rx_highest = cpu_to_le16(vht_cap->vht_mcs.rx_highest);
+ req->vht_cap.rx_mcs_map = cpu_to_le16(vht_cap->vht_mcs.rx_mcs_map);
+ req->vht_cap.tx_highest = cpu_to_le16(vht_cap->vht_mcs.tx_highest);
+ req->vht_cap.tx_mcs_map = cpu_to_le16(vht_cap->vht_mcs.tx_mcs_map);
+ }
+ #endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0) || defined(CONFIG_HE_FOR_OLD_KERNEL)
+ #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)
+ if (wiphy->bands[NL80211_BAND_2GHZ]->iftype_data != NULL) {
+ he_cap = &wiphy->bands[NL80211_BAND_2GHZ]->iftype_data->he_cap;
+ #endif
+ #if defined(CONFIG_HE_FOR_OLD_KERNEL)
+ if (1) {
+ he_cap = &rwnx_he_capa.he_cap;
+ #endif
+
+ req->he_supp = he_cap->has_he;
+
+ for (i = 0; i < ARRAY_SIZE(he_cap->he_cap_elem.mac_cap_info); i++) {
+ req->he_cap.mac_cap_info[i] = he_cap->he_cap_elem.mac_cap_info[i];
+ }
+ for (i = 0; i < ARRAY_SIZE(he_cap->he_cap_elem.phy_cap_info); i++) {
+ req->he_cap.phy_cap_info[i] = he_cap->he_cap_elem.phy_cap_info[i];
+ }
+ req->he_cap.mcs_supp.rx_mcs_80 = cpu_to_le16(he_cap->he_mcs_nss_supp.rx_mcs_80);
+ req->he_cap.mcs_supp.tx_mcs_80 = cpu_to_le16(he_cap->he_mcs_nss_supp.tx_mcs_80);
+ req->he_cap.mcs_supp.rx_mcs_160 = cpu_to_le16(he_cap->he_mcs_nss_supp.rx_mcs_160);
+ req->he_cap.mcs_supp.tx_mcs_160 = cpu_to_le16(he_cap->he_mcs_nss_supp.tx_mcs_160);
+ req->he_cap.mcs_supp.rx_mcs_80p80 = cpu_to_le16(he_cap->he_mcs_nss_supp.rx_mcs_80p80);
+ req->he_cap.mcs_supp.tx_mcs_80p80 = cpu_to_le16(he_cap->he_mcs_nss_supp.tx_mcs_80p80);
+ for (i = 0; i < MAC_HE_PPE_THRES_MAX_LEN; i++) {
+ req->he_cap.ppe_thres[i] = he_cap->ppe_thres[i];
+ }
+ req->he_ul_on = rwnx_hw->mod_params->he_ul_on;
+ }
+
+#else
+ req->he_supp = false;
+ req->he_ul_on = false;
+#endif
+ req->ps_on = rwnx_hw->mod_params->ps_on;
+ req->dpsm = rwnx_hw->mod_params->dpsm;
+ req->tx_lft = rwnx_hw->mod_params->tx_lft;
+ req->ant_div_on = rwnx_hw->mod_params->ant_div;
+
+ if (rwnx_hw->mod_params->use_80){
+ req->phy_bw_max = PHY_CHNL_BW_80;
+ }else if (rwnx_hw->mod_params->use_2040){
+ req->phy_bw_max = PHY_CHNL_BW_40;
+ }else{
+ req->phy_bw_max = PHY_CHNL_BW_20;
+ }
+
+#if 0
+ if(!rwnx_hw->he_flag){
+ req->he_supp = false;
+ req->he_ul_on = false;
+ }
+
+ wiphy_info(wiphy, "HT supp %d, VHT supp %d, HE supp %d\n", req->ht_supp,
+ req->vht_supp,
+ req->he_supp);
+#endif
+
+ AICWFDBG(LOGINFO, "HT supp %d, VHT supp %d, HE supp %d\n", req->ht_supp,
+ req->vht_supp,
+ req->he_supp);
+
+ /* Send the ME_CONFIG_REQ message to LMAC FW */
+ return rwnx_send_msg(rwnx_hw, req, 1, ME_CONFIG_CFM, NULL);
+}
+int rwnx_send_me_chan_config_req(struct rwnx_hw *rwnx_hw)
+{
+ struct me_chan_config_req *req;
+ struct wiphy *wiphy = rwnx_hw->wiphy;
+ int i;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ /* Build the ME_CHAN_CONFIG_REQ message */
+ req = rwnx_msg_zalloc(ME_CHAN_CONFIG_REQ, TASK_ME, DRV_TASK_ID,
+ sizeof(struct me_chan_config_req));
+ if (!req)
+ return -ENOMEM;
+
+ req->chan2G4_cnt= 0;
+ if (wiphy->bands[NL80211_BAND_2GHZ] != NULL) {
+ struct ieee80211_supported_band *b = wiphy->bands[NL80211_BAND_2GHZ];
+ for (i = 0; i < b->n_channels; i++) {
+ req->chan2G4[req->chan2G4_cnt].flags = 0;
+ if (b->channels[i].flags & IEEE80211_CHAN_DISABLED)
+ req->chan2G4[req->chan2G4_cnt].flags |= CHAN_DISABLED;
+ req->chan2G4[req->chan2G4_cnt].flags |= get_chan_flags(b->channels[i].flags);
+ req->chan2G4[req->chan2G4_cnt].band = NL80211_BAND_2GHZ;
+ req->chan2G4[req->chan2G4_cnt].freq = b->channels[i].center_freq;
+ req->chan2G4[req->chan2G4_cnt].tx_power = chan_to_fw_pwr(b->channels[i].max_power);
+ req->chan2G4_cnt++;
+ if (req->chan2G4_cnt == MAC_DOMAINCHANNEL_24G_MAX)
+ break;
+ }
+ }
+
+ req->chan5G_cnt = 0;
+ if (wiphy->bands[NL80211_BAND_5GHZ] != NULL) {
+ struct ieee80211_supported_band *b = wiphy->bands[NL80211_BAND_5GHZ];
+ for (i = 0; i < b->n_channels; i++) {
+ req->chan5G[req->chan5G_cnt].flags = 0;
+ if (b->channels[i].flags & IEEE80211_CHAN_DISABLED)
+ req->chan5G[req->chan5G_cnt].flags |= CHAN_DISABLED;
+ req->chan5G[req->chan5G_cnt].flags |= get_chan_flags(b->channels[i].flags);
+ req->chan5G[req->chan5G_cnt].band = NL80211_BAND_5GHZ;
+ req->chan5G[req->chan5G_cnt].freq = b->channels[i].center_freq;
+ req->chan5G[req->chan5G_cnt].tx_power = chan_to_fw_pwr(b->channels[i].max_power);
+ req->chan5G_cnt++;
+ if (req->chan5G_cnt == MAC_DOMAINCHANNEL_5G_MAX)
+ break;
+ }
+ }
+
+ /* Send the ME_CHAN_CONFIG_REQ message to LMAC FW */
+ return rwnx_send_msg(rwnx_hw, req, 1, ME_CHAN_CONFIG_CFM, NULL);
+}
+
+int rwnx_send_me_set_control_port_req(struct rwnx_hw *rwnx_hw, bool opened, u8 sta_idx)
+{
+ struct me_set_control_port_req *req;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ /* Build the ME_SET_CONTROL_PORT_REQ message */
+ req = rwnx_msg_zalloc(ME_SET_CONTROL_PORT_REQ, TASK_ME, DRV_TASK_ID,
+ sizeof(struct me_set_control_port_req));
+ if (!req)
+ return -ENOMEM;
+
+ /* Set parameters for the ME_SET_CONTROL_PORT_REQ message */
+ req->sta_idx = sta_idx;
+ req->control_port_open = opened;
+
+ /* Send the ME_SET_CONTROL_PORT_REQ message to LMAC FW */
+ return rwnx_send_msg(rwnx_hw, req, 1, ME_SET_CONTROL_PORT_CFM, NULL);
+}
+
+int rwnx_send_me_sta_add(struct rwnx_hw *rwnx_hw, struct station_parameters *params,
+ const u8 *mac, u8 inst_nbr, struct me_sta_add_cfm *cfm)
+{
+ struct me_sta_add_req *req;
+
+#if LINUX_VERSION_CODE < HIGH_KERNEL_VERSION
+ u8 *ht_mcs = (u8 *)&params->ht_capa->mcs;
+#else
+ u8 *ht_mcs = (u8 *)&params->link_sta_params.ht_capa->mcs;
+#endif//HIGH_KERNEL_VERSION
+
+ int i;
+ struct rwnx_vif *rwnx_vif = rwnx_hw->vif_table[inst_nbr];
+ #if (defined CONFIG_HE_FOR_OLD_KERNEL) || (defined CONFIG_VHT_FOR_OLD_KERNEL)
+ struct aic_sta *sta = &rwnx_hw->aic_table[rwnx_vif->ap.aic_index];
+ printk("assoc_req idx %d, he: %d, vht: %d\n ", rwnx_vif->ap.aic_index, sta->he, sta->vht);
+ if (rwnx_vif->ap.aic_index < NX_REMOTE_STA_MAX + NX_VIRT_DEV_MAX)
+ rwnx_vif->ap.aic_index++;
+ else
+ rwnx_vif->ap.aic_index = 0;
+ #endif
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ /* Build the MM_STA_ADD_REQ message */
+ req = rwnx_msg_zalloc(ME_STA_ADD_REQ, TASK_ME, DRV_TASK_ID,
+ sizeof(struct me_sta_add_req));
+ if (!req)
+ return -ENOMEM;
+
+ /* Set parameters for the MM_STA_ADD_REQ message */
+ memcpy(&(req->mac_addr.array[0]), mac, ETH_ALEN);
+
+#if LINUX_VERSION_CODE < HIGH_KERNEL_VERSION
+ req->rate_set.length = params->supported_rates_len;
+#else
+ req->rate_set.length = params->link_sta_params.supported_rates_len;
+#endif//LINUX_VERSION_CODE < HIGH_KERNEL_VERSION
+
+ for (i = 0; i < req->rate_set.length; i++){
+#if LINUX_VERSION_CODE < HIGH_KERNEL_VERSION
+ req->rate_set.array[i] = params->supported_rates[i];
+#else
+ req->rate_set.array[i] = params->link_sta_params.supported_rates[i];
+#endif//LINUX_VERSION_CODE < HIGH_KERNEL_VERSION
+ }
+
+ req->flags = 0;
+
+#if LINUX_VERSION_CODE < HIGH_KERNEL_VERSION
+ if (params->ht_capa) {
+ const struct ieee80211_ht_cap *ht_capa = params->ht_capa;
+#else
+ if (params->link_sta_params.ht_capa) {
+ const struct ieee80211_ht_cap *ht_capa = params->link_sta_params.ht_capa;
+#endif//LINUX_VERSION_CODE < HIGH_KERNEL_VERSION
+
+
+ req->flags |= STA_HT_CAPA;
+ req->ht_cap.ht_capa_info = cpu_to_le16(ht_capa->cap_info);
+ req->ht_cap.a_mpdu_param = ht_capa->ampdu_params_info;
+ for (i = 0; i < sizeof(ht_capa->mcs); i++)
+ req->ht_cap.mcs_rate[i] = ht_mcs[i];
+ req->ht_cap.ht_extended_capa = cpu_to_le16(ht_capa->extended_ht_cap_info);
+ req->ht_cap.tx_beamforming_capa = cpu_to_le32(ht_capa->tx_BF_cap_info);
+ req->ht_cap.asel_capa = ht_capa->antenna_selection_info;
+ }
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
+#if LINUX_VERSION_CODE < HIGH_KERNEL_VERSION
+ if (params->vht_capa) {
+ const struct ieee80211_vht_cap *vht_capa = params->vht_capa;
+#else
+ if (params->link_sta_params.vht_capa) {
+ const struct ieee80211_vht_cap *vht_capa = params->link_sta_params.vht_capa;
+#endif//LINUX_VERSION_CODE < HIGH_KERNEL_VERSION
+ req->flags |= STA_VHT_CAPA;
+ req->vht_cap.vht_capa_info = cpu_to_le32(vht_capa->vht_cap_info);
+ req->vht_cap.rx_highest = cpu_to_le16(vht_capa->supp_mcs.rx_highest);
+ req->vht_cap.rx_mcs_map = cpu_to_le16(vht_capa->supp_mcs.rx_mcs_map);
+ req->vht_cap.tx_highest = cpu_to_le16(vht_capa->supp_mcs.tx_highest);
+ req->vht_cap.tx_mcs_map = cpu_to_le16(vht_capa->supp_mcs.tx_mcs_map);
+ }
+#elif defined(CONFIG_VHT_FOR_OLD_KERNEL)
+ if (sta->vht) {
+ const struct ieee80211_vht_cap *vht_capa = rwnx_vht_capa;
+
+ req->flags |= STA_VHT_CAPA;
+ req->vht_cap.vht_capa_info = cpu_to_le32(vht_capa->vht_cap_info);
+ req->vht_cap.rx_highest = cpu_to_le16(vht_capa->supp_mcs.rx_highest);
+ req->vht_cap.rx_mcs_map = cpu_to_le16(vht_capa->supp_mcs.rx_mcs_map);
+ req->vht_cap.tx_highest = cpu_to_le16(vht_capa->supp_mcs.tx_highest);
+ req->vht_cap.tx_mcs_map = cpu_to_le16(vht_capa->supp_mcs.tx_mcs_map);
+ }
+#endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0)
+#if LINUX_VERSION_CODE < HIGH_KERNEL_VERSION
+ if (params->he_capa) {
+ const struct ieee80211_he_cap_elem *he_capa = params->he_capa;
+#else
+ if (params->link_sta_params.he_capa) {
+ const struct ieee80211_he_cap_elem *he_capa = params->link_sta_params.he_capa;
+#endif//LINUX_VERSION_CODE < HIGH_KERNEL_VERSION
+ struct ieee80211_he_mcs_nss_supp *mcs_nss_supp =
+ (struct ieee80211_he_mcs_nss_supp *)(he_capa + 1);
+
+ req->flags |= STA_HE_CAPA;
+ for (i = 0; i < ARRAY_SIZE(he_capa->mac_cap_info); i++) {
+ req->he_cap.mac_cap_info[i] = he_capa->mac_cap_info[i];
+ }
+ for (i = 0; i < ARRAY_SIZE(he_capa->phy_cap_info); i++) {
+ req->he_cap.phy_cap_info[i] = he_capa->phy_cap_info[i];
+ }
+ req->he_cap.mcs_supp.rx_mcs_80 = mcs_nss_supp->rx_mcs_80;
+ req->he_cap.mcs_supp.tx_mcs_80 = mcs_nss_supp->tx_mcs_80;
+ req->he_cap.mcs_supp.rx_mcs_160 = mcs_nss_supp->rx_mcs_160;
+ req->he_cap.mcs_supp.tx_mcs_160 = mcs_nss_supp->tx_mcs_160;
+ req->he_cap.mcs_supp.rx_mcs_80p80 = mcs_nss_supp->rx_mcs_80p80;
+ req->he_cap.mcs_supp.tx_mcs_80p80 = mcs_nss_supp->tx_mcs_80p80;
+ }
+#else
+ #ifdef CONFIG_HE_FOR_OLD_KERNEL
+ if (sta->he) {
+ const struct ieee80211_he_cap_elem *he_capa = &rwnx_he_capa.he_cap.he_cap_elem;
+ struct ieee80211_he_mcs_nss_supp *mcs_nss_supp =
+ (struct ieee80211_he_mcs_nss_supp *)(he_capa + 1);
+ req->flags |= STA_HE_CAPA;
+ for (i = 0; i < ARRAY_SIZE(he_capa->mac_cap_info); i++) {
+ req->he_cap.mac_cap_info[i] = he_capa->mac_cap_info[i];
+ }
+ for (i = 0; i < ARRAY_SIZE(he_capa->phy_cap_info); i++) {
+ req->he_cap.phy_cap_info[i] = he_capa->phy_cap_info[i];
+ }
+ req->he_cap.mcs_supp.rx_mcs_80 = mcs_nss_supp->rx_mcs_80;
+ req->he_cap.mcs_supp.tx_mcs_80 = mcs_nss_supp->tx_mcs_80;
+ req->he_cap.mcs_supp.rx_mcs_160 = mcs_nss_supp->rx_mcs_160;
+ req->he_cap.mcs_supp.tx_mcs_160 = mcs_nss_supp->tx_mcs_160;
+ req->he_cap.mcs_supp.rx_mcs_80p80 = mcs_nss_supp->rx_mcs_80p80;
+ req->he_cap.mcs_supp.tx_mcs_80p80 = mcs_nss_supp->tx_mcs_80p80;
+ }
+ #endif
+#endif
+
+ if (params->sta_flags_set & BIT(NL80211_STA_FLAG_WME))
+ req->flags |= STA_QOS_CAPA;
+
+ if (params->sta_flags_set & BIT(NL80211_STA_FLAG_MFP))
+ req->flags |= STA_MFP_CAPA;
+
+ #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
+#if LINUX_VERSION_CODE < HIGH_KERNEL_VERSION
+ if (params->opmode_notif_used) {
+ req->opmode = params->opmode_notif;
+#else
+ if (params->link_sta_params.opmode_notif_used) {
+ req->opmode = params->link_sta_params.opmode_notif;
+#endif//LINUX_VERSION_CODE < HIGH_KERNEL_VERSION
+ req->flags |= STA_OPMOD_NOTIF;
+ }
+ #endif
+
+ req->aid = cpu_to_le16(params->aid);
+ req->uapsd_queues = params->uapsd_queues;
+ req->max_sp_len = params->max_sp * 2;
+ req->vif_idx = inst_nbr;
+
+ if (params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) {
+ //struct rwnx_vif *rwnx_vif = rwnx_hw->vif_table[inst_nbr];
+ req->tdls_sta = true;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
+ if ((params->ext_capab[3] & WLAN_EXT_CAPA4_TDLS_CHAN_SWITCH) &&
+ !rwnx_vif->tdls_chsw_prohibited)
+ req->tdls_chsw_allowed = true;
+#endif
+ if (rwnx_vif->tdls_status == TDLS_SETUP_RSP_TX)
+ req->tdls_sta_initiator = true;
+ }
+
+ /* Send the ME_STA_ADD_REQ message to LMAC FW */
+ return rwnx_send_msg(rwnx_hw, req, 1, ME_STA_ADD_CFM, cfm);
+}
+
+int rwnx_send_me_sta_del(struct rwnx_hw *rwnx_hw, u8 sta_idx, bool tdls_sta)
+{
+ struct me_sta_del_req *req;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ /* Build the MM_STA_DEL_REQ message */
+ req = rwnx_msg_zalloc(ME_STA_DEL_REQ, TASK_ME, DRV_TASK_ID,
+ sizeof(struct me_sta_del_req));
+ if (!req)
+ return -ENOMEM;
+
+ /* Set parameters for the MM_STA_DEL_REQ message */
+ req->sta_idx = sta_idx;
+ req->tdls_sta = tdls_sta;
+
+ /* Send the ME_STA_DEL_REQ message to LMAC FW */
+ return rwnx_send_msg(rwnx_hw, req, 1, ME_STA_DEL_CFM, NULL);
+}
+
+int rwnx_send_me_traffic_ind(struct rwnx_hw *rwnx_hw, u8 sta_idx, bool uapsd, u8 tx_status)
+{
+ struct me_traffic_ind_req *req;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ /* Build the ME_UTRAFFIC_IND_REQ message */
+ req = rwnx_msg_zalloc(ME_TRAFFIC_IND_REQ, TASK_ME, DRV_TASK_ID,
+ sizeof(struct me_traffic_ind_req));
+ if (!req)
+ return -ENOMEM;
+
+ /* Set parameters for the ME_TRAFFIC_IND_REQ message */
+ req->sta_idx = sta_idx;
+ req->tx_avail = tx_status;
+ req->uapsd = uapsd;
+
+ /* Send the ME_TRAFFIC_IND_REQ to UMAC FW */
+ return rwnx_send_msg(rwnx_hw, req, 1, ME_TRAFFIC_IND_CFM, NULL);
+}
+
+int rwnx_send_me_rc_stats(struct rwnx_hw *rwnx_hw,
+ u8 sta_idx,
+ struct me_rc_stats_cfm *cfm)
+{
+ struct me_rc_stats_req *req;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ /* Build the ME_RC_STATS_REQ message */
+ req = rwnx_msg_zalloc(ME_RC_STATS_REQ, TASK_ME, DRV_TASK_ID,
+ sizeof(struct me_rc_stats_req));
+ if (!req)
+ return -ENOMEM;
+
+ /* Set parameters for the ME_RC_STATS_REQ message */
+ req->sta_idx = sta_idx;
+
+ /* Send the ME_RC_STATS_REQ message to LMAC FW */
+ return rwnx_send_msg(rwnx_hw, req, 1, ME_RC_STATS_CFM, cfm);
+}
+
+int rwnx_send_me_rc_set_rate(struct rwnx_hw *rwnx_hw,
+ u8 sta_idx,
+ u16 rate_cfg)
+{
+ struct me_rc_set_rate_req *req;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ /* Build the ME_RC_SET_RATE_REQ message */
+ req = rwnx_msg_zalloc(ME_RC_SET_RATE_REQ, TASK_ME, DRV_TASK_ID,
+ sizeof(struct me_rc_set_rate_req));
+ if (!req)
+ return -ENOMEM;
+
+ /* Set parameters for the ME_RC_SET_RATE_REQ message */
+ req->sta_idx = sta_idx;
+ req->fixed_rate_cfg = rate_cfg;
+
+ /* Send the ME_RC_SET_RATE_REQ message to FW */
+ return rwnx_send_msg(rwnx_hw, req, 0, 0, NULL);
+}
+
+int rwnx_send_me_set_ps_mode(struct rwnx_hw *rwnx_hw, u8 ps_mode)
+{
+ struct me_set_ps_mode_req *req;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ /* Build the ME_SET_PS_MODE_REQ message */
+ req = rwnx_msg_zalloc(ME_SET_PS_MODE_REQ, TASK_ME, DRV_TASK_ID,
+ sizeof(struct me_set_ps_mode_req));
+ if (!req)
+ return -ENOMEM;
+
+ /* Set parameters for the ME_SET_PS_MODE_REQ message */
+ req->ps_state = ps_mode;
+
+ /* Send the ME_SET_PS_MODE_REQ message to FW */
+ return rwnx_send_msg(rwnx_hw, req, 1, ME_SET_PS_MODE_CFM, NULL);
+}
+
+int rwnx_send_me_set_lp_level(struct rwnx_hw *rwnx_hw, u8 lp_level)
+{
+ struct me_set_lp_level_req *req;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ /* Build the ME_SET_LP_LEVEL_REQ message */
+ req = rwnx_msg_zalloc(ME_SET_LP_LEVEL_REQ, TASK_ME, DRV_TASK_ID,
+ sizeof(struct me_set_lp_level_req));
+ if (!req)
+ return -ENOMEM;
+
+ /* Set parameters for the ME_SET_LP_LEVEL_REQ message */
+ req->lp_level = lp_level;
+
+ /* Send the ME_SET_LP_LEVEL_REQ message to FW */
+ return rwnx_send_msg(rwnx_hw, req, 1, ME_SET_LP_LEVEL_CFM, NULL);
+}
+
+int rwnx_send_sm_connect_req(struct rwnx_hw *rwnx_hw,
+ struct rwnx_vif *rwnx_vif,
+ struct cfg80211_connect_params *sme,
+ struct sm_connect_cfm *cfm)
+{
+ struct sm_connect_req *req;
+ int i;
+ u32_l flags = 0;
+ bool gval = false;
+ bool pval = false;
+
+ rwnx_vif->wep_enabled = false;
+ rwnx_vif->wep_auth_err = false;
+ rwnx_vif->last_auth_type = 0;
+
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ /* Build the SM_CONNECT_REQ message */
+ req = rwnx_msg_zalloc(SM_CONNECT_REQ, TASK_SM, DRV_TASK_ID,
+ sizeof(struct sm_connect_req));
+ if (!req)
+ return -ENOMEM;
+
+ if ((sme->crypto.cipher_group == WLAN_CIPHER_SUITE_WEP40) ||
+ (sme->crypto.cipher_group == WLAN_CIPHER_SUITE_WEP104)) {
+ gval = true;
+ }
+
+ if (sme->crypto.n_ciphers_pairwise &&
+ ((sme->crypto.ciphers_pairwise[0] == WLAN_CIPHER_SUITE_WEP40) ||
+ (sme->crypto.ciphers_pairwise[0] == WLAN_CIPHER_SUITE_WEP104))) {
+ pval = true;
+ }
+
+ /* Set parameters for the SM_CONNECT_REQ message */
+ if (sme->crypto.n_ciphers_pairwise &&
+ ((sme->crypto.ciphers_pairwise[0] == WLAN_CIPHER_SUITE_WEP40) ||
+ (sme->crypto.ciphers_pairwise[0] == WLAN_CIPHER_SUITE_TKIP) ||
+ (sme->crypto.ciphers_pairwise[0] == WLAN_CIPHER_SUITE_WEP104)))
+ flags |= DISABLE_HT;
+
+ if (sme->crypto.control_port)
+ flags |= CONTROL_PORT_HOST;
+
+ if (sme->crypto.control_port_no_encrypt)
+ flags |= CONTROL_PORT_NO_ENC;
+
+ if (use_pairwise_key(&sme->crypto))
+ flags |= WPA_WPA2_IN_USE;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0) || defined (CONFIG_WPA3_FOR_OLD_KERNEL)
+ if (sme->mfp == NL80211_MFP_REQUIRED)
+ flags |= MFP_IN_USE;
+#endif
+ if (rwnx_vif->sta.ap)
+ flags |= REASSOCIATION;
+
+ req->ctrl_port_ethertype = sme->crypto.control_port_ethertype;
+
+ if (sme->bssid)
+ memcpy(&req->bssid, sme->bssid, ETH_ALEN);
+ else
+ req->bssid = mac_addr_bcst;
+ req->vif_idx = rwnx_vif->vif_index;
+ if (sme->channel) {
+ req->chan.band = sme->channel->band;
+ req->chan.freq = sme->channel->center_freq;
+ req->chan.flags = get_chan_flags(sme->channel->flags);
+ } else {
+ req->chan.freq = (u16_l)-1;
+ }
+ for (i = 0; i < sme->ssid_len; i++)
+ req->ssid.array[i] = sme->ssid[i];
+ req->ssid.length = sme->ssid_len;
+
+ req->flags = flags;
+ if (WARN_ON(sme->ie_len > sizeof(req->ie_buf)))
+ goto invalid_param;
+ if (sme->ie_len)
+ memcpy(req->ie_buf, sme->ie, sme->ie_len);
+ req->ie_len = sme->ie_len;
+ req->listen_interval = rwnx_mod_params.listen_itv;
+ req->dont_wait_bcmc = !rwnx_mod_params.listen_bcmc;
+
+ /* Set auth_type */
+ if (sme->auth_type == NL80211_AUTHTYPE_AUTOMATIC)
+ req->auth_type = WLAN_AUTH_OPEN;
+ else if (sme->auth_type == NL80211_AUTHTYPE_OPEN_SYSTEM)
+ req->auth_type = WLAN_AUTH_OPEN;
+ else if (sme->auth_type == NL80211_AUTHTYPE_SHARED_KEY)
+ req->auth_type = WLAN_AUTH_SHARED_KEY;
+ else if (sme->auth_type == NL80211_AUTHTYPE_FT)
+ req->auth_type = WLAN_AUTH_FT;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) || defined (CONFIG_WPA3_FOR_OLD_KERNEL)
+ else if (sme->auth_type == NL80211_AUTHTYPE_SAE)
+ req->auth_type = WLAN_AUTH_SAE;
+#endif
+ else
+ goto invalid_param;
+
+ /* Set UAPSD queues */
+ req->uapsd_queues = rwnx_mod_params.uapsd_queues;
+
+ rwnx_vif->wep_enabled = pval & gval;
+
+ if (rwnx_vif->wep_enabled) {
+ rwnx_vif->last_auth_type = sme->auth_type;
+ }
+#ifdef CONFIG_USE_WIRELESS_EXT
+ memset(rwnx_hw->wext_essid, 0, 32);
+ memcpy(rwnx_hw->wext_essid, sme->ssid, (int)sme->ssid_len);
+#endif
+
+ rwnx_vif->sta.ssid_len = (int)sme->ssid_len;
+ memset(rwnx_vif->sta.ssid, 0, rwnx_vif->sta.ssid_len + 1);
+ memcpy(rwnx_vif->sta.ssid, sme->ssid, rwnx_vif->sta.ssid_len);
+ memcpy(rwnx_vif->sta.bssid, sme->bssid, ETH_ALEN);
+
+ AICWFDBG(LOGINFO, "%s drv_vif_index:%d connect to %s(%d) channel:%d auth_type:%d\r\n",
+ __func__,
+ rwnx_vif->drv_vif_index,
+ rwnx_vif->sta.ssid,
+ rwnx_vif->sta.ssid_len,
+ req->chan.freq,
+ req->auth_type);
+
+ /* Send the SM_CONNECT_REQ message to LMAC FW */
+ return rwnx_send_msg(rwnx_hw, req, 1, SM_CONNECT_CFM, cfm);
+
+invalid_param:
+ rwnx_msg_free(rwnx_hw, req);
+ return -EINVAL;
+}
+
+int rwnx_send_sm_disconnect_req(struct rwnx_hw *rwnx_hw,
+ struct rwnx_vif *rwnx_vif,
+ u16 reason)
+{
+ struct sm_disconnect_req *req;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ /* Build the SM_DISCONNECT_REQ message */
+ req = rwnx_msg_zalloc(SM_DISCONNECT_REQ, TASK_SM, DRV_TASK_ID,
+ sizeof(struct sm_disconnect_req));
+ if (!req)
+ return -ENOMEM;
+
+ /* Set parameters for the SM_DISCONNECT_REQ message */
+ req->reason_code = reason;
+ req->vif_idx = rwnx_vif->vif_index;
+
+ /* Send the SM_DISCONNECT_REQ message to LMAC FW */
+ return rwnx_send_msg(rwnx_hw, req, 1, SM_DISCONNECT_CFM, NULL);
+}
+
+int rwnx_send_sm_external_auth_required_rsp(struct rwnx_hw *rwnx_hw,
+ struct rwnx_vif *rwnx_vif,
+ u16 status)
+{
+ struct sm_external_auth_required_rsp *rsp;
+
+ /* Build the SM_EXTERNAL_AUTH_CFM message */
+ rsp = rwnx_msg_zalloc(SM_EXTERNAL_AUTH_REQUIRED_RSP, TASK_SM, DRV_TASK_ID,
+ sizeof(struct sm_external_auth_required_rsp));
+ if (!rsp)
+ return -ENOMEM;
+
+ rsp->status = status;
+ rsp->vif_idx = rwnx_vif->vif_index;
+
+ /* send the SM_EXTERNAL_AUTH_REQUIRED_RSP message UMAC FW */
+ return rwnx_send_msg(rwnx_hw, rsp, 0, 0, NULL);
+}
+
+int rwnx_send_apm_start_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *vif,
+ struct cfg80211_ap_settings *settings,
+ struct apm_start_cfm *cfm,
+ struct rwnx_ipc_elem_var *elem)
+{
+ struct apm_start_req *req;
+ struct rwnx_bcn *bcn = &vif->ap.bcn;
+ u8 *buf;
+ u32 flags = 0;
+ const u8 *rate_ie;
+ u8 rate_len = 0;
+ int var_offset = offsetof(struct ieee80211_mgmt, u.beacon.variable);
+ const u8 *var_pos;
+ int len, i;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ /* Build the APM_START_REQ message */
+ req = rwnx_msg_zalloc(APM_START_REQ, TASK_APM, DRV_TASK_ID,
+ sizeof(struct apm_start_req));
+ if (!req)
+ return -ENOMEM;
+
+ // Build the beacon
+ bcn->dtim = (u8)settings->dtim_period;
+ buf = rwnx_build_bcn(bcn, &settings->beacon);
+ if (!buf) {
+ rwnx_msg_free(rwnx_hw, req);
+ return -ENOMEM;
+ }
+
+ // Retrieve the basic rate set from the beacon buffer
+ len = bcn->len - var_offset;
+ var_pos = buf + var_offset;
+
+// Assume that rate higher that 54 Mbps are BSS membership
+#define IS_BASIC_RATE(r) (r & 0x80) && ((r & ~0x80) <= (54 * 2))
+
+ rate_ie = cfg80211_find_ie(WLAN_EID_SUPP_RATES, var_pos, len);
+ if (rate_ie) {
+ const u8 *rates = rate_ie + 2;
+ for (i = 0; (i < rate_ie[1]) && (rate_len < MAC_RATESET_LEN); i++) {
+ if (IS_BASIC_RATE(rates[i]))
+ req->basic_rates.array[rate_len++] = rates[i];
+ }
+ }
+ rate_ie = cfg80211_find_ie(WLAN_EID_EXT_SUPP_RATES, var_pos, len);
+ if (rate_ie) {
+ const u8 *rates = rate_ie + 2;
+ for (i = 0; (i < rate_ie[1]) && (rate_len < MAC_RATESET_LEN); i++) {
+ if (IS_BASIC_RATE(rates[i]))
+ req->basic_rates.array[rate_len++] = rates[i];
+ }
+ }
+ req->basic_rates.length = rate_len;
+#undef IS_BASIC_RATE
+
+ #if 0
+ // Sync buffer for FW
+ if ((error = rwnx_ipc_elem_var_allocs(rwnx_hw, elem, bcn->len,
+ DMA_TO_DEVICE, buf, NULL, NULL))) {
+ return error;
+ }
+ #else
+ rwnx_send_bcn(rwnx_hw, buf, vif->vif_index, bcn->len);
+ #endif
+
+ /* Set parameters for the APM_START_REQ message */
+ req->vif_idx = vif->vif_index;
+ req->bcn_addr = elem->dma_addr;
+ req->bcn_len = bcn->len;
+ req->tim_oft = bcn->head_len;
+ req->tim_len = bcn->tim_len;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
+ req->chan.band = settings->chandef.chan->band;
+ req->chan.freq = settings->chandef.chan->center_freq;
+#endif
+ req->chan.flags = 0;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
+ req->chan.tx_power = chan_to_fw_pwr(settings->chandef.chan->max_power);
+ req->center_freq1 = settings->chandef.center_freq1;
+ req->center_freq2 = settings->chandef.center_freq2;
+ req->ch_width = bw2chnl[settings->chandef.width];
+#endif
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0)
+ req->chan.band = rwnx_hw->ap_chan.band;
+ req->chan.freq = rwnx_hw->ap_chan.prim20_freq;
+ req->center_freq1 = rwnx_hw->ap_chan.center1_freq;
+ req->center_freq2 = rwnx_hw->ap_chan.center2_freq;
+ req->chan.tx_power = rwnx_hw->ap_chan.tx_power;
+#endif
+ req->bcn_int = settings->beacon_interval;
+ if (settings->crypto.control_port)
+ flags |= CONTROL_PORT_HOST;
+
+ if (settings->crypto.control_port_no_encrypt)
+ flags |= CONTROL_PORT_NO_ENC;
+
+ if (use_pairwise_key(&settings->crypto))
+ flags |= WPA_WPA2_IN_USE;
+
+ if (settings->crypto.control_port_ethertype)
+ req->ctrl_port_ethertype = settings->crypto.control_port_ethertype;
+ else
+ req->ctrl_port_ethertype = ETH_P_PAE;
+ req->flags = flags;
+
+ /* Send the APM_START_REQ message to LMAC FW */
+ return rwnx_send_msg(rwnx_hw, req, 1, APM_START_CFM, cfm);
+}
+
+int rwnx_send_apm_stop_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *vif)
+{
+ struct apm_stop_req *req;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ /* Build the APM_STOP_REQ message */
+ req = rwnx_msg_zalloc(APM_STOP_REQ, TASK_APM, DRV_TASK_ID,
+ sizeof(struct apm_stop_req));
+ if (!req)
+ return -ENOMEM;
+
+ /* Set parameters for the APM_STOP_REQ message */
+ req->vif_idx = vif->vif_index;
+
+ /* Send the APM_STOP_REQ message to LMAC FW */
+ return rwnx_send_msg(rwnx_hw, req, 1, APM_STOP_CFM, NULL);
+}
+
+uint8_t scanning = 0;
+uint8_t p2p_working = 0;
+
+#define P2P_WILDCARD_SSID "DIRECT-"
+#define P2P_WILDCARD_SSID_LEN (sizeof(P2P_WILDCARD_SSID) - 1)
+
+
+#ifdef CONFIG_SET_VENDOR_EXTENSION_IE
+u8_l vendor_extension_data[256];
+u8_l vendor_extension_len = 0;
+#if 0
+u8_l vendor_extension_data[]={
+ 0x10,0x49,0x00,0x17,0x00,0x01,0x37,0x10,
+ 0x06,0x00,0x10,0xc5,0xc9,0x91,0xeb,0x1f,
+ 0xce,0x4d,0x00,0xa1,0x2a,0xdf,0xa1,0xe9,
+ 0xc3,0x44,0xe6,0x10,0x49,0x00,0x21,0x00,
+ 0x01,0x37,0x20,0x01,0x00,0x01,0x05,0x20,
+ 0x02,0x00,0x04,0x43,0x56,0x54,0x45,0x20,
+ 0x05,0x00,0x0d,0x31,0x39,0x32,0x2e,0x31,
+ 0x36,0x38,0x2e,0x31,0x35,0x34,0x2e,0x31};
+#endif
+
+void rwnx_insert_vendor_extension_data(struct scanu_vendor_ie_req *ie_req){
+ u8_l temp_ie[256];
+ u8_l vendor_extension_subelement[3] = {0x00,0x37,0x2A};
+ u8_l vendor_extension_id[2] = {0x10,0x49};
+ int index = 0;
+ int vendor_extension_subelement_len = 0;
+
+ memset(temp_ie, 0, 256);
+
+ //find vendor_extension_subelement
+ for(index = 0; index < ie_req->add_ie_len; index++){
+ if(ie_req->ie[index] == vendor_extension_id[0]){
+ index++;
+ if(index == ie_req->add_ie_len){
+ return;
+ }
+ if(ie_req->ie[index] == vendor_extension_id[1] &&
+ ie_req->ie[index + 3] == vendor_extension_subelement[0]&&
+ ie_req->ie[index + 4] == vendor_extension_subelement[1]&&
+ ie_req->ie[index + 5] == vendor_extension_subelement[2]){
+ index = index + 2;
+ vendor_extension_subelement_len = ie_req->ie[index];
+ AICWFDBG(LOGDEBUG, "%s find vendor_extension_subelement,index:%d len:%d\r\n", __func__, index, ie_req->ie[index]);
+ break;
+ }
+ }
+ }
+ index = index + vendor_extension_subelement_len;
+
+ //insert vendor extension
+ memcpy(&temp_ie[0], ie_req->ie, index + 1);
+ memcpy(&temp_ie[index + 1], vendor_extension_data, vendor_extension_len/*sizeof(vendor_extension_data)*/);//insert vendor extension data
+ memcpy(&temp_ie[index + 1 + vendor_extension_len/*sizeof(vendor_extension_data)*/], &ie_req->ie[index + 1], ie_req->add_ie_len - index);
+
+ memcpy(ie_req->ie, temp_ie, ie_req->add_ie_len + vendor_extension_len/*sizeof(vendor_extension_data)*/);
+ ie_req->add_ie_len = ie_req->add_ie_len + vendor_extension_len/*sizeof(vendor_extension_data)*/;
+ ie_req->ie[1] = ie_req->ie[1] + vendor_extension_len/*sizeof(vendor_extension_data)*/;
+
+ //rwnx_data_dump((char*)__func__, (void*)ie_req->ie, ie_req->add_ie_len);
+}
+#endif//CONFIG_SET_VENDOR_EXTENSION_IE
+
+int rwnx_send_scanu_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *rwnx_vif,
+ struct cfg80211_scan_request *param)
+{
+ struct scanu_start_req *req = NULL;
+ struct scanu_vendor_ie_req *ie_req = NULL;
+ struct mm_add_if_cfm add_if_cfm;
+ int i;
+ uint8_t chan_flags = 0;
+ int err;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ /* Build the SCANU_START_REQ message */
+ req = rwnx_msg_zalloc(SCANU_START_REQ, TASK_SCANU, DRV_TASK_ID,
+ sizeof(struct scanu_start_req));
+ if (!req)
+ return -ENOMEM;
+
+ scanning = 1;
+ /* Set parameters */
+ req->vif_idx = rwnx_vif->vif_index;
+ req->chan_cnt = (u8)min_t(int, SCAN_CHANNEL_MAX, param->n_channels);
+ req->ssid_cnt = (u8)min_t(int, SCAN_SSID_MAX, param->n_ssids);
+ req->bssid = mac_addr_bcst;
+ req->no_cck = param->no_cck;
+
+#ifdef RADAR_OR_IR_DETECT
+ if (req->ssid_cnt == 0)
+ chan_flags |= CHAN_NO_IR;
+#endif
+ for (i = 0; i < req->ssid_cnt; i++) {
+ int j;
+ for (j = 0; j < param->ssids[i].ssid_len; j++)
+ req->ssid[i].array[j] = param->ssids[i].ssid[j];
+ req->ssid[i].length = param->ssids[i].ssid_len;
+
+ if (!memcmp(P2P_WILDCARD_SSID, param->ssids[i].ssid,
+ P2P_WILDCARD_SSID_LEN)) {
+ AICWFDBG(LOGINFO, "p2p scanu:%d,%d,%d\n",rwnx_vif->vif_index, rwnx_vif->is_p2p_vif, rwnx_hw->is_p2p_alive);
+#ifdef CONFIG_USE_P2P0
+ if (rwnx_vif->is_p2p_vif && !rwnx_hw->is_p2p_alive) {
+#else
+ if (rwnx_vif == rwnx_hw->p2p_dev_vif && !rwnx_vif->up) {
+#endif
+ err = rwnx_send_add_if (rwnx_hw, rwnx_vif->wdev.address,
+ RWNX_VIF_TYPE(rwnx_vif), false, &add_if_cfm);
+ if (err)
+ goto error;
+
+ /*
+ if ((err = rwnx_send_add_if(rwnx_hw, rwnx_vif->wdev.address,
+ RWNX_VIF_TYPE(rwnx_vif), false, &add_if_cfm)))
+ goto error;
+ */
+
+ if (add_if_cfm.status != 0) {
+ return -EIO;
+ }
+
+ /* Save the index retrieved from LMAC */
+ spin_lock_bh(&rwnx_hw->cb_lock);
+ rwnx_vif->vif_index = add_if_cfm.inst_nbr;
+ rwnx_vif->up = true;
+ rwnx_hw->vif_started++;
+ rwnx_hw->vif_table[add_if_cfm.inst_nbr] = rwnx_vif;
+ spin_unlock_bh(&rwnx_hw->cb_lock);
+ }
+ rwnx_hw->is_p2p_alive = 1;
+#ifndef CONFIG_USE_P2P0
+ mod_timer(&rwnx_hw->p2p_alive_timer, jiffies + msecs_to_jiffies(1000));
+ atomic_set(&rwnx_hw->p2p_alive_timer_count, 0);
+#endif
+ AICWFDBG(LOGINFO ,"p2p scan start\n");
+#ifdef CONFIG_STA_SCAN_WHEN_P2P_WORKING
+ p2p_working = 0;
+#else
+ p2p_working = 1;
+#endif
+ }
+ }
+
+#if 1
+ if (param->ie) {
+ #if 0
+ if (rwnx_ipc_elem_var_allocs(rwnx_hw, &rwnx_hw->scan_ie,
+ param->ie_len, DMA_TO_DEVICE,
+ NULL, param->ie, NULL))
+ goto error;
+
+ req->add_ie_len = param->ie_len;
+ req->add_ies = rwnx_hw->scan_ie.dma_addr;
+ #else
+ ie_req = rwnx_msg_zalloc(SCANU_VENDOR_IE_REQ, TASK_SCANU, DRV_TASK_ID,
+ sizeof(struct scanu_vendor_ie_req));
+ if (!ie_req)
+ return -ENOMEM;
+
+ ie_req->add_ie_len = param->ie_len;
+ ie_req->vif_idx = rwnx_vif->vif_index;
+ memcpy(ie_req->ie, param->ie, param->ie_len);
+#ifdef CONFIG_SET_VENDOR_EXTENSION_IE
+ rwnx_insert_vendor_extension_data(ie_req);
+#endif //CONFIG_SET_VENDOR_EXTENSION_IE
+ req->add_ie_len = 0;
+ req->add_ies = 0;
+
+ if ((err = rwnx_send_msg(rwnx_hw, ie_req, 1, SCANU_VENDOR_IE_CFM, NULL)))
+ goto error;
+ #endif
+ }
+ else {
+ req->add_ie_len = 0;
+ req->add_ies = 0;
+ }
+#else
+ req->add_ie_len = 0;
+ req->add_ies = 0;
+#endif
+
+ for (i = 0; i < req->chan_cnt; i++) {
+ struct ieee80211_channel *chan = param->channels[i];
+ AICWFDBG(LOGDEBUG, "scan channel:%d(%d) \r\n", ieee80211_frequency_to_channel(chan->center_freq), chan->center_freq);
+ req->chan[i].band = chan->band;
+ req->chan[i].freq = chan->center_freq;
+ req->chan[i].flags = chan_flags | get_chan_flags(chan->flags);
+ req->chan[i].tx_power = chan_to_fw_pwr(chan->max_reg_power);
+ }
+
+ /* Send the SCANU_START_REQ message to LMAC FW */
+ return rwnx_send_msg(rwnx_hw, req, 1, SCANU_START_CFM_ADDTIONAL, NULL);
+error:
+ if (req != NULL)
+ rwnx_msg_free(rwnx_hw, req);
+ if (ie_req != NULL)
+ rwnx_msg_free(rwnx_hw, ie_req);
+ return -ENOMEM;
+}
+
+int rwnx_send_scanu_cancel_req(struct rwnx_hw *rwnx_hw, struct scan_cancel_cfm *cfm)
+{
+ struct scan_cancel_req *req;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ /* Build the SCAN_CANCEL_REQ message */
+ req = rwnx_msg_zalloc(SCANU_CANCEL_REQ, TASK_SCANU, DRV_TASK_ID,
+ sizeof(struct scan_cancel_req));
+ if (!req)
+ return -ENOMEM;
+
+ /* Send the SCAN_CANCEL_REQ message to LMAC FW */
+ return rwnx_send_msg(rwnx_hw, req, 1, SCANU_CANCEL_CFM, cfm);
+}
+
+int rwnx_send_apm_start_cac_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *vif,
+ struct cfg80211_chan_def *chandef,
+ struct apm_start_cac_cfm *cfm)
+{
+ struct apm_start_cac_req *req;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ /* Build the APM_START_CAC_REQ message */
+ req = rwnx_msg_zalloc(APM_START_CAC_REQ, TASK_APM, DRV_TASK_ID,
+ sizeof(struct apm_start_cac_req));
+ if (!req)
+ return -ENOMEM;
+
+ /* Set parameters for the APM_START_CAC_REQ message */
+ req->vif_idx = vif->vif_index;
+ req->chan.band = chandef->chan->band;
+ req->chan.freq = chandef->chan->center_freq;
+ req->chan.flags = 0;
+ req->center_freq1 = chandef->center_freq1;
+ req->center_freq2 = chandef->center_freq2;
+ req->ch_width = bw2chnl[chandef->width];
+
+ /* Send the APM_START_CAC_REQ message to LMAC FW */
+ return rwnx_send_msg(rwnx_hw, req, 1, APM_START_CAC_CFM, cfm);
+}
+
+int rwnx_send_apm_stop_cac_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *vif)
+{
+ struct apm_stop_cac_req *req;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ /* Build the APM_STOP_CAC_REQ message */
+ req = rwnx_msg_zalloc(APM_STOP_CAC_REQ, TASK_APM, DRV_TASK_ID,
+ sizeof(struct apm_stop_cac_req));
+ if (!req)
+ return -ENOMEM;
+
+ /* Set parameters for the APM_STOP_CAC_REQ message */
+ req->vif_idx = vif->vif_index;
+
+ /* Send the APM_STOP_CAC_REQ message to LMAC FW */
+ return rwnx_send_msg(rwnx_hw, req, 1, APM_STOP_CAC_CFM, NULL);
+}
+
+int rwnx_send_mesh_start_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *vif,
+ const struct mesh_config *conf, const struct mesh_setup *setup,
+ struct mesh_start_cfm *cfm)
+{
+ // Message to send
+ struct mesh_start_req *req;
+ // Supported basic rates
+ struct ieee80211_supported_band *band_2GHz = rwnx_hw->wiphy->bands[NL80211_BAND_2GHZ];
+ /* Counter */
+ int i;
+ /* Return status */
+ int status;
+ /* DMA Address to be unmapped after confirmation reception */
+ u32 dma_addr = 0;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ /* Build the MESH_START_REQ message */
+ req = rwnx_msg_zalloc(MESH_START_REQ, TASK_MESH, DRV_TASK_ID,
+ sizeof(struct mesh_start_req));
+ if (!req) {
+ return -ENOMEM;
+ }
+
+ req->vif_index = vif->vif_index;
+ #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
+ req->bcn_int = setup->beacon_interval;
+ req->dtim_period = setup->dtim_period;
+#endif
+ req->mesh_id_len = setup->mesh_id_len;
+
+ for (i = 0; i < setup->mesh_id_len; i++) {
+ req->mesh_id[i] = *(setup->mesh_id + i);
+ }
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+ req->user_mpm = setup->user_mpm;
+#endif
+ req->is_auth = setup->is_authenticated;
+ #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0)
+ req->auth_id = setup->auth_id;
+ #endif
+ req->ie_len = setup->ie_len;
+
+ if (setup->ie_len) {
+ /*
+ * Need to provide a Virtual Address to the MAC so that it can download the
+ * additional information elements.
+ */
+ req->ie_addr = dma_map_single(rwnx_hw->dev, (void *)setup->ie,
+ setup->ie_len, DMA_FROM_DEVICE);
+
+ /* Check DMA mapping result */
+ if (dma_mapping_error(rwnx_hw->dev, req->ie_addr)) {
+ printk(KERN_CRIT "%s - DMA Mapping error on additional IEs\n", __func__);
+
+ /* Consider there is no Additional IEs */
+ req->ie_len = 0;
+ } else {
+ /* Store DMA Address so that we can unmap the memory section once MESH_START_CFM is received */
+ dma_addr = req->ie_addr;
+ }
+ }
+
+ /* Provide rate information */
+ req->basic_rates.length = 0;
+ for (i = 0; i < band_2GHz->n_bitrates; i++) {
+ u16 rate = band_2GHz->bitrates[i].bitrate;
+
+ /* Read value is in in units of 100 Kbps, provided value is in units
+ * of 1Mbps, and multiplied by 2 so that 5.5 becomes 11 */
+ rate = (rate << 1) / 10;
+
+ #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0) // TODO: check basic rates
+ if (setup->basic_rates & CO_BIT(i)) {
+ rate |= 0x80;
+ }
+ #endif
+
+ req->basic_rates.array[i] = (u8)rate;
+ req->basic_rates.length++;
+ }
+
+ /* Provide channel information */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
+ req->chan.band = setup->chandef.chan->band;
+ req->chan.freq = setup->chandef.chan->center_freq;
+#endif
+
+ req->chan.flags = 0;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
+ req->chan.tx_power = chan_to_fw_pwr(setup->chandef.chan->max_power);
+ req->center_freq1 = setup->chandef.center_freq1;
+ req->center_freq2 = setup->chandef.center_freq2;
+ req->ch_width = bw2chnl[setup->chandef.width];
+#endif
+
+ /* Send the MESH_START_REQ message to UMAC FW */
+ status = rwnx_send_msg(rwnx_hw, req, 1, MESH_START_CFM, cfm);
+
+ /* Unmap DMA area */
+ if (setup->ie_len) {
+ dma_unmap_single(rwnx_hw->dev, dma_addr, setup->ie_len, DMA_TO_DEVICE);
+ }
+
+ /* Return the status */
+ return (status);
+}
+
+int rwnx_send_mesh_stop_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *vif,
+ struct mesh_stop_cfm *cfm)
+{
+ // Message to send
+ struct mesh_stop_req *req;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ /* Build the MESH_STOP_REQ message */
+ req = rwnx_msg_zalloc(MESH_STOP_REQ, TASK_MESH, DRV_TASK_ID,
+ sizeof(struct mesh_stop_req));
+ if (!req) {
+ return -ENOMEM;
+ }
+
+ req->vif_idx = vif->vif_index;
+
+ /* Send the MESH_STOP_REQ message to UMAC FW */
+ return rwnx_send_msg(rwnx_hw, req, 1, MESH_STOP_CFM, cfm);
+}
+
+int rwnx_send_mesh_update_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *vif,
+ u32 mask, const struct mesh_config *p_mconf, struct mesh_update_cfm *cfm)
+{
+ // Message to send
+ struct mesh_update_req *req;
+ // Keep only bit for fields which can be updated
+ u32 supp_mask = (mask << 1) & (CO_BIT(NL80211_MESHCONF_GATE_ANNOUNCEMENTS)
+ | CO_BIT(NL80211_MESHCONF_HWMP_ROOTMODE)
+ | CO_BIT(NL80211_MESHCONF_FORWARDING)
+ | CO_BIT(NL80211_MESHCONF_POWER_MODE));
+
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ if (!supp_mask) {
+ return -ENOENT;
+ }
+
+ /* Build the MESH_UPDATE_REQ message */
+ req = rwnx_msg_zalloc(MESH_UPDATE_REQ, TASK_MESH, DRV_TASK_ID,
+ sizeof(struct mesh_update_req));
+
+ if (!req) {
+ return -ENOMEM;
+ }
+
+ req->vif_idx = vif->vif_index;
+
+ if (supp_mask & CO_BIT(NL80211_MESHCONF_GATE_ANNOUNCEMENTS))
+ {
+ req->flags |= CO_BIT(MESH_UPDATE_FLAGS_GATE_MODE_BIT);
+ req->gate_announ = p_mconf->dot11MeshGateAnnouncementProtocol;
+ }
+
+ if (supp_mask & CO_BIT(NL80211_MESHCONF_HWMP_ROOTMODE))
+ {
+ req->flags |= CO_BIT(MESH_UPDATE_FLAGS_ROOT_MODE_BIT);
+ req->root_mode = p_mconf->dot11MeshHWMPRootMode;
+ }
+
+ if (supp_mask & CO_BIT(NL80211_MESHCONF_FORWARDING))
+ {
+ req->flags |= CO_BIT(MESH_UPDATE_FLAGS_MESH_FWD_BIT);
+ req->mesh_forward = p_mconf->dot11MeshForwarding;
+ }
+
+ if (supp_mask & CO_BIT(NL80211_MESHCONF_POWER_MODE))
+ {
+ req->flags |= CO_BIT(MESH_UPDATE_FLAGS_LOCAL_PSM_BIT);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
+ req->local_ps_mode = p_mconf->power_mode;
+#endif
+ }
+
+ /* Send the MESH_UPDATE_REQ message to UMAC FW */
+ return rwnx_send_msg(rwnx_hw, req, 1, MESH_UPDATE_CFM, cfm);
+}
+
+int rwnx_send_mesh_peer_info_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *vif,
+ u8 sta_idx, struct mesh_peer_info_cfm *cfm)
+{
+ // Message to send
+ struct mesh_peer_info_req *req;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ /* Build the MESH_PEER_INFO_REQ message */
+ req = rwnx_msg_zalloc(MESH_PEER_INFO_REQ, TASK_MESH, DRV_TASK_ID,
+ sizeof(struct mesh_peer_info_req));
+ if (!req) {
+ return -ENOMEM;
+ }
+
+ req->sta_idx = sta_idx;
+
+ /* Send the MESH_PEER_INFO_REQ message to UMAC FW */
+ return rwnx_send_msg(rwnx_hw, req, 1, MESH_PEER_INFO_CFM, cfm);
+}
+
+void rwnx_send_mesh_peer_update_ntf(struct rwnx_hw *rwnx_hw, struct rwnx_vif *vif,
+ u8 sta_idx, u8 mlink_state)
+{
+ // Message to send
+ struct mesh_peer_update_ntf *ntf;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ /* Build the MESH_PEER_UPDATE_NTF message */
+ ntf = rwnx_msg_zalloc(MESH_PEER_UPDATE_NTF, TASK_MESH, DRV_TASK_ID,
+ sizeof(struct mesh_peer_update_ntf));
+
+ if (ntf) {
+ ntf->vif_idx = vif->vif_index;
+ ntf->sta_idx = sta_idx;
+ ntf->state = mlink_state;
+
+ /* Send the MESH_PEER_INFO_REQ message to UMAC FW */
+ rwnx_send_msg(rwnx_hw, ntf, 0, 0, NULL);
+ }
+}
+
+void rwnx_send_mesh_path_create_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *vif, u8 *tgt_addr)
+{
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ /* Check if we are already waiting for a confirmation */
+ if (!vif->ap.create_path) {
+ // Message to send
+ struct mesh_path_create_req *req;
+
+ /* Build the MESH_PATH_CREATE_REQ message */
+ req = rwnx_msg_zalloc(MESH_PATH_CREATE_REQ, TASK_MESH, DRV_TASK_ID,
+ sizeof(struct mesh_path_create_req));
+
+ if (req) {
+ req->vif_idx = vif->vif_index;
+ memcpy(&req->tgt_mac_addr, tgt_addr, ETH_ALEN);
+
+ vif->ap.create_path = true;
+
+ /* Send the MESH_PATH_CREATE_REQ message to UMAC FW */
+ rwnx_send_msg(rwnx_hw, req, 0, 0, NULL);
+ }
+ }
+}
+
+int rwnx_send_mesh_path_update_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *vif, const u8 *tgt_addr,
+ const u8 *p_nhop_addr, struct mesh_path_update_cfm *cfm)
+{
+ // Message to send
+ struct mesh_path_update_req *req;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ /* Build the MESH_PATH_UPDATE_REQ message */
+ req = rwnx_msg_zalloc(MESH_PATH_UPDATE_REQ, TASK_MESH, DRV_TASK_ID,
+ sizeof(struct mesh_path_update_req));
+ if (!req) {
+ return -ENOMEM;
+ }
+
+ req->delete = (p_nhop_addr == NULL);
+ req->vif_idx = vif->vif_index;
+ memcpy(&req->tgt_mac_addr, tgt_addr, ETH_ALEN);
+
+ if (p_nhop_addr) {
+ memcpy(&req->nhop_mac_addr, p_nhop_addr, ETH_ALEN);
+ }
+
+ /* Send the MESH_PATH_UPDATE_REQ message to UMAC FW */
+ return rwnx_send_msg(rwnx_hw, req, 1, MESH_PATH_UPDATE_CFM, cfm);
+}
+
+void rwnx_send_mesh_proxy_add_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *vif, u8 *ext_addr)
+{
+ // Message to send
+ struct mesh_proxy_add_req *req;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ /* Build the MESH_PROXY_ADD_REQ message */
+ req = rwnx_msg_zalloc(MESH_PROXY_ADD_REQ, TASK_MESH, DRV_TASK_ID,
+ sizeof(struct mesh_proxy_add_req));
+
+ if (req) {
+ req->vif_idx = vif->vif_index;
+ memcpy(&req->ext_sta_addr, ext_addr, ETH_ALEN);
+
+ /* Send the MESH_PROXY_ADD_REQ message to UMAC FW */
+ rwnx_send_msg(rwnx_hw, req, 0, 0, NULL);
+ }
+}
+
+int rwnx_send_tdls_peer_traffic_ind_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *rwnx_vif)
+{
+ struct tdls_peer_traffic_ind_req *tdls_peer_traffic_ind_req;
+
+ if (!rwnx_vif->sta.tdls_sta)
+ return -ENOLINK;
+
+ /* Build the TDLS_PEER_TRAFFIC_IND_REQ message */
+ tdls_peer_traffic_ind_req = rwnx_msg_zalloc(TDLS_PEER_TRAFFIC_IND_REQ, TASK_TDLS, DRV_TASK_ID,
+ sizeof(struct tdls_peer_traffic_ind_req));
+
+ if (!tdls_peer_traffic_ind_req)
+ return -ENOMEM;
+
+ /* Set parameters for the TDLS_PEER_TRAFFIC_IND_REQ message */
+ tdls_peer_traffic_ind_req->vif_index = rwnx_vif->vif_index;
+ tdls_peer_traffic_ind_req->sta_idx = rwnx_vif->sta.tdls_sta->sta_idx;
+ memcpy(&(tdls_peer_traffic_ind_req->peer_mac_addr.array[0]),
+ rwnx_vif->sta.tdls_sta->mac_addr, ETH_ALEN);
+ tdls_peer_traffic_ind_req->dialog_token = 0; // check dialog token value
+ tdls_peer_traffic_ind_req->last_tid = rwnx_vif->sta.tdls_sta->tdls.last_tid;
+ tdls_peer_traffic_ind_req->last_sn = rwnx_vif->sta.tdls_sta->tdls.last_sn;
+
+ /* Send the TDLS_PEER_TRAFFIC_IND_REQ message to LMAC FW */
+ return rwnx_send_msg(rwnx_hw, tdls_peer_traffic_ind_req, 0, 0, NULL);
+}
+
+int rwnx_send_config_monitor_req(struct rwnx_hw *rwnx_hw,
+ struct cfg80211_chan_def *chandef,
+ struct me_config_monitor_cfm *cfm)
+{
+ struct me_config_monitor_req *req;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ /* Build the ME_CONFIG_MONITOR_REQ message */
+ req = rwnx_msg_zalloc(ME_CONFIG_MONITOR_REQ, TASK_ME, DRV_TASK_ID,
+ sizeof(struct me_config_monitor_req));
+ if (!req)
+ return -ENOMEM;
+
+ if (chandef) {
+ req->chan_set = true;
+
+ req->chan.band = chandef->chan->band;
+ req->chan.type = bw2chnl[chandef->width];
+ req->chan.prim20_freq = chandef->chan->center_freq;
+ req->chan.center1_freq = chandef->center_freq1;
+ req->chan.center2_freq = chandef->center_freq2;
+ req->chan.tx_power = chan_to_fw_pwr(chandef->chan->max_power);
+
+ if (rwnx_hw->phy.limit_bw)
+ limit_chan_bw(&req->chan.type, req->chan.prim20_freq, &req->chan.center1_freq);
+ } else {
+ req->chan_set = false;
+ }
+
+ req->uf = rwnx_hw->mod_params->uf;
+ req->auto_reply = rwnx_hw->mod_params->auto_reply;
+
+ /* Send the ME_CONFIG_MONITOR_REQ message to FW */
+ return rwnx_send_msg(rwnx_hw, req, 1, ME_CONFIG_MONITOR_CFM, cfm);
+}
+#endif /* CONFIG_RWNX_FULLMAC */
+
+int rwnx_send_tdls_chan_switch_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *rwnx_vif,
+ struct rwnx_sta *rwnx_sta, bool sta_initiator,
+ u8 oper_class, struct cfg80211_chan_def *chandef,
+ struct tdls_chan_switch_cfm *cfm)
+{
+ struct tdls_chan_switch_req *tdls_chan_switch_req;
+
+
+ /* Build the TDLS_CHAN_SWITCH_REQ message */
+ tdls_chan_switch_req = rwnx_msg_zalloc(TDLS_CHAN_SWITCH_REQ, TASK_TDLS, DRV_TASK_ID,
+ sizeof(struct tdls_chan_switch_req));
+
+ if (!tdls_chan_switch_req)
+ return -ENOMEM;
+
+ /* Set parameters for the TDLS_CHAN_SWITCH_REQ message */
+ tdls_chan_switch_req->vif_index = rwnx_vif->vif_index;
+ tdls_chan_switch_req->sta_idx = rwnx_sta->sta_idx;
+ memcpy(&(tdls_chan_switch_req->peer_mac_addr.array[0]),
+ rwnx_sta_addr(rwnx_sta), ETH_ALEN);
+ tdls_chan_switch_req->initiator = sta_initiator;
+ tdls_chan_switch_req->band = chandef->chan->band;
+ tdls_chan_switch_req->type = bw2chnl[chandef->width];
+ tdls_chan_switch_req->prim20_freq = chandef->chan->center_freq;
+ tdls_chan_switch_req->center1_freq = chandef->center_freq1;
+ tdls_chan_switch_req->center2_freq = chandef->center_freq2;
+ tdls_chan_switch_req->tx_power = chan_to_fw_pwr(chandef->chan->max_power);
+ tdls_chan_switch_req->op_class = oper_class;
+
+ /* Send the TDLS_CHAN_SWITCH_REQ message to LMAC FW */
+ return rwnx_send_msg(rwnx_hw, tdls_chan_switch_req, 1, TDLS_CHAN_SWITCH_CFM, cfm);
+}
+
+int rwnx_send_tdls_cancel_chan_switch_req(struct rwnx_hw *rwnx_hw,
+ struct rwnx_vif *rwnx_vif,
+ struct rwnx_sta *rwnx_sta,
+ struct tdls_cancel_chan_switch_cfm *cfm)
+{
+ struct tdls_cancel_chan_switch_req *tdls_cancel_chan_switch_req;
+
+ /* Build the TDLS_CHAN_SWITCH_REQ message */
+ tdls_cancel_chan_switch_req = rwnx_msg_zalloc(TDLS_CANCEL_CHAN_SWITCH_REQ, TASK_TDLS, DRV_TASK_ID,
+ sizeof(struct tdls_cancel_chan_switch_req));
+ if (!tdls_cancel_chan_switch_req)
+ return -ENOMEM;
+
+ /* Set parameters for the TDLS_CHAN_SWITCH_REQ message */
+ tdls_cancel_chan_switch_req->vif_index = rwnx_vif->vif_index;
+ tdls_cancel_chan_switch_req->sta_idx = rwnx_sta->sta_idx;
+ memcpy(&(tdls_cancel_chan_switch_req->peer_mac_addr.array[0]),
+ rwnx_sta_addr(rwnx_sta), ETH_ALEN);
+
+ /* Send the TDLS_CHAN_SWITCH_REQ message to LMAC FW */
+ return rwnx_send_msg(rwnx_hw, tdls_cancel_chan_switch_req, 1, TDLS_CANCEL_CHAN_SWITCH_CFM, cfm);
+}
+
+#ifdef CONFIG_RWNX_BFMER
+#ifdef CONFIG_RWNX_FULLMAC
+void rwnx_send_bfmer_enable(struct rwnx_hw *rwnx_hw, struct rwnx_sta *rwnx_sta,
+ const struct ieee80211_vht_cap *vht_cap)
+#endif /* CONFIG_RWNX_FULLMAC*/
+{
+ struct mm_bfmer_enable_req *bfmer_en_req;
+#ifdef CONFIG_RWNX_FULLMAC
+ __le32 vht_capability;
+ u8 rx_nss = 0;
+#endif /* CONFIG_RWNX_FULLMAC */
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+#ifdef CONFIG_RWNX_FULLMAC
+ if (!vht_cap) {
+#endif /* CONFIG_RWNX_FULLMAC */
+ goto end;
+ }
+
+#ifdef CONFIG_RWNX_FULLMAC
+ vht_capability = vht_cap->vht_cap_info;
+#endif /* CONFIG_RWNX_FULLMAC */
+
+ if (!(vht_capability & IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE)) {
+ goto end;
+ }
+
+#ifdef CONFIG_RWNX_FULLMAC
+ rx_nss = rwnx_bfmer_get_rx_nss(vht_cap);
+#endif /* CONFIG_RWNX_FULLMAC */
+
+ /* Allocate a structure that will contain the beamforming report */
+ if (rwnx_bfmer_report_add(rwnx_hw, rwnx_sta, RWNX_BFMER_REPORT_SPACE_SIZE))
+ {
+ goto end;
+ }
+
+ /* Build the MM_BFMER_ENABLE_REQ message */
+ bfmer_en_req = rwnx_msg_zalloc(MM_BFMER_ENABLE_REQ, TASK_MM, DRV_TASK_ID,
+ sizeof(struct mm_bfmer_enable_req));
+
+ /* Check message allocation */
+ if (!bfmer_en_req) {
+ /* Free memory allocated for the report */
+ rwnx_bfmer_report_del(rwnx_hw, rwnx_sta);
+
+ /* Do not use beamforming */
+ goto end;
+ }
+
+ /* Provide DMA address to the MAC */
+ bfmer_en_req->host_bfr_addr = rwnx_sta->bfm_report->dma_addr;
+ bfmer_en_req->host_bfr_size = RWNX_BFMER_REPORT_SPACE_SIZE;
+ bfmer_en_req->sta_idx = rwnx_sta->sta_idx;
+#ifdef CONFIG_RWNX_FULLMAC
+ bfmer_en_req->aid = rwnx_sta->aid;
+ bfmer_en_req->rx_nss = rx_nss;
+#endif /* CONFIG_RWNX_FULLMAC */
+
+ if (vht_capability & IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE) {
+ bfmer_en_req->vht_mu_bfmee = true;
+ } else {
+ bfmer_en_req->vht_mu_bfmee = false;
+ }
+
+ /* Send the MM_BFMER_EN_REQ message to LMAC FW */
+ rwnx_send_msg(rwnx_hw, bfmer_en_req, 0, 0, NULL);
+
+end:
+ return;
+}
+
+#ifdef CONFIG_RWNX_MUMIMO_TX
+int rwnx_send_mu_group_update_req(struct rwnx_hw *rwnx_hw, struct rwnx_sta *rwnx_sta)
+{
+ struct mm_mu_group_update_req *req;
+ int group_id, i = 0;
+ u64 map;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ /* Build the MM_MU_GROUP_UPDATE_REQ message */
+ req = rwnx_msg_zalloc(MM_MU_GROUP_UPDATE_REQ, TASK_MM, DRV_TASK_ID,
+ sizeof(struct mm_mu_group_update_req) +
+ rwnx_sta->group_info.cnt * sizeof(req->groups[0]));
+
+ /* Check message allocation */
+ if (!req)
+ return -ENOMEM;
+
+ /* Go through the groups the STA belongs to */
+ group_sta_for_each(rwnx_sta, group_id, map) {
+ int user_pos = rwnx_mu_group_sta_get_pos(rwnx_hw, rwnx_sta, group_id);
+
+ if (WARN((i >= rwnx_sta->group_info.cnt),
+ "STA%d: Too much group (%d)\n",
+ rwnx_sta->sta_idx, i + 1))
+ break;
+
+ req->groups[i].group_id = group_id;
+ req->groups[i].user_pos = user_pos;
+
+ i++;
+ }
+
+ req->group_cnt = rwnx_sta->group_info.cnt;
+ req->sta_idx = rwnx_sta->sta_idx;
+
+ /* Send the MM_MU_GROUP_UPDATE_REQ message to LMAC FW */
+ return rwnx_send_msg(rwnx_hw, req, 1, MM_MU_GROUP_UPDATE_CFM, NULL);
+}
+#endif /* CONFIG_RWNX_MUMIMO_TX */
+#endif /* CONFIG_RWNX_BFMER */
+
+/**********************************************************************
+ * Debug Messages
+ *********************************************************************/
+int rwnx_send_dbg_trigger_req(struct rwnx_hw *rwnx_hw, char *msg)
+{
+ struct mm_dbg_trigger_req *req;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ /* Build the MM_DBG_TRIGGER_REQ message */
+ req = rwnx_msg_zalloc(MM_DBG_TRIGGER_REQ, TASK_MM, DRV_TASK_ID,
+ sizeof(struct mm_dbg_trigger_req));
+ if (!req)
+ return -ENOMEM;
+
+ /* Set parameters for the MM_DBG_TRIGGER_REQ message */
+ strncpy(req->error, msg, sizeof(req->error));
+
+ /* Send the MM_DBG_TRIGGER_REQ message to LMAC FW */
+ return rwnx_send_msg(rwnx_hw, req, 0, -1, NULL);
+}
+
+int rwnx_send_dbg_mem_read_req(struct rwnx_hw *rwnx_hw, u32 mem_addr,
+ struct dbg_mem_read_cfm *cfm)
+{
+ struct dbg_mem_read_req *mem_read_req;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ /* Build the DBG_MEM_READ_REQ message */
+ mem_read_req = rwnx_msg_zalloc(DBG_MEM_READ_REQ, TASK_DBG, DRV_TASK_ID,
+ sizeof(struct dbg_mem_read_req));
+ if (!mem_read_req)
+ return -ENOMEM;
+
+ /* Set parameters for the DBG_MEM_READ_REQ message */
+ mem_read_req->memaddr = mem_addr;
+
+ /* Send the DBG_MEM_READ_REQ message to LMAC FW */
+ return rwnx_send_msg(rwnx_hw, mem_read_req, 1, DBG_MEM_READ_CFM, cfm);
+}
+
+int rwnx_send_dbg_mem_write_req(struct rwnx_hw *rwnx_hw, u32 mem_addr,
+ u32 mem_data)
+{
+ struct dbg_mem_write_req *mem_write_req;
+
+ //RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ /* Build the DBG_MEM_WRITE_REQ message */
+ mem_write_req = rwnx_msg_zalloc(DBG_MEM_WRITE_REQ, TASK_DBG, DRV_TASK_ID,
+ sizeof(struct dbg_mem_write_req));
+ if (!mem_write_req)
+ return -ENOMEM;
+
+ /* Set parameters for the DBG_MEM_WRITE_REQ message */
+ mem_write_req->memaddr = mem_addr;
+ mem_write_req->memdata = mem_data;
+
+ /* Send the DBG_MEM_WRITE_REQ message to LMAC FW */
+ return rwnx_send_msg(rwnx_hw, mem_write_req, 1, DBG_MEM_WRITE_CFM, NULL);
+}
+
+int rwnx_send_dbg_mem_mask_write_req(struct rwnx_hw *rwnx_hw, u32 mem_addr,
+ u32 mem_mask, u32 mem_data)
+{
+ struct dbg_mem_mask_write_req *mem_mask_write_req;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ /* Build the DBG_MEM_MASK_WRITE_REQ message */
+ mem_mask_write_req = rwnx_msg_zalloc(DBG_MEM_MASK_WRITE_REQ, TASK_DBG, DRV_TASK_ID,
+ sizeof(struct dbg_mem_mask_write_req));
+ if (!mem_mask_write_req)
+ return -ENOMEM;
+
+ /* Set parameters for the DBG_MEM_MASK_WRITE_REQ message */
+ mem_mask_write_req->memaddr = mem_addr;
+ mem_mask_write_req->memmask = mem_mask;
+ mem_mask_write_req->memdata = mem_data;
+
+ /* Send the DBG_MEM_MASK_WRITE_REQ message to LMAC FW */
+ return rwnx_send_msg(rwnx_hw, mem_mask_write_req, 1, DBG_MEM_MASK_WRITE_CFM, NULL);
+}
+
+#ifdef CONFIG_RFTEST
+int rwnx_send_rftest_req(struct rwnx_hw *rwnx_hw, u32_l cmd, u32_l argc, u8_l *argv, struct dbg_rftest_cmd_cfm *cfm)
+{
+ struct dbg_rftest_cmd_req *mem_rftest_cmd_req;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ /* Build the DBG_RFTEST_CMD_REQ message */
+ mem_rftest_cmd_req = rwnx_msg_zalloc(DBG_RFTEST_CMD_REQ, TASK_DBG, DRV_TASK_ID,
+ sizeof(struct dbg_rftest_cmd_req));
+ if (!mem_rftest_cmd_req)
+ return -ENOMEM;
+
+ if(argc > 30)
+ return -ENOMEM;
+
+ /* Set parameters for the DBG_MEM_MASK_WRITE_REQ message */
+ mem_rftest_cmd_req->cmd = cmd;
+ mem_rftest_cmd_req->argc = argc;
+ if(argc != 0)
+ memcpy(mem_rftest_cmd_req->argv, argv, argc);
+
+ /* Send the DBG_RFTEST_CMD_REQ message to LMAC FW */
+ return rwnx_send_msg(rwnx_hw, mem_rftest_cmd_req, 1, DBG_RFTEST_CMD_CFM, cfm);
+}
+#endif
+
+int rwnx_send_dbg_set_mod_filter_req(struct rwnx_hw *rwnx_hw, u32 filter)
+{
+ struct dbg_set_mod_filter_req *set_mod_filter_req;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ /* Build the DBG_SET_MOD_FILTER_REQ message */
+ set_mod_filter_req =
+ rwnx_msg_zalloc(DBG_SET_MOD_FILTER_REQ, TASK_DBG, DRV_TASK_ID,
+ sizeof(struct dbg_set_mod_filter_req));
+ if (!set_mod_filter_req)
+ return -ENOMEM;
+
+ /* Set parameters for the DBG_SET_MOD_FILTER_REQ message */
+ set_mod_filter_req->mod_filter = filter;
+
+ /* Send the DBG_SET_MOD_FILTER_REQ message to LMAC FW */
+ return rwnx_send_msg(rwnx_hw, set_mod_filter_req, 1, DBG_SET_MOD_FILTER_CFM, NULL);
+}
+
+int rwnx_send_dbg_set_sev_filter_req(struct rwnx_hw *rwnx_hw, u32 filter)
+{
+ struct dbg_set_sev_filter_req *set_sev_filter_req;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ /* Build the DBG_SET_SEV_FILTER_REQ message */
+ set_sev_filter_req =
+ rwnx_msg_zalloc(DBG_SET_SEV_FILTER_REQ, TASK_DBG, DRV_TASK_ID,
+ sizeof(struct dbg_set_sev_filter_req));
+ if (!set_sev_filter_req)
+ return -ENOMEM;
+
+ /* Set parameters for the DBG_SET_SEV_FILTER_REQ message */
+ set_sev_filter_req->sev_filter = filter;
+
+ /* Send the DBG_SET_SEV_FILTER_REQ message to LMAC FW */
+ return rwnx_send_msg(rwnx_hw, set_sev_filter_req, 1, DBG_SET_SEV_FILTER_CFM, NULL);
+}
+
+int rwnx_send_dbg_get_sys_stat_req(struct rwnx_hw *rwnx_hw,
+ struct dbg_get_sys_stat_cfm *cfm)
+{
+ void *req;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ /* Allocate the message */
+ req = rwnx_msg_zalloc(DBG_GET_SYS_STAT_REQ, TASK_DBG, DRV_TASK_ID, 0);
+ if (!req)
+ return -ENOMEM;
+
+ /* Send the DBG_MEM_READ_REQ message to LMAC FW */
+ return rwnx_send_msg(rwnx_hw, req, 1, DBG_GET_SYS_STAT_CFM, cfm);
+}
+
+int rwnx_send_dbg_mem_block_write_req(struct rwnx_hw *rwnx_hw, u32 mem_addr,
+ u32 mem_size, u32 *mem_data)
+{
+ struct dbg_mem_block_write_req *mem_blk_write_req;
+
+ //RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ /* Build the DBG_MEM_BLOCK_WRITE_REQ message */
+ mem_blk_write_req = rwnx_msg_zalloc(DBG_MEM_BLOCK_WRITE_REQ, TASK_DBG, DRV_TASK_ID,
+ sizeof(struct dbg_mem_block_write_req));
+ if (!mem_blk_write_req)
+ return -ENOMEM;
+
+ /* Set parameters for the DBG_MEM_BLOCK_WRITE_REQ message */
+ mem_blk_write_req->memaddr = mem_addr;
+ mem_blk_write_req->memsize = mem_size;
+ memcpy(mem_blk_write_req->memdata, mem_data, mem_size);
+
+ /* Send the DBG_MEM_BLOCK_WRITE_REQ message to LMAC FW */
+ return rwnx_send_msg(rwnx_hw, mem_blk_write_req, 1, DBG_MEM_BLOCK_WRITE_CFM, NULL);
+}
+
+int rwnx_send_dbg_start_app_req(struct rwnx_hw *rwnx_hw, u32 boot_addr,
+ u32 boot_type)
+{
+ struct dbg_start_app_req *start_app_req;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ /* Build the DBG_START_APP_REQ message */
+ start_app_req = rwnx_msg_zalloc(DBG_START_APP_REQ, TASK_DBG, DRV_TASK_ID,
+ sizeof(struct dbg_start_app_req));
+ if (!start_app_req)
+ return -ENOMEM;
+
+ /* Set parameters for the DBG_START_APP_REQ message */
+ start_app_req->bootaddr = boot_addr;
+ start_app_req->boottype = boot_type;
+
+ /* Send the DBG_START_APP_REQ message to LMAC FW */
+ return rwnx_send_msg(rwnx_hw, start_app_req, 1, DBG_START_APP_CFM, NULL);
+}
+
+int rwnx_send_dbg_gpio_write_req(struct rwnx_hw *rwnx_hw, u8 gpio_idx, u8 gpio_val)
+{
+ struct dbg_gpio_write_req *gpio_write_req;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ gpio_write_req = rwnx_msg_zalloc(DBG_GPIO_WRITE_REQ, TASK_DBG, DRV_TASK_ID,
+ sizeof(struct dbg_gpio_write_req));
+ if (!gpio_write_req)
+ return -ENOMEM;
+
+ gpio_write_req->gpio_idx = gpio_idx;
+ gpio_write_req->gpio_val = gpio_val;
+
+ return rwnx_send_msg(rwnx_hw, gpio_write_req, 1, DBG_GPIO_WRITE_CFM, NULL);
+}
+
+int rwnx_send_dbg_gpio_read_req(struct rwnx_hw *rwnx_hw, u8_l gpio_idx, struct dbg_gpio_read_cfm *cfm)
+{
+ struct dbg_gpio_read_req *gpio_read_req;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ gpio_read_req = rwnx_msg_zalloc(DBG_GPIO_READ_REQ, TASK_DBG, DRV_TASK_ID,
+ sizeof(struct dbg_gpio_read_req));
+ if (!gpio_read_req)
+ return -ENOMEM;
+
+ gpio_read_req->gpio_idx = gpio_idx;
+
+ return rwnx_send_msg(rwnx_hw, gpio_read_req, 1, DBG_GPIO_READ_CFM, cfm);
+}
+
+int rwnx_send_dbg_gpio_init_req(struct rwnx_hw *rwnx_hw, u8_l gpio_idx, u8_l gpio_dir, u8_l gpio_val)
+{
+ struct dbg_gpio_init_req *gpio_init_req;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ gpio_init_req = rwnx_msg_zalloc(DBG_GPIO_INIT_REQ, TASK_DBG, DRV_TASK_ID,
+ sizeof(struct dbg_gpio_init_req));
+ if (!gpio_init_req)
+ return -ENOMEM;
+
+ gpio_init_req->gpio_idx = gpio_idx;
+ gpio_init_req->gpio_dir = gpio_dir;
+ gpio_init_req->gpio_val = gpio_val;
+
+ return rwnx_send_msg(rwnx_hw, gpio_init_req, 1, DBG_GPIO_INIT_CFM, NULL);
+}
+
+int rwnx_send_cfg_rssi_req(struct rwnx_hw *rwnx_hw, u8 vif_index, int rssi_thold, u32 rssi_hyst)
+{
+ struct mm_cfg_rssi_req *req;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ /* Build the MM_CFG_RSSI_REQ message */
+ req = rwnx_msg_zalloc(MM_CFG_RSSI_REQ, TASK_MM, DRV_TASK_ID,
+ sizeof(struct mm_cfg_rssi_req));
+ if (!req)
+ return -ENOMEM;
+
+ if(rwnx_hw->vif_table[vif_index]==NULL)
+ return 0;
+
+ /* Set parameters for the MM_CFG_RSSI_REQ message */
+ req->vif_index = vif_index;
+ req->rssi_thold = (s8)rssi_thold;
+ req->rssi_hyst = (u8)rssi_hyst;
+
+ /* Send the MM_CFG_RSSI_REQ message to LMAC FW */
+ return rwnx_send_msg(rwnx_hw, req, 0, 0, NULL);
+}
+
+#ifdef CONFIG_USB_BT
+int rwnx_send_reboot(struct rwnx_hw *rwnx_hw)
+{
+ int ret = 0;
+ u32 delay = 2 *1000; //1s
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ ret = rwnx_send_dbg_start_app_req(rwnx_hw, delay, HOST_START_APP_REBOOT);
+ return ret;
+}
+#endif // CONFIG_USB_BT
diff --git a/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_msg_tx.h b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_msg_tx.h
new file mode 100644
index 000000000000..7142dc0aa065
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_msg_tx.h
@@ -0,0 +1,181 @@
+/**
+ ****************************************************************************************
+ *
+ * @file rwnx_msg_tx.h
+ *
+ * @brief TX function declarations
+ *
+ * Copyright (C) RivieraWaves 2012-2019
+ *
+ ****************************************************************************************
+ */
+
+#ifndef _RWNX_MSG_TX_H_
+#define _RWNX_MSG_TX_H_
+
+#include "rwnx_defs.h"
+
+int rwnx_send_reset(struct rwnx_hw *rwnx_hw);
+int rwnx_send_start(struct rwnx_hw *rwnx_hw);
+int rwnx_send_version_req(struct rwnx_hw *rwnx_hw, struct mm_version_cfm *cfm);
+int rwnx_send_add_if(struct rwnx_hw *rwnx_hw, const unsigned char *mac,
+ enum nl80211_iftype iftype, bool p2p, struct mm_add_if_cfm *cfm);
+int rwnx_send_remove_if(struct rwnx_hw *rwnx_hw, u8 vif_index, bool defer);
+int rwnx_send_set_channel(struct rwnx_hw *rwnx_hw, int phy_idx,
+ struct mm_set_channel_cfm *cfm);
+int rwnx_send_key_add(struct rwnx_hw *rwnx_hw, u8 vif_idx, u8 sta_idx, bool pairwise,
+ u8 *key, u8 key_len, u8 key_idx, u8 cipher_suite,
+ struct mm_key_add_cfm *cfm);
+int rwnx_send_key_del(struct rwnx_hw *rwnx_hw, uint8_t hw_key_idx);
+int rwnx_send_bcn(struct rwnx_hw *rwnx_hw,u8 *buf, u8 vif_idx, u16 bcn_len);
+
+int rwnx_send_bcn_change(struct rwnx_hw *rwnx_hw, u8 vif_idx, u32 bcn_addr,
+ u16 bcn_len, u16 tim_oft, u16 tim_len, u16 *csa_oft);
+int rwnx_send_tim_update(struct rwnx_hw *rwnx_hw, u8 vif_idx, u16 aid,
+ u8 tx_status);
+int rwnx_send_roc(struct rwnx_hw *rwnx_hw, struct rwnx_vif *vif,
+ struct ieee80211_channel *chan, unsigned int duration, struct mm_remain_on_channel_cfm *roc_cfm);
+int rwnx_send_cancel_roc(struct rwnx_hw *rwnx_hw);
+int rwnx_send_set_power(struct rwnx_hw *rwnx_hw, u8 vif_idx, s8 pwr,
+ struct mm_set_power_cfm *cfm);
+int rwnx_send_set_edca(struct rwnx_hw *rwnx_hw, u8 hw_queue, u32 param,
+ bool uapsd, u8 inst_nbr);
+int rwnx_send_tdls_chan_switch_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *rwnx_vif,
+ struct rwnx_sta *rwnx_sta, bool sta_initiator,
+ u8 oper_class, struct cfg80211_chan_def *chandef,
+ struct tdls_chan_switch_cfm *cfm);
+int rwnx_send_tdls_cancel_chan_switch_req(struct rwnx_hw *rwnx_hw,
+ struct rwnx_vif *rwnx_vif,
+ struct rwnx_sta *rwnx_sta,
+ struct tdls_cancel_chan_switch_cfm *cfm);
+
+#ifdef CONFIG_RWNX_P2P_DEBUGFS
+int rwnx_send_p2p_oppps_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *rwnx_vif,
+ u8 ctw, struct mm_set_p2p_oppps_cfm *cfm);
+int rwnx_send_p2p_noa_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *rwnx_vif,
+ int count, int interval, int duration,
+ bool dyn_noa, struct mm_set_p2p_noa_cfm *cfm);
+#endif /* CONFIG_RWNX_P2P_DEBUGFS */
+
+#ifdef AICWF_ARP_OFFLOAD
+int rwnx_send_arpoffload_en_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *rwnx_vif,
+ u32_l ipaddr, u8_l enable);
+#endif
+int rwnx_send_rf_config_req(struct rwnx_hw *rwnx_hw, u8_l ofst, u8_l sel, u8_l *tbl, u16_l len);
+int rwnx_send_rf_calib_req(struct rwnx_hw *rwnx_hw, struct mm_set_rf_calib_cfm *cfm);
+int rwnx_send_get_macaddr_req(struct rwnx_hw *rwnx_hw, struct mm_get_mac_addr_cfm *cfm);
+
+#ifdef CONFIG_RWNX_FULLMAC
+int rwnx_send_me_config_req(struct rwnx_hw *rwnx_hw);
+int rwnx_send_me_chan_config_req(struct rwnx_hw *rwnx_hw);
+int rwnx_send_me_set_control_port_req(struct rwnx_hw *rwnx_hw, bool opened,
+ u8 sta_idx);
+int rwnx_send_me_sta_add(struct rwnx_hw *rwnx_hw, struct station_parameters *params,
+ const u8 *mac, u8 inst_nbr, struct me_sta_add_cfm *cfm);
+int rwnx_send_me_sta_del(struct rwnx_hw *rwnx_hw, u8 sta_idx, bool tdls_sta);
+int rwnx_send_me_traffic_ind(struct rwnx_hw *rwnx_hw, u8 sta_idx, bool uapsd, u8 tx_status);
+int rwnx_send_me_rc_stats(struct rwnx_hw *rwnx_hw, u8 sta_idx,
+ struct me_rc_stats_cfm *cfm);
+int rwnx_send_me_rc_set_rate(struct rwnx_hw *rwnx_hw,
+ u8 sta_idx,
+ u16 rate_idx);
+int rwnx_send_me_set_ps_mode(struct rwnx_hw *rwnx_hw, u8 ps_mode);
+int rwnx_send_me_set_lp_level(struct rwnx_hw *rwnx_hw, u8 lp_level);
+int rwnx_send_sm_connect_req(struct rwnx_hw *rwnx_hw,
+ struct rwnx_vif *rwnx_vif,
+ struct cfg80211_connect_params *sme,
+ struct sm_connect_cfm *cfm);
+int rwnx_send_sm_disconnect_req(struct rwnx_hw *rwnx_hw,
+ struct rwnx_vif *rwnx_vif,
+ u16 reason);
+int rwnx_send_sm_external_auth_required_rsp(struct rwnx_hw *rwnx_hw,
+ struct rwnx_vif *rwnx_vif,
+ u16 status);
+int rwnx_send_apm_start_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *vif,
+ struct cfg80211_ap_settings *settings,
+ struct apm_start_cfm *cfm,
+ struct rwnx_ipc_elem_var *elem);
+int rwnx_send_apm_stop_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *vif);
+int rwnx_send_scanu_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *rwnx_vif,
+ struct cfg80211_scan_request *param);
+int rwnx_send_scanu_cancel_req(struct rwnx_hw *rwnx_hw,
+ struct scan_cancel_cfm *cfm);
+
+int rwnx_send_apm_start_cac_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *vif,
+ struct cfg80211_chan_def *chandef,
+ struct apm_start_cac_cfm *cfm);
+int rwnx_send_apm_stop_cac_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *vif);
+int rwnx_send_tdls_peer_traffic_ind_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *rwnx_vif);
+int rwnx_send_config_monitor_req(struct rwnx_hw *rwnx_hw,
+ struct cfg80211_chan_def *chandef,
+ struct me_config_monitor_cfm *cfm);
+int rwnx_send_mesh_start_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *vif,
+ const struct mesh_config *conf, const struct mesh_setup *setup,
+ struct mesh_start_cfm *cfm);
+int rwnx_send_mesh_stop_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *vif,
+ struct mesh_stop_cfm *cfm);
+int rwnx_send_mesh_update_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *vif,
+ u32 mask, const struct mesh_config *p_mconf, struct mesh_update_cfm *cfm);
+int rwnx_send_mesh_peer_info_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *vif,
+ u8 sta_idx, struct mesh_peer_info_cfm *cfm);
+void rwnx_send_mesh_peer_update_ntf(struct rwnx_hw *rwnx_hw, struct rwnx_vif *vif,
+ u8 sta_idx, u8 mlink_state);
+void rwnx_send_mesh_path_create_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *vif, u8 *tgt_addr);
+int rwnx_send_mesh_path_update_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *vif, const u8 *tgt_addr,
+ const u8 *p_nhop_addr, struct mesh_path_update_cfm *cfm);
+void rwnx_send_mesh_proxy_add_req(struct rwnx_hw *rwnx_hw, struct rwnx_vif *vif, u8 *ext_addr);
+#endif /* CONFIG_RWNX_FULLMAC */
+
+#ifdef CONFIG_RWNX_BFMER
+#ifdef CONFIG_RWNX_FULLMAC
+void rwnx_send_bfmer_enable(struct rwnx_hw *rwnx_hw, struct rwnx_sta *rwnx_sta,
+ const struct ieee80211_vht_cap *vht_cap);
+#endif /* CONFIG_RWNX_FULLMAC */
+#ifdef CONFIG_RWNX_MUMIMO_TX
+int rwnx_send_mu_group_update_req(struct rwnx_hw *rwnx_hw, struct rwnx_sta *rwnx_sta);
+#endif /* CONFIG_RWNX_MUMIMO_TX */
+#endif /* CONFIG_RWNX_BFMER */
+
+/* Debug messages */
+int rwnx_send_dbg_trigger_req(struct rwnx_hw *rwnx_hw, char *msg);
+int rwnx_send_dbg_mem_read_req(struct rwnx_hw *rwnx_hw, u32 mem_addr,
+ struct dbg_mem_read_cfm *cfm);
+int rwnx_send_dbg_mem_write_req(struct rwnx_hw *rwnx_hw, u32 mem_addr,
+ u32 mem_data);
+int rwnx_send_dbg_mem_mask_write_req(struct rwnx_hw *rwnx_hw, u32 mem_addr,
+ u32 mem_mask, u32 mem_data);
+int rwnx_send_dbg_set_mod_filter_req(struct rwnx_hw *rwnx_hw, u32 filter);
+#ifdef CONFIG_RFTEST
+int rwnx_send_rftest_req(struct rwnx_hw *rwnx_hw, u32_l cmd, u32_l argc, u8_l *argv, struct dbg_rftest_cmd_cfm *cfm);
+#endif
+int rwnx_send_dbg_set_sev_filter_req(struct rwnx_hw *rwnx_hw, u32 filter);
+int rwnx_send_dbg_get_sys_stat_req(struct rwnx_hw *rwnx_hw,
+ struct dbg_get_sys_stat_cfm *cfm);
+int rwnx_send_dbg_mem_block_write_req(struct rwnx_hw *rwnx_hw, u32 mem_addr,
+ u32 mem_size, u32 *mem_data);
+int rwnx_send_dbg_start_app_req(struct rwnx_hw *rwnx_hw, u32 boot_addr,
+ u32 boot_type);
+int rwnx_send_dbg_gpio_write_req(struct rwnx_hw *rwnx_hw, u8_l gpio_idx, u8_l gpio_val);
+int rwnx_send_dbg_gpio_read_req(struct rwnx_hw *rwnx_hw, u8_l gpio_idx, struct dbg_gpio_read_cfm *cfm);
+int rwnx_send_dbg_gpio_init_req(struct rwnx_hw *rwnx_hw, u8_l gpio_idx, u8_l gpio_dir, u8_l gpio_val);
+int rwnx_send_cfg_rssi_req(struct rwnx_hw *rwnx_hw, u8 vif_index, int rssi_thold, u32 rssi_hyst);
+int rwnx_send_coex_req(struct rwnx_hw *rwnx_hw, u8_l disable_coexnull, u8_l enable_nullcts);
+int rwnx_send_get_sta_info_req(struct rwnx_hw *rwnx_hw, u8_l sta_idx, struct mm_get_sta_info_cfm *cfm);
+int rwnx_send_set_stack_start_req(struct rwnx_hw *rwnx_hw, u8_l on, u8_l efuse_valid, u8_l set_vendor_info,
+ u8_l fwtrace_redir_en, struct mm_set_stack_start_cfm *cfm);
+int rwnx_send_txop_req(struct rwnx_hw *rwnx_hw, uint16_t *txop, u8_l long_nav_en, u8_l cfe_en);
+int rwnx_send_vendor_hwconfig_req(struct rwnx_hw *rwnx_hw, uint32_t hwconfig_id, int32_t *param);
+
+int rwnx_send_get_fw_version_req(struct rwnx_hw *rwnx_hw, struct mm_get_fw_version_cfm *cfm);
+int rwnx_send_txpwr_idx_req(struct rwnx_hw *rwnx_hw);
+int rwnx_send_txpwr_ofst_req(struct rwnx_hw *rwnx_hw);
+int rwnx_send_set_filter(struct rwnx_hw *rwnx_hw, uint32_t filter);
+int rwnx_send_txpwr_lvl_req(struct rwnx_hw *rwnx_hw);
+int rwnx_send_txpwr_lvl_v3_req(struct rwnx_hw *rwnx_hw);
+
+#ifdef CONFIG_USB_BT
+int rwnx_send_reboot(struct rwnx_hw *rwnx_hw);
+#endif // CONFIG_USB_BT
+
+
+#endif /* _RWNX_MSG_TX_H_ */
diff --git a/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_mu_group.c b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_mu_group.c
new file mode 100644
index 000000000000..def09a83e4a5
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_mu_group.c
@@ -0,0 +1,659 @@
+/**
+ ******************************************************************************
+ *
+ * @file rwnx_mu_group.c
+ *
+ * Copyright (C) RivieraWaves 2016-2019
+ *
+ ******************************************************************************
+ */
+
+#include "rwnx_defs.h"
+#include "rwnx_msg_tx.h"
+#include "rwnx_events.h"
+
+
+/**
+ * rwnx_mu_group_sta_init - Initialize group information for a STA
+ *
+ * @sta: Sta to initialize
+ */
+void rwnx_mu_group_sta_init(struct rwnx_sta *sta,
+ const struct ieee80211_vht_cap *vht_cap)
+{
+ sta->group_info.map = 0;
+ sta->group_info.cnt = 0;
+ sta->group_info.active.next = LIST_POISON1;
+ sta->group_info.update.next = LIST_POISON1;
+ sta->group_info.last_update = 0;
+ sta->group_info.traffic = 0;
+ sta->group_info.group = 0;
+
+ if (!vht_cap ||
+ !(vht_cap->vht_cap_info & IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE)) {
+ sta->group_info.map = RWNX_SU_GROUP;
+ }
+}
+
+/**
+ * rwnx_mu_group_sta_del - Remove a sta from all MU group
+ *
+ * @rwnx_hw: main driver data
+ * @sta: STA to remove
+ *
+ * Remove one sta from all the MU groups it belongs to.
+ */
+void rwnx_mu_group_sta_del(struct rwnx_hw *rwnx_hw, struct rwnx_sta *sta)
+{
+ struct rwnx_mu_info *mu = &rwnx_hw->mu;
+ int i, j, group_id;
+ bool lock_taken;
+ u64 map;
+
+ lock_taken = (down_interruptible(&mu->lock) == 0);
+
+ group_sta_for_each(sta, group_id, map) {
+ struct rwnx_mu_group *group = rwnx_mu_group_from_id(mu, group_id);
+
+ for (i = 0; i < CONFIG_USER_MAX; i++) {
+ if (group->users[i] == sta) {
+ group->users[i] = NULL;
+ group->user_cnt --;
+ /* Don't keep group with only one user */
+ if (group->user_cnt == 1) {
+ for (j = 0; j < CONFIG_USER_MAX; j++) {
+ if (group->users[j]) {
+ group->users[j]->group_info.cnt--;
+ group->users[j]->group_info.map &= ~BIT_ULL(group->group_id);
+ if (group->users[j]->group_info.group == group_id)
+ group->users[j]->group_info.group = 0;
+ group->user_cnt --;
+ break;
+ }
+ }
+ mu->group_cnt--;
+ trace_mu_group_delete(group->group_id);
+ } else {
+ trace_mu_group_update(group);
+ }
+ break;
+ }
+ }
+
+ WARN((i == CONFIG_USER_MAX), "sta %d doesn't belongs to group %d",
+ sta->sta_idx, group_id);
+ }
+
+ sta->group_info.map = 0;
+ sta->group_info.cnt = 0;
+ sta->group_info.traffic = 0;
+
+ if (sta->group_info.active.next != LIST_POISON1)
+ list_del(&sta->group_info.active);
+
+ if (sta->group_info.update.next != LIST_POISON1)
+ list_del(&sta->group_info.update);
+
+ if (lock_taken)
+ up(&mu->lock);
+}
+
+/**
+ * rwnx_mu_group_sta_get_map - Get the list of group a STA belongs to
+ *
+ * @sta: pointer to the sta
+ *
+ * @return the list of group a STA belongs to as a bitfield
+ */
+u64 rwnx_mu_group_sta_get_map(struct rwnx_sta *sta)
+{
+ if (sta)
+ return sta->group_info.map;
+ return 0;
+}
+
+/**
+ * rwnx_mu_group_sta_get_pos - Get sta position in a group
+ *
+ * @rwnx_hw: main driver data
+ * @sta: pointer to the sta
+ * @group_id: Group id
+ *
+ * @return the positon of @sta in group @group_id or -1 if the sta
+ * doesn't belongs to the group (or group id is invalid)
+ */
+int rwnx_mu_group_sta_get_pos(struct rwnx_hw *rwnx_hw, struct rwnx_sta *sta,
+ int group_id)
+{
+ struct rwnx_mu_group *group;
+ int i;
+
+ group = rwnx_mu_group_from_id(&rwnx_hw->mu, group_id);
+ if (!group)
+ return -1;
+
+ for (i = 0; i < CONFIG_USER_MAX; i++) {
+ if (group->users[i] == sta)
+ return i;
+ }
+
+ WARN(1, "sta %d doesn't belongs to group %d",
+ sta->sta_idx, group_id);
+ return -1;
+}
+
+/**
+ * rwnx_mu_group_move_head - Move (or add) one element at the top of a list
+ *
+ * @list: list pointer
+ * @elem: element to move (or add) at the top of @list
+ *
+ */
+static inline
+void rwnx_mu_group_move_head(struct list_head *list, struct list_head *elem)
+{
+ if (elem->next != LIST_POISON1) {
+ __list_del_entry(elem);
+ }
+ list_add(elem, list);
+}
+
+/**
+ * rwnx_mu_group_remove_users - Remove all the users of a group
+ *
+ * @mu: pointer on MU info
+ * @group: pointer on group to remove users from
+ *
+ * Loop over all users one one group and remove this group from their
+ * map (and count).
+ * Each users is also added to the update_sta list, so that group info
+ * will be resent to fw for this user.
+ */
+static inline
+void rwnx_mu_group_remove_users(struct rwnx_mu_info *mu,
+ struct rwnx_mu_group *group)
+{
+ struct rwnx_sta *sta;
+ int i, group_id = group->group_id;
+
+ for (i = 0; i < CONFIG_USER_MAX; i++) {
+ if (group->users[i]) {
+ sta = group->users[i];
+ group->users[i] = NULL;
+ sta->group_info.cnt--;
+ sta->group_info.map &= ~BIT_ULL(group_id);
+ rwnx_mu_group_move_head(&mu->update_sta,
+ &sta->group_info.update);
+ }
+ }
+
+ if (group->user_cnt)
+ mu->group_cnt--;
+ group->user_cnt = 0;
+ trace_mu_group_delete(group_id);
+}
+
+/**
+ * rwnx_mu_group_add_users - Add users to a group
+ *
+ * @mu: pointer on MU info
+ * @group: pointer on group to add users in
+ * @nb_user: number of users to ad
+ * @users: table of user to add
+ *
+ * Add @nb_users to @group (which may already have users)
+ * Each new users is added to the first free position.
+ * It is assume that @group has at least @nb_user free position. If it is not
+ * case it only add the number of users needed to complete the group.
+ * Each users (effectively added to @group) is also added to the update_sta
+ * list, so that group info will be resent to fw for this user.
+ */
+static inline
+void rwnx_mu_group_add_users(struct rwnx_mu_info *mu,
+ struct rwnx_mu_group *group,
+ int nb_user, struct rwnx_sta **users)
+{
+ int i, j, group_id = group->group_id;
+
+ if (!group->user_cnt)
+ mu->group_cnt++;
+
+ j = 0;
+ for (i = 0; i < nb_user ; i++) {
+ for (; j < CONFIG_USER_MAX ; j++) {
+ if (group->users[j] == NULL) {
+ group->users[j] = users[i];
+ users[i]->group_info.cnt ++;
+ users[i]->group_info.map |= BIT_ULL(group_id);
+
+ rwnx_mu_group_move_head(&(mu->update_sta),
+ &(users[i]->group_info.update));
+ group->user_cnt ++;
+ j ++;
+ break;
+ }
+
+ WARN(j == (CONFIG_USER_MAX - 1),
+ "Too many user for group %d (nb_user=%d)",
+ group_id, group->user_cnt + nb_user - i);
+ }
+ }
+
+ trace_mu_group_update(group);
+}
+
+
+/**
+ * rwnx_mu_group_create_one - create on group with a specific group of user
+ *
+ * @mu: pointer on MU info
+ * @nb_user: number of user to include in the group (<= CONFIG_USER_MAX)
+ * @users: table of users
+ *
+ * Try to create a new group with a specific group of users.
+ * 1- First it checks if a group containing all this users already exists.
+ *
+ * 2- Then it checks if it is possible to complete a group which already
+ * contains at least one user.
+ *
+ * 3- Finally it create a new group. To do so, it take take the last group of
+ * the active_groups list, remove all its current users and add the new ones
+ *
+ * In all cases, the group selected is moved at the top of the active_groups
+ * list
+ *
+ * @return 1 if a new group has been created and 0 otherwise
+ */
+static
+int rwnx_mu_group_create_one(struct rwnx_mu_info *mu, int nb_user,
+ struct rwnx_sta **users, int *nb_group_left)
+{
+ int i, group_id;
+ struct rwnx_mu_group *group;
+ u64 group_match;
+ u64 group_avail;
+
+ group_match = users[0]->group_info.map;
+ group_avail = users[0]->group_info.map;
+ for (i = 1; i < nb_user ; i++) {
+ group_match &= users[i]->group_info.map;
+ group_avail |= users[i]->group_info.map;
+
+ }
+
+ if (group_match) {
+ /* a group (or more) with all the users already exist */
+ group_id = RWNX_GET_FIRST_GROUP_ID(group_match);
+ group = rwnx_mu_group_from_id(mu, group_id);
+ rwnx_mu_group_move_head(&mu->active_groups, &group->list);
+ return 0;
+ }
+
+#if CONFIG_USER_MAX > 2
+ if (group_avail) {
+ /* check if we can complete a group */
+ struct rwnx_sta *users2[CONFIG_USER_MAX];
+ int nb_user2;
+
+ group_for_each(group_id, group_avail) {
+ group = rwnx_mu_group_from_id(mu, group_id);
+ if (group->user_cnt == CONFIG_USER_MAX)
+ continue;
+
+ nb_user2 = 0;
+ for (i = 0; i < nb_user ; i++) {
+ if (!(users[i]->group_info.map & BIT_ULL(group_id))) {
+ users2[nb_user2] = users[i];
+ nb_user2++;
+ }
+ }
+
+ if ((group->user_cnt + nb_user2) <= CONFIG_USER_MAX) {
+ rwnx_mu_group_add_users(mu, group, nb_user2, users2);
+ rwnx_mu_group_move_head(&mu->active_groups, &group->list);
+ return 0;
+ }
+ }
+ }
+#endif /* CONFIG_USER_MAX > 2*/
+
+ /* create a new group */
+ group = list_last_entry(&mu->active_groups, struct rwnx_mu_group, list);
+ rwnx_mu_group_remove_users(mu, group);
+ rwnx_mu_group_add_users(mu, group, nb_user, users);
+ rwnx_mu_group_move_head(&mu->active_groups, &group->list);
+ (*nb_group_left)--;
+
+ return 1;
+}
+
+/**
+ * rwnx_mu_group_create - Create new groups containing one specific sta
+ *
+ * @mu: pointer on MU info
+ * @sta: sta to add in each group
+ * @nb_group_left: maximum number to new group allowed. (updated on exit)
+ *
+ * This will try to create "all the possible" group with a specific sta being
+ * a member of all these group.
+ * The function simply loops over the @active_sta list (starting from @sta).
+ * When it has (CONFIG_USER_MAX - 1) users it try to create a new group with
+ * these users (plus @sta).
+ * Loops end when there is no more users, or no more new group is allowed
+ *
+ */
+static
+void rwnx_mu_group_create(struct rwnx_mu_info *mu, struct rwnx_sta *sta,
+ int *nb_group_left)
+{
+ struct rwnx_sta *user_sta = sta;
+ struct rwnx_sta *users[CONFIG_USER_MAX];
+ int nb_user = 1;
+
+ users[0] = sta;
+ while (*nb_group_left) {
+
+ list_for_each_entry_continue(user_sta, &mu->active_sta, group_info.active) {
+ users[nb_user] = user_sta;
+ if (++nb_user == CONFIG_USER_MAX) {
+ break;
+ }
+ }
+
+ if (nb_user > 1) {
+ if (rwnx_mu_group_create_one(mu, nb_user, users, nb_group_left))
+ (*nb_group_left)--;
+
+ if (nb_user < CONFIG_USER_MAX)
+ break;
+ else
+ nb_user = 1;
+ } else
+ break;
+ }
+}
+
+/**
+ * rwnx_mu_group_work - process function of the "group_work"
+ *
+ * The work is scheduled when several sta (MU beamformee capable) are active.
+ * When called, the @active_sta contains the list of the active sta (starting
+ * from the most recent one), and @active_groups is the list of all possible
+ * groups ordered so that the first one is the most recently used.
+ *
+ * This function will create new groups, starting from group containing the
+ * most "active" sta.
+ * For example if the list of sta is :
+ * sta8 -> sta3 -> sta4 -> sta7 -> sta1
+ * and the number of user per group is 3, it will create grooups :
+ * - sta8 / sta3 / sta4
+ * - sta8 / sta7 / sta1
+ * - sta3 / sta4 / sta7
+ * - sta3 / sta1
+ * - sta4 / sta7 / sta1
+ * - sta7 / sta1
+ *
+ * To create new group, the least used group are first selected.
+ * It is only allowed to create NX_MU_GROUP_MAX per iteration.
+ *
+ * Once groups have been updated, mu group information is update to the fw.
+ * To do so it use the @update_sta list to know which sta has been affected.
+ * As it is necessary to wait for fw confirmation before using this new group
+ * MU is temporarily disabled during group update
+ *
+ * Work is then rescheduled.
+ *
+ * At the end of the function, both @active_sta and @update_sta list are empty.
+ *
+ * Note:
+ * - This is still a WIP, and will require more tuning
+ * - not all combinations are created, to avoid to much processing.
+ * - reschedule delay should be adaptative
+ */
+void rwnx_mu_group_work(struct work_struct *ws)
+{
+ struct delayed_work *dw = container_of(ws, struct delayed_work, work);
+ struct rwnx_mu_info *mu = container_of(dw, struct rwnx_mu_info, group_work);
+ struct rwnx_hw *rwnx_hw = container_of(mu, struct rwnx_hw, mu);
+ struct rwnx_sta *sta, *next;
+ int nb_group_left = NX_MU_GROUP_MAX;
+
+ if (WARN(!rwnx_hw->mod_params->mutx,
+ "In group formation work, but mutx disabled"))
+ return;
+
+ if (down_interruptible(&mu->lock) != 0)
+ return;
+
+ mu->update_count++;
+ if (!mu->update_count)
+ mu->update_count++;
+
+ list_for_each_entry_safe(sta, next, &mu->active_sta, group_info.active) {
+ if (nb_group_left)
+ rwnx_mu_group_create(mu, sta, &nb_group_left);
+
+ sta->group_info.last_update = mu->update_count;
+ list_del(&sta->group_info.active);
+ }
+
+ if (! list_empty(&mu->update_sta)) {
+ list_for_each_entry_safe(sta, next, &mu->update_sta, group_info.update) {
+ rwnx_send_mu_group_update_req(rwnx_hw, sta);
+ list_del(&sta->group_info.update);
+ }
+ }
+
+ mu->next_group_select = jiffies;
+ rwnx_mu_group_sta_select(rwnx_hw);
+ up(&mu->lock);
+
+ return;
+}
+
+/**
+ * rwnx_mu_group_init - Initialize MU groups
+ *
+ * @rwnx_hw: main driver data
+ *
+ * Initialize all MU group
+ */
+void rwnx_mu_group_init(struct rwnx_hw *rwnx_hw)
+{
+ struct rwnx_mu_info *mu = &rwnx_hw->mu;
+ int i;
+
+ INIT_LIST_HEAD(&mu->active_groups);
+ INIT_LIST_HEAD(&mu->active_sta);
+ INIT_LIST_HEAD(&mu->update_sta);
+
+ for (i = 0; i < NX_MU_GROUP_MAX; i++) {
+ int j;
+ mu->groups[i].user_cnt = 0;
+ mu->groups[i].group_id = i + 1;
+ for (j = 0; j < CONFIG_USER_MAX; j++) {
+ mu->groups[i].users[j] = NULL;
+ }
+ list_add(&mu->groups[i].list, &mu->active_groups);
+ }
+
+ mu->update_count = 1;
+ mu->group_cnt = 0;
+ mu->next_group_select = jiffies;
+ INIT_DELAYED_WORK(&mu->group_work, rwnx_mu_group_work);
+ sema_init(&mu->lock, 1);
+}
+
+/**
+ * rwnx_mu_set_active_sta - mark a STA as active
+ *
+ * @rwnx_hw: main driver data
+ * @sta: pointer to the sta
+ * @traffic: Number of buffers to add in the sta's traffic counter
+ *
+ * If @sta is MU beamformee capable (and MU-MIMO tx is enabled) move the
+ * sta at the top of the @active_sta list.
+ * It also schedule the group_work if not already scheduled and the list
+ * contains more than one sta.
+ *
+ * If a STA was already in the list during the last group update
+ * (i.e. sta->group_info.last_update == mu->update_count) it is not added
+ * back to the list until a sta that wasn't active during the last update is
+ * added. This is to avoid scheduling group update with a list of sta that
+ * were all already in the list during previous update.
+ *
+ * It is called with mu->lock taken.
+ */
+void rwnx_mu_set_active_sta(struct rwnx_hw *rwnx_hw, struct rwnx_sta *sta,
+ int traffic)
+{
+ struct rwnx_mu_info *mu = &rwnx_hw->mu;
+
+ if (!sta || (sta->group_info.map & RWNX_SU_GROUP))
+ return;
+
+ sta->group_info.traffic += traffic;
+
+ if ((sta->group_info.last_update != mu->update_count) ||
+ !list_empty(&mu->active_sta)) {
+
+ rwnx_mu_group_move_head(&mu->active_sta, &sta->group_info.active);
+
+ if (!delayed_work_pending(&mu->group_work) &&
+ !list_is_singular(&mu->active_sta)) {
+ schedule_delayed_work(&mu->group_work,
+ msecs_to_jiffies(RWNX_MU_GROUP_INTERVAL));
+ }
+ }
+}
+
+/**
+ * rwnx_mu_set_active_group - mark a MU group as active
+ *
+ * @rwnx_hw: main driver data
+ * @group_id: Group id
+ *
+ * move a group at the top of the @active_groups list
+ */
+void rwnx_mu_set_active_group(struct rwnx_hw *rwnx_hw, int group_id)
+{
+ struct rwnx_mu_info *mu = &rwnx_hw->mu;
+ struct rwnx_mu_group *group = rwnx_mu_group_from_id(mu, group_id);
+
+ rwnx_mu_group_move_head(&mu->active_groups, &group->list);
+}
+
+
+/**
+ * rwnx_mu_group_sta_select - Select the best group for MU stas
+ *
+ * @rwnx_hw: main driver data
+ *
+ * For each MU capable client of AP interfaces this function tries to select
+ * the best group to use.
+ *
+ * In first pass, gather information from all stations to form statistics
+ * for each group for the previous @RWNX_MU_GROUP_SELECT_INTERVAL interval:
+ * - number of buffers transmitted
+ * - number of user
+ *
+ * Then groups with more than 2 active users, are assigned after being ordered
+ * by traffic :
+ * - group with highest traffic is selected: set this group for all its users
+ * - update nb_users for all others group (as one sta may be in several groups)
+ * - select the next group that have still mor than 2 users and assign it.
+ * - continue until all group are processed
+ *
+ */
+void rwnx_mu_group_sta_select(struct rwnx_hw *rwnx_hw)
+{
+ struct rwnx_mu_info *mu = &rwnx_hw->mu;
+ int nb_users[NX_MU_GROUP_MAX + 1];
+ int traffic[NX_MU_GROUP_MAX + 1];
+ int order[NX_MU_GROUP_MAX + 1];
+ struct rwnx_sta *sta;
+ struct rwnx_vif *vif;
+ struct list_head *head;
+ u64 map;
+ int i, j, update, group_id, tmp, cnt = 0;
+
+ if (!mu->group_cnt || time_before(jiffies, mu->next_group_select))
+ return;
+
+ list_for_each_entry(vif, &rwnx_hw->vifs, list) {
+
+ if (RWNX_VIF_TYPE(vif) != NL80211_IFTYPE_AP)
+ continue;
+
+#ifdef CONFIG_RWNX_FULLMAC
+ head = &vif->ap.sta_list;
+#else
+ head = &vif->stations;
+#endif /* CONFIG_RWNX_FULLMAC */
+
+ memset(nb_users, 0, sizeof(nb_users));
+ memset(traffic, 0, sizeof(traffic));
+ list_for_each_entry(sta, head, list) {
+ int sta_traffic = sta->group_info.traffic;
+
+ /* reset statistics for next selection */
+ sta->group_info.traffic = 0;
+ if (sta->group_info.group)
+ trace_mu_group_selection(sta, 0);
+ sta->group_info.group = 0;
+
+ if (sta->group_info.cnt == 0 ||
+ sta_traffic < RWNX_MU_GROUP_MIN_TRAFFIC)
+ continue;
+
+ group_sta_for_each(sta, group_id, map) {
+ nb_users[group_id]++;
+ traffic[group_id] += sta_traffic;
+
+ /* list group with 2 users or more */
+ if (nb_users[group_id] == 2)
+ order[cnt++] = group_id;
+ }
+ }
+
+ /* reorder list of group with more that 2 users */
+ update = 1;
+ while(update) {
+ update = 0;
+ for (i = 0; i < cnt - 1; i++) {
+ if (traffic[order[i]] < traffic[order[i + 1]]) {
+ tmp = order[i];
+ order[i] = order[i + 1];
+ order[i + 1] = tmp;
+ update = 1;
+ }
+ }
+ }
+
+ /* now assign group in traffic order */
+ for (i = 0; i < cnt ; i ++) {
+ struct rwnx_mu_group *group;
+ group_id = order[i];
+
+ if (nb_users[group_id] < 2)
+ continue;
+
+ group = rwnx_mu_group_from_id(mu, group_id);
+ for (j = 0; j < CONFIG_USER_MAX ; j++) {
+ if (group->users[j]) {
+ trace_mu_group_selection(group->users[j], group_id);
+ group->users[j]->group_info.group = group_id;
+
+ group_sta_for_each(group->users[j], tmp, map) {
+ if (group_id != tmp)
+ nb_users[tmp]--;
+ }
+ }
+ }
+ }
+ }
+
+ mu->next_group_select = jiffies +
+ msecs_to_jiffies(RWNX_MU_GROUP_SELECT_INTERVAL);
+ mu->next_group_select |= 1;
+}
diff --git a/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_mu_group.h b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_mu_group.h
new file mode 100644
index 000000000000..338821eed93e
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_mu_group.h
@@ -0,0 +1,179 @@
+/**
+ ******************************************************************************
+ *
+ * @file rwnx_mu_group.h
+ *
+ * Copyright (C) RivieraWaves 2016-2019
+ *
+ ******************************************************************************
+ */
+#ifndef _RWNX_MU_GROUP_H_
+#define _RWNX_MU_GROUP_H_
+
+#include <linux/workqueue.h>
+#include <linux/semaphore.h>
+
+struct rwnx_hw;
+struct rwnx_sta;
+
+#ifdef CONFIG_RWNX_MUMIMO_TX
+
+/**
+ * struct rwnx_sta_group_info - Group Information for a STA
+ *
+ * @active: node for @mu->active_sta list
+ * @update: node for @mu->update_sta list
+ * @cnt: Number of groups the STA belongs to
+ * @map: Bitfield of groups the sta belongs to
+ * @traffic: Number of buffers sent since previous group selection
+ * @group: Id of the group selected by previous group selection
+ * (cf @rwnx_mu_group_sta_select)
+ */
+struct rwnx_sta_group_info {
+ struct list_head active;
+ struct list_head update;
+ u16 last_update;
+ int cnt;
+ u64 map;
+ int traffic;
+ u8 group;
+};
+
+/**
+ * struct mu_group_info - Information about the users of a group
+ *
+ * @list: node for mu->active_groups
+ * @group_id: Group identifier
+ * @user_cnt: Number of the users in the group
+ * @users: Pointer to the sta, ordered by user position
+ */
+struct rwnx_mu_group {
+ struct list_head list;
+ int group_id;
+ int user_cnt;
+ struct rwnx_sta *users[CONFIG_USER_MAX];
+};
+
+/**
+ * struct rwnx_mu_info - Information about all MU group
+ *
+ * @active_groups: List of all possible groups. Ordered from the most recently
+ * used one to the least one (and possibly never used)
+ * @active_sta: List of MU beamformee sta that have been active (since previous
+ * group update). Ordered from the most recently active.
+ * @update_sta: List of sta whose group information has changed and need to be
+ * updated at fw level
+ * @groups: Table of all groups
+ * @group_work: Work item used to schedule group update
+ * @update_count: Counter used to identify the last group formation update.
+ * (cf rwnx_sta_group_info.last_update)
+ * @lock: Lock taken during group update. If tx happens lock is taken, then tx
+ * will not used MU.
+ * @next_group_assign: Next time the group selection should be run
+ * (ref @rwnx_mu_group_sta_select)
+ * @group_cnt: Number of group created
+ */
+struct rwnx_mu_info {
+ struct list_head active_groups;
+ struct list_head active_sta;
+ struct list_head update_sta;
+ struct rwnx_mu_group groups[NX_MU_GROUP_MAX];
+ struct delayed_work group_work;
+ u16 update_count;
+ struct semaphore lock;
+ unsigned long next_group_select;
+ u8 group_cnt;
+};
+
+#define RWNX_SU_GROUP BIT_ULL(0)
+#define RWNX_MU_GROUP_MASK 0x7ffffffffffffffeULL
+#define RWNX_MU_GROUP_INTERVAL 200 /* in ms */
+#define RWNX_MU_GROUP_SELECT_INTERVAL 100 /* in ms */
+// minimum traffic in a RWNX_MU_GROUP_SELECT_INTERVAL to consider the sta
+#define RWNX_MU_GROUP_MIN_TRAFFIC 50 /* in number of packet */
+
+
+#define RWNX_GET_FIRST_GROUP_ID(map) (fls64(map) - 1)
+
+#define group_sta_for_each(sta, id, map) \
+ map = sta->group_info.map & RWNX_MU_GROUP_MASK; \
+ for (id = (fls64(map) - 1) ; id > 0 ; \
+ map &= ~(u64)BIT_ULL(id), id = (fls64(map) - 1))
+
+#define group_for_each(id, map) \
+ for (id = (fls64(map) - 1) ; id > 0 ; \
+ map &= ~(u64)BIT_ULL(id), id = (fls64(map) - 1))
+
+#define RWNX_MUMIMO_INFO_POS_ID(info) (((info) >> 6) & 0x3)
+#define RWNX_MUMIMO_INFO_GROUP_ID(info) ((info) & 0x3f)
+
+static inline
+struct rwnx_mu_group *rwnx_mu_group_from_id(struct rwnx_mu_info *mu, int id)
+{
+ if (id > NX_MU_GROUP_MAX)
+ return NULL;
+
+ return &mu->groups[id - 1];
+}
+
+
+void rwnx_mu_group_sta_init(struct rwnx_sta *sta,
+ const struct ieee80211_vht_cap *vht_cap);
+void rwnx_mu_group_sta_del(struct rwnx_hw *rwnx_hw, struct rwnx_sta *sta);
+u64 rwnx_mu_group_sta_get_map(struct rwnx_sta *sta);
+int rwnx_mu_group_sta_get_pos(struct rwnx_hw *rwnx_hw, struct rwnx_sta *sta,
+ int group_id);
+
+void rwnx_mu_group_init(struct rwnx_hw *rwnx_hw);
+
+void rwnx_mu_set_active_sta(struct rwnx_hw *rwnx_hw, struct rwnx_sta *sta,
+ int traffic);
+void rwnx_mu_set_active_group(struct rwnx_hw *rwnx_hw, int group_id);
+void rwnx_mu_group_sta_select(struct rwnx_hw *rwnx_hw);
+
+
+#else /* ! CONFIG_RWNX_MUMIMO_TX */
+
+static inline
+void rwnx_mu_group_sta_init(struct rwnx_sta *sta,
+ const struct ieee80211_vht_cap *vht_cap)
+{}
+
+static inline
+void rwnx_mu_group_sta_del(struct rwnx_hw *rwnx_hw, struct rwnx_sta *sta)
+{}
+
+static inline
+u64 rwnx_mu_group_sta_get_map(struct rwnx_sta *sta)
+{
+ return 0;
+}
+
+static inline
+int rwnx_mu_group_sta_get_pos(struct rwnx_hw *rwnx_hw, struct rwnx_sta *sta,
+ int group_id)
+{
+ return 0;
+}
+
+static inline
+void rwnx_mu_group_init(struct rwnx_hw *rwnx_hw)
+{}
+
+static inline
+void rwnx_mu_set_active_sta(struct rwnx_hw *rwnx_hw, struct rwnx_sta *sta,
+ int traffic)
+{}
+
+static inline
+void rwnx_mu_set_active_group(struct rwnx_hw *rwnx_hw, int group_id)
+{}
+
+static inline
+void rwnx_mu_group_sta_select(struct rwnx_hw *rwnx_hw)
+{}
+
+#endif /* CONFIG_RWNX_MUMIMO_TX */
+
+#endif /* _RWNX_MU_GROUP_H_ */
+
diff --git a/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_pci.c b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_pci.c
new file mode 100644
index 000000000000..cd207c8d6507
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_pci.c
@@ -0,0 +1,94 @@
+/**
+ ******************************************************************************
+ *
+ * @file rwnx_pci.c
+ *
+ * Copyright (C) RivieraWaves 2012-2019
+ *
+ ******************************************************************************
+ */
+#include <linux/pci.h>
+#include <linux/module.h>
+
+#include "rwnx_defs.h"
+#include "rwnx_dini.h"
+#include "rwnx_v7.h"
+
+#define PCI_VENDOR_ID_DINIGROUP 0x17DF
+#define PCI_DEVICE_ID_DINIGROUP_DNV6_F2PCIE 0x1907
+
+#define PCI_DEVICE_ID_XILINX_CEVA_VIRTEX7 0x7011
+
+static const struct pci_device_id rwnx_pci_ids[] = {
+ {PCI_DEVICE(PCI_VENDOR_ID_DINIGROUP, PCI_DEVICE_ID_DINIGROUP_DNV6_F2PCIE)},
+ {PCI_DEVICE(PCI_VENDOR_ID_XILINX, PCI_DEVICE_ID_XILINX_CEVA_VIRTEX7)},
+ {0,}
+};
+
+
+/* Uncomment this for depmod to create module alias */
+/* We don't want this on development platform */
+//MODULE_DEVICE_TABLE(pci, rwnx_pci_ids);
+
+static int rwnx_pci_probe(struct pci_dev *pci_dev,
+ const struct pci_device_id *pci_id)
+{
+ struct rwnx_plat *rwnx_plat = NULL;
+ void *drvdata;
+ int ret = -ENODEV;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ if (pci_id->vendor == PCI_VENDOR_ID_DINIGROUP) {
+ ret = rwnx_dini_platform_init(pci_dev, &rwnx_plat);
+ } else if (pci_id->vendor == PCI_VENDOR_ID_XILINX) {
+ ret = rwnx_v7_platform_init(pci_dev, &rwnx_plat);
+ }
+
+ if (ret)
+ return ret;
+
+ rwnx_plat->pci_dev = pci_dev;
+
+ ret = rwnx_platform_init(rwnx_plat, &drvdata);
+ pci_set_drvdata(pci_dev, drvdata);
+
+ if (ret)
+ rwnx_plat->deinit(rwnx_plat);
+
+ return ret;
+}
+
+static void rwnx_pci_remove(struct pci_dev *pci_dev)
+{
+ struct rwnx_hw *rwnx_hw;
+ struct rwnx_plat *rwnx_plat;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ rwnx_hw = pci_get_drvdata(pci_dev);
+ rwnx_plat = rwnx_hw->plat;
+
+ rwnx_platform_deinit(rwnx_hw);
+ rwnx_plat->deinit(rwnx_plat);
+
+ pci_set_drvdata(pci_dev, NULL);
+}
+
+static struct pci_driver rwnx_pci_drv = {
+ .name = KBUILD_MODNAME,
+ .id_table = rwnx_pci_ids,
+ .probe = rwnx_pci_probe,
+ .remove = rwnx_pci_remove
+};
+
+int rwnx_pci_register_drv(void)
+{
+ return pci_register_driver(&rwnx_pci_drv);
+}
+
+void rwnx_pci_unregister_drv(void)
+{
+ pci_unregister_driver(&rwnx_pci_drv);
+}
+
diff --git a/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_pci.h b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_pci.h
new file mode 100644
index 000000000000..d81578cbef48
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_pci.h
@@ -0,0 +1,17 @@
+/**
+ ******************************************************************************
+ *
+ * @file rwnx_pci.h
+ *
+ * Copyright (C) RivieraWaves 2012-2019
+ *
+ ******************************************************************************
+ */
+
+#ifndef _RWNX_PCI_H_
+#define _RWNX_PCI_H_
+
+int rwnx_pci_register_drv(void);
+void rwnx_pci_unregister_drv(void);
+
+#endif /* _RWNX_PCI_H_ */
diff --git a/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_platform.c b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_platform.c
new file mode 100644
index 000000000000..9ccd54389901
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_platform.c
@@ -0,0 +1,2306 @@
+/**
+ ******************************************************************************
+ *
+ * @file rwnx_platform.c
+ *
+ * Copyright (C) RivieraWaves 2012-2019
+ *
+ ******************************************************************************
+ */
+
+#include <linux/module.h>
+#include <linux/firmware.h>
+#include <linux/delay.h>
+#include <linux/vmalloc.h>
+
+#include "rwnx_platform.h"
+#include "reg_access.h"
+#include "hal_desc.h"
+#include "rwnx_main.h"
+#include "rwnx_pci.h"
+#ifndef CONFIG_RWNX_FHOST
+#include "ipc_host.h"
+#endif /* !CONFIG_RWNX_FHOST */
+#include "rwnx_msg_tx.h"
+
+#ifdef AICWF_SDIO_SUPPORT
+#include "aicwf_sdio.h"
+#endif
+
+#ifdef AICWF_USB_SUPPORT
+#include "aicwf_usb.h"
+#endif
+#include "md5.h"
+#include "aicwf_compat_8800dc.h"
+#include "aicwf_compat_8800d80.h"
+#ifdef CONFIG_USE_FW_REQUEST
+#include <linux/firmware.h>
+#endif
+
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 9, 0))
+static inline struct inode *file_inode(const struct file *f)
+{
+ return f->f_dentry->d_inode;
+}
+#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(3, 9, 0)) */
+struct rwnx_plat *g_rwnx_plat = NULL;
+
+#define FW_PATH_MAX_LEN 200
+extern char aic_fw_path[FW_PATH_MAX_LEN];
+
+//Parser state
+#define INIT 0
+#define CMD 1
+#define PRINT 2
+#define GET_VALUE 3
+
+typedef struct
+{
+ txpwr_lvl_conf_t txpwr_lvl;
+ txpwr_lvl_conf_v2_t txpwr_lvl_v2;
+ txpwr_lvl_conf_v3_t txpwr_lvl_v3;
+ txpwr_loss_conf_t txpwr_loss;
+ txpwr_ofst_conf_t txpwr_ofst;
+ xtal_cap_conf_t xtal_cap;
+} userconfig_info_t;
+
+userconfig_info_t userconfig_info = {
+ .txpwr_lvl = {
+ .enable = 1,
+ .dsss = 9,
+ .ofdmlowrate_2g4 = 8,
+ .ofdm64qam_2g4 = 8,
+ .ofdm256qam_2g4 = 8,
+ .ofdm1024qam_2g4 = 8,
+ .ofdmlowrate_5g = 11,
+ .ofdm64qam_5g = 10,
+ .ofdm256qam_5g = 9,
+ .ofdm1024qam_5g = 9
+ },
+ .txpwr_lvl_v2 = {
+ .enable = 1,
+ .pwrlvl_11b_11ag_2g4 =
+ //1M, 2M, 5M5, 11M, 6M, 9M, 12M, 18M, 24M, 36M, 48M, 54M
+ { 20, 20, 20, 20, 20, 20, 20, 20, 18, 18, 16, 16},
+ .pwrlvl_11n_11ac_2g4 =
+ //MCS0, MCS1, MCS2, MCS3, MCS4, MCS5, MCS6, MCS7, MCS8, MCS9
+ { 20, 20, 20, 20, 18, 18, 16, 16, 16, 16},
+ .pwrlvl_11ax_2g4 =
+ //MCS0, MCS1, MCS2, MCS3, MCS4, MCS5, MCS6, MCS7, MCS8, MCS9, MCS10,MCS11
+ { 20, 20, 20, 20, 18, 18, 16, 16, 16, 16, 15, 15},
+ },
+ .txpwr_lvl_v3 = {
+ .enable = 1,
+ .pwrlvl_11b_11ag_2g4 =
+ //1M, 2M, 5M5, 11M, 6M, 9M, 12M, 18M, 24M, 36M, 48M, 54M
+ { 20, 20, 20, 20, 20, 20, 20, 20, 18, 18, 16, 16},
+ .pwrlvl_11n_11ac_2g4 =
+ //MCS0, MCS1, MCS2, MCS3, MCS4, MCS5, MCS6, MCS7, MCS8, MCS9
+ { 20, 20, 20, 20, 18, 18, 16, 16, 16, 16},
+ .pwrlvl_11ax_2g4 =
+ //MCS0, MCS1, MCS2, MCS3, MCS4, MCS5, MCS6, MCS7, MCS8, MCS9, MCS10,MCS11
+ { 20, 20, 20, 20, 18, 18, 16, 16, 16, 16, 15, 15},
+ .pwrlvl_11a_5g =
+ //NA, NA, NA, NA, 6M, 9M, 12M, 18M, 24M, 36M, 48M, 54M
+ { 0x80, 0x80, 0x80, 0x80, 20, 20, 20, 20, 18, 18, 16, 16},
+ .pwrlvl_11n_11ac_5g =
+ //MCS0, MCS1, MCS2, MCS3, MCS4, MCS5, MCS6, MCS7, MCS8, MCS9
+ { 20, 20, 20, 20, 18, 18, 16, 16, 16, 15},
+ .pwrlvl_11ax_5g =
+ //MCS0, MCS1, MCS2, MCS3, MCS4, MCS5, MCS6, MCS7, MCS8, MCS9, MCS10,MCS11
+ { 20, 20, 20, 20, 18, 18, 16, 16, 16, 15, 14, 14},
+ },
+ .txpwr_loss = {
+ .loss_enable = 1,
+ .loss_value = 0,
+ },
+ .txpwr_ofst = {
+ .enable = 1,
+ .chan_1_4 = 0,
+ .chan_5_9 = 0,
+ .chan_10_13 = 0,
+ .chan_36_64 = 0,
+ .chan_100_120 = 0,
+ .chan_122_140 = 0,
+ .chan_142_165 = 0,
+ },
+ .xtal_cap = {
+ .enable = 0,
+ .xtal_cap = 24,
+ .xtal_cap_fine = 31,
+ },
+};
+
+
+#ifndef CONFIG_ROM_PATCH_EN
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 9, 0))
+static inline struct inode *file_inode(const struct file *f)
+{
+ return f->f_dentry->d_inode;
+}
+#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(3, 9, 0)) */
+
+
+#endif/* !CONFIG_ROM_PATCH_EN */
+
+
+
+#ifdef CONFIG_RWNX_TL4
+/**
+ * rwnx_plat_tl4_fw_upload() - Load the requested FW into embedded side.
+ *
+ * @rwnx_plat: pointer to platform structure
+ * @fw_addr: Virtual address where the fw must be loaded
+ * @filename: Name of the fw.
+ *
+ * Load a fw, stored as a hex file, into the specified address
+ */
+static int rwnx_plat_tl4_fw_upload(struct rwnx_plat *rwnx_plat, u8* fw_addr,
+ char *filename)
+{
+ struct device *dev = rwnx_platform_get_dev(rwnx_plat);
+ const struct firmware *fw;
+ int err = 0;
+ u32 *dst;
+ u8 const *file_data;
+ char typ0, typ1;
+ u32 addr0, addr1;
+ u32 dat0, dat1;
+ int remain;
+
+ err = request_firmware(&fw, filename, dev);
+ if (err) {
+ return err;
+ }
+ file_data = fw->data;
+ remain = fw->size;
+
+ /* Copy the file on the Embedded side */
+ dev_dbg(dev, "\n### Now copy %s firmware, @ = %p\n", filename, fw_addr);
+
+ /* Walk through all the lines of the configuration file */
+ while (remain >= 16) {
+ u32 data, offset;
+
+ if (sscanf(file_data, "%c:%08X %04X", &typ0, &addr0, &dat0) != 3)
+ break;
+ if ((addr0 & 0x01) != 0) {
+ addr0 = addr0 - 1;
+ dat0 = 0;
+ } else {
+ file_data += 16;
+ remain -= 16;
+ }
+ if ((remain < 16) ||
+ (sscanf(file_data, "%c:%08X %04X", &typ1, &addr1, &dat1) != 3) ||
+ (typ1 != typ0) || (addr1 != (addr0 + 1))) {
+ typ1 = typ0;
+ addr1 = addr0 + 1;
+ dat1 = 0;
+ } else {
+ file_data += 16;
+ remain -= 16;
+ }
+
+ if (typ0 == 'C') {
+ offset = 0x00200000;
+ if ((addr1 % 4) == 3)
+ offset += 2*(addr1 - 3);
+ else
+ offset += 2*(addr1 + 1);
+
+ data = dat1 | (dat0 << 16);
+ } else {
+ offset = 2*(addr1 - 1);
+ data = dat0 | (dat1 << 16);
+ }
+ dst = (u32 *)(fw_addr + offset);
+ *dst = data;
+ }
+
+ release_firmware(fw);
+
+ return err;
+}
+#endif
+
+#if 0
+/**
+ * rwnx_plat_bin_fw_upload() - Load the requested binary FW into embedded side.
+ *
+ * @rwnx_plat: pointer to platform structure
+ * @fw_addr: Virtual address where the fw must be loaded
+ * @filename: Name of the fw.
+ *
+ * Load a fw, stored as a binary file, into the specified address
+ */
+static int rwnx_plat_bin_fw_upload(struct rwnx_plat *rwnx_plat, u8* fw_addr,
+ char *filename)
+{
+ const struct firmware *fw;
+ struct device *dev = rwnx_platform_get_dev(rwnx_plat);
+ int err = 0;
+ unsigned int i, size;
+ u32 *src, *dst;
+
+ err = request_firmware(&fw, filename, dev);
+ if (err) {
+ return err;
+ }
+
+ /* Copy the file on the Embedded side */
+ dev_dbg(dev, "\n### Now copy %s firmware, @ = %p\n", filename, fw_addr);
+
+ src = (u32 *)fw->data;
+ dst = (u32 *)fw_addr;
+ size = (unsigned int)fw->size;
+
+ /* check potential platform bug on multiple stores vs memcpy */
+ for (i = 0; i < size; i += 4) {
+ *dst++ = *src++;
+ }
+
+ release_firmware(fw);
+
+ return err;
+}
+#endif
+
+#define MD5(x) x[0],x[1],x[2],x[3],x[4],x[5],x[6],x[7],x[8],x[9],x[10],x[11],x[12],x[13],x[14],x[15]
+#define MD5PINRT "file md5:%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\r\n"
+
+static int rwnx_load_firmware(u32 **fw_buf, const char *name, struct device *device)
+{
+
+#ifdef CONFIG_USE_FW_REQUEST
+ const struct firmware *fw = NULL;
+ u32 *dst = NULL;
+ void *buffer=NULL;
+ MD5_CTX md5;
+ unsigned char decrypt[16];
+ int size = 0;
+ int ret = 0;
+
+ AICWFDBG(LOGINFO, "%s: request firmware = %s \n", __func__ ,name);
+
+ ret = request_firmware(&fw, name, NULL);
+
+ if (ret < 0) {
+ AICWFDBG(LOGERROR, "Load %s fail\n", name);
+ release_firmware(fw);
+ return -1;
+ }
+
+ size = fw->size;
+ dst = (u32 *)fw->data;
+
+ if (size <= 0) {
+ AICWFDBG(LOGERROR, "wrong size of firmware file\n");
+ release_firmware(fw);
+ return -1;
+ }
+
+ buffer = vmalloc(size);
+ memset(buffer, 0, size);
+ memcpy(buffer, dst, size);
+
+ *fw_buf = buffer;
+
+ MD5Init(&md5);
+ MD5Update(&md5, (unsigned char *)buffer, size);
+ MD5Final(&md5, decrypt);
+ AICWFDBG(LOGINFO, MD5PINRT, MD5(decrypt));
+
+ release_firmware(fw);
+
+ return size;
+#else
+ void *buffer = NULL;
+ char *path = NULL;
+ struct file *fp = NULL;
+ int size = 0, len = 0, i = 0;
+ ssize_t rdlen = 0;
+ u32 *src = NULL, *dst = NULL;
+ MD5_CTX md5;
+ unsigned char decrypt[16];
+
+ /* get the firmware path */
+ path = __getname();
+ if (!path) {
+ *fw_buf = NULL;
+ return -1;
+ }
+
+ len = snprintf(path, FW_PATH_MAX_LEN, "%s/%s", aic_fw_path, name);
+
+ //len = snprintf(path, FW_PATH_MAX_LEN, "%s", name);
+ if (len >= FW_PATH_MAX_LEN) {
+ AICWFDBG(LOGERROR, "%s: %s file's path too long\n", __func__, name);
+ *fw_buf = NULL;
+ __putname(path);
+ return -1;
+ }
+
+ AICWFDBG(LOGINFO, "%s :firmware path = %s \n", __func__, path);
+
+ /* open the firmware file */
+ fp = filp_open(path, O_RDONLY, 0);
+ if (IS_ERR_OR_NULL(fp)) {
+ AICWFDBG(LOGERROR, "%s: %s file failed to open\n", __func__, name);
+ *fw_buf = NULL;
+ __putname(path);
+ fp = NULL;
+ return -1;
+ }
+
+ size = i_size_read(file_inode(fp));
+ if (size <= 0) {
+ AICWFDBG(LOGERROR, "%s: %s file size invalid %d\n", __func__, name, size);
+ *fw_buf = NULL;
+ __putname(path);
+ filp_close(fp, NULL);
+ fp = NULL;
+ return -1;
+ }
+
+ /* start to read from firmware file */
+ buffer = vmalloc(size);
+ if (!buffer) {
+ *fw_buf = NULL;
+ __putname(path);
+ filp_close(fp, NULL);
+ fp = NULL;
+ return -1;
+ }
+
+ #if LINUX_VERSION_CODE > KERNEL_VERSION(4, 13, 16)
+ rdlen = kernel_read(fp, buffer, size, &fp->f_pos);
+ #else
+ rdlen = kernel_read(fp, fp->f_pos, buffer, size);
+ #endif
+
+ if (size != rdlen) {
+ AICWFDBG(LOGERROR, "%s: %s file rdlen invalid %d\n", __func__, name, (int)rdlen);
+ *fw_buf = NULL;
+ __putname(path);
+ filp_close(fp, NULL);
+ fp = NULL;
+ vfree(buffer);
+ buffer = NULL;
+ return -1;
+ }
+ if (rdlen > 0) {
+ fp->f_pos += rdlen;
+ }
+
+ /*start to transform the data format*/
+ src = (u32 *)buffer;
+ dst = (u32 *)vmalloc(size);
+
+ if (!dst) {
+ *fw_buf = NULL;
+ __putname(path);
+ filp_close(fp, NULL);
+ fp = NULL;
+ vfree(buffer);
+ buffer = NULL;
+ return -1;
+ }
+
+ for (i = 0; i < (size/4); i++) {
+ dst[i] = src[i];
+ }
+
+ __putname(path);
+ filp_close(fp, NULL);
+ fp = NULL;
+ vfree(buffer);
+ buffer = NULL;
+ *fw_buf = dst;
+
+ MD5Init(&md5);
+ MD5Update(&md5, (unsigned char *)dst, size);
+ MD5Final(&md5, decrypt);
+
+ AICWFDBG(LOGINFO, MD5PINRT, MD5(decrypt));
+
+ return size;
+#endif
+}
+
+static void rwnx_restore_firmware(u32 **fw_buf)
+{
+ vfree(*fw_buf);
+ *fw_buf = NULL;
+}
+
+
+/* buffer is allocated by kzalloc */
+int rwnx_request_firmware_common(struct rwnx_hw *rwnx_hw, u32** buffer, const char *filename)
+{
+ int size;
+
+ AICWFDBG(LOGINFO, "### Load file %s\n", filename);
+
+ size = rwnx_load_firmware(buffer, filename, NULL);
+
+ return size;
+}
+
+void rwnx_release_firmware_common(u32** buffer)
+{
+ rwnx_restore_firmware(buffer);
+}
+
+
+
+/**
+ * rwnx_plat_bin_fw_upload_2() - Load the requested binary FW into embedded side.
+ *
+ * @rwnx_hw: Main driver data
+ * @fw_addr: Address where the fw must be loaded
+ * @filename: Name of the fw.
+ *
+ * Load a fw, stored as a binary file, into the specified address
+ */
+
+int rwnx_plat_bin_fw_upload_2(struct rwnx_hw *rwnx_hw, u32 fw_addr,
+ char *filename)
+{
+ int err = 0;
+ unsigned int i = 0, size;
+// u32 *src;
+ u32 *dst = NULL;
+
+ /* Copy the file on the Embedded side */
+ AICWFDBG(LOGINFO, "### Upload %s firmware, @ = %x\n", filename, fw_addr);
+
+ size = rwnx_request_firmware_common(rwnx_hw, &dst, filename);
+ if (!dst) {
+ AICWFDBG(LOGERROR, "No such file or directory\n");
+ return -1;
+ }
+ if (size <= 0) {
+ AICWFDBG(LOGERROR, "wrong size of firmware file\n");
+ dst = NULL;
+ err = -1;
+ return -1;
+ }
+
+ AICWFDBG(LOGINFO, "size=%d, dst[0]=%x\n", size, dst[0]);
+ if (size > 512) {
+ for (; i < (size - 512); i += 512) {
+ //printk("wr blk 0: %p -> %x\r\n", dst + i / 4, fw_addr + i);
+ err = rwnx_send_dbg_mem_block_write_req(rwnx_hw, fw_addr + i, 512, dst + i / 4);
+ if (err) {
+ AICWFDBG(LOGERROR, "bin upload fail: %x, err:%d\r\n", fw_addr + i, err);
+ break;
+ }
+ }
+ }
+ if (!err && (i < size)) {
+ //printk("wr blk 1: %p -> %x\r\n", dst + i / 4, fw_addr + i);
+ err = rwnx_send_dbg_mem_block_write_req(rwnx_hw, fw_addr + i, size - i, dst + i / 4);
+ if (err) {
+ AICWFDBG(LOGERROR, "bin upload fail: %x, err:%d\r\n", fw_addr + i, err);
+ }
+ }
+
+ if (dst) {
+ rwnx_release_firmware_common(&dst);
+ }
+
+ return err;
+}
+
+
+
+#ifndef CONFIG_ROM_PATCH_EN
+#if defined(CONFIG_PLATFORM_ALLWINNER) || defined(CONFIG_NANOPI_M4)
+#if 0
+static int aic_load_firmware(u32 ** fw_buf, const char *name,
+ struct device *device)
+{
+ void *buffer=NULL;
+ char *path=NULL;
+ struct file *fp=NULL;
+ int size = 0, len=0, i=0;
+ ssize_t rdlen=0;
+ u32 *src=NULL, *dst = NULL;
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ /* get the firmware path */
+ path = __getname();
+ if (!path){
+ *fw_buf=NULL;
+ return -1;
+ }
+
+ len = snprintf(path, FW_PATH_MAX_LEN, "%s/%s",aic_fw_path, name);
+ if (len >= FW_PATH_MAX_LEN) {
+ printk("%s: %s file's path too long\n", __func__, name);
+ *fw_buf=NULL;
+ __putname(path);
+ return -1;
+ }
+
+ printk("%s :firmware path = %s \n", __func__ ,path);
+
+
+ /* open the firmware file */
+ fp=filp_open(path, O_RDONLY, 0);
+ if(IS_ERR(fp) || (!fp)){
+ printk("%s: %s file failed to open\n", __func__, name);
+ if(IS_ERR(fp))
+ printk("is_Err\n");
+ *fw_buf=NULL;
+ __putname(path);
+ fp=NULL;
+ return -1;
+ }
+
+ size = i_size_read(file_inode(fp));
+ if(size<=0){
+ printk("%s: %s file size invalid %d\n", __func__, name, size);
+ *fw_buf=NULL;
+ __putname(path);
+ filp_close(fp,NULL);
+ fp=NULL;
+ return -1;
+ }
+
+ /* start to read from firmware file */
+ buffer = kzalloc(size, GFP_KERNEL);
+ if(!buffer){
+ *fw_buf=NULL;
+ __putname(path);
+ filp_close(fp,NULL);
+ fp=NULL;
+ return -1;
+ }
+
+
+ #if LINUX_VERSION_CODE > KERNEL_VERSION(4, 13, 16)
+ rdlen = kernel_read(fp, buffer, size, &fp->f_pos);
+ #else
+ rdlen = kernel_read(fp, fp->f_pos, buffer, size);
+ #endif
+
+ if(size != rdlen){
+ printk("%s: %s file rdlen invalid %ld\n", __func__, name, (long int)rdlen);
+ *fw_buf=NULL;
+ __putname(path);
+ filp_close(fp,NULL);
+ fp=NULL;
+ kfree(buffer);
+ buffer=NULL;
+ return -1;
+ }
+ if(rdlen > 0){
+ fp->f_pos += rdlen;
+ }
+
+
+ /*start to transform the data format*/
+ src = (u32*)buffer;
+ printk("malloc dst\n");
+ dst = (u32*)kzalloc(size,GFP_KERNEL);
+
+ if(!dst){
+ *fw_buf=NULL;
+ __putname(path);
+ filp_close(fp,NULL);
+ fp=NULL;
+ kfree(buffer);
+ buffer=NULL;
+ return -1;
+ }
+
+ for(i=0;i<(size/4);i++){
+ dst[i] = src[i];
+ }
+
+ __putname(path);
+ filp_close(fp,NULL);
+ fp=NULL;
+ kfree(buffer);
+ buffer=NULL;
+ *fw_buf = dst;
+
+ return size;
+
+}
+#endif
+#endif
+#endif
+
+
+#ifndef CONFIG_ROM_PATCH_EN
+#if defined(CONFIG_PLATFORM_ALLWINNER) || defined(CONFIG_NANOPI_M4)
+#if 0
+static int rwnx_plat_bin_fw_upload_android(struct rwnx_hw *rwnx_hw, u32 fw_addr,
+ char *filename)
+{
+ struct device *dev = rwnx_platform_get_dev(rwnx_hw->plat);
+ unsigned int i=0;
+ int size;
+ u32 *dst=NULL;
+ int err=0;
+
+
+ /* load aic firmware */
+ size = aic_load_firmware(&dst, filename, dev);
+ if(size<=0){
+ printk("wrong size of firmware file\n");
+ kfree(dst);
+ dst = NULL;
+ return -1;
+ }
+
+
+ /* Copy the file on the Embedded side */
+ printk("\n### Upload %s firmware, @ = %x size=%d\n", filename, fw_addr, size);
+
+ if (size > 1024) {// > 1KB data
+ for (i = 0; i < (size - 1024); i += 1024) {//each time write 1KB
+ err = rwnx_send_dbg_mem_block_write_req(rwnx_hw, fw_addr + i, 1024, dst + i / 4);
+ if (err) {
+ printk("bin upload fail: %x, err:%d\r\n", fw_addr + i, err);
+ break;
+ }
+ }
+ }
+
+ if (!err && (i < size)) {// <1KB data
+ err = rwnx_send_dbg_mem_block_write_req(rwnx_hw, fw_addr + i, size - i, dst + i / 4);
+ if (err) {
+ printk("bin upload fail: %x, err:%d\r\n", fw_addr + i, err);
+ }
+ }
+
+ if (dst) {
+ kfree(dst);
+ dst = NULL;
+ }
+
+ return err;
+}
+#endif
+#endif
+#endif
+
+
+
+#if 0
+#ifndef CONFIG_RWNX_TL4
+#define IHEX_REC_DATA 0
+#define IHEX_REC_EOF 1
+#define IHEX_REC_EXT_SEG_ADD 2
+#define IHEX_REC_START_SEG_ADD 3
+#define IHEX_REC_EXT_LIN_ADD 4
+#define IHEX_REC_START_LIN_ADD 5
+
+/**
+ * rwnx_plat_ihex_fw_upload() - Load the requested intel hex 8 FW into embedded side.
+ *
+ * @rwnx_plat: pointer to platform structure
+ * @fw_addr: Virtual address where the fw must be loaded
+ * @filename: Name of the fw.
+ *
+ * Load a fw, stored as a ihex file, into the specified address.
+ */
+static int rwnx_plat_ihex_fw_upload(struct rwnx_plat *rwnx_plat, u8* fw_addr,
+ char *filename)
+{
+ const struct firmware *fw;
+ struct device *dev = rwnx_platform_get_dev(rwnx_plat);
+ u8 const *src, *end;
+ u32 *dst;
+ u16 haddr, segaddr, addr;
+ u32 hwaddr;
+ u8 load_fw, byte_count, checksum, csum, rec_type;
+ int err, rec_idx;
+ char hex_buff[9];
+
+ err = request_firmware(&fw, filename, dev);
+ if (err) {
+ return err;
+ }
+
+ /* Copy the file on the Embedded side */
+ dev_dbg(dev, "\n### Now copy %s firmware, @ = %p\n", filename, fw_addr);
+
+ src = fw->data;
+ end = src + (unsigned int)fw->size;
+ haddr = 0;
+ segaddr = 0;
+ load_fw = 1;
+ err = -EINVAL;
+ rec_idx = 0;
+ hwaddr = 0;
+
+#define IHEX_READ8(_val, _cs) { \
+ hex_buff[2] = 0; \
+ strncpy(hex_buff, src, 2); \
+ if (kstrtou8(hex_buff, 16, &_val)) \
+ goto end; \
+ src += 2; \
+ if (_cs) \
+ csum += _val; \
+ }
+
+#define IHEX_READ16(_val) { \
+ hex_buff[4] = 0; \
+ strncpy(hex_buff, src, 4); \
+ if (kstrtou16(hex_buff, 16, &_val)) \
+ goto end; \
+ src += 4; \
+ csum += (_val & 0xff) + (_val >> 8); \
+ }
+
+#define IHEX_READ32(_val) { \
+ hex_buff[8] = 0; \
+ strncpy(hex_buff, src, 8); \
+ if (kstrtouint(hex_buff, 16, &_val)) \
+ goto end; \
+ src += 8; \
+ csum += (_val & 0xff) + ((_val >> 8) & 0xff) + \
+ ((_val >> 16) & 0xff) + (_val >> 24); \
+ }
+
+#define IHEX_READ32_PAD(_val, _nb) { \
+ memset(hex_buff, '0', 8); \
+ hex_buff[8] = 0; \
+ strncpy(hex_buff, src, (2 * _nb)); \
+ if (kstrtouint(hex_buff, 16, &_val)) \
+ goto end; \
+ src += (2 * _nb); \
+ csum += (_val & 0xff) + ((_val >> 8) & 0xff) + \
+ ((_val >> 16) & 0xff) + (_val >> 24); \
+}
+
+ /* loop until end of file is read*/
+ while (load_fw) {
+ rec_idx++;
+ csum = 0;
+
+ /* Find next colon start code */
+ while (*src != ':') {
+ src++;
+ if ((src + 3) >= end) /* 3 = : + rec_len */
+ goto end;
+ }
+ src++;
+
+ /* Read record len */
+ IHEX_READ8(byte_count, 1);
+ if ((src + (byte_count * 2) + 8) >= end) /* 8 = rec_addr + rec_type + chksum */
+ goto end;
+
+ /* Read record addr */
+ IHEX_READ16(addr);
+
+ /* Read record type */
+ IHEX_READ8(rec_type, 1);
+
+ switch(rec_type) {
+ case IHEX_REC_DATA:
+ {
+ /* Update destination address */
+ dst = (u32 *) (fw_addr + hwaddr + addr);
+
+ while (byte_count) {
+ u32 val;
+ if (byte_count >= 4) {
+ IHEX_READ32(val);
+ byte_count -= 4;
+ } else {
+ IHEX_READ32_PAD(val, byte_count);
+ byte_count = 0;
+ }
+ *dst++ = __swab32(val);
+ }
+ break;
+ }
+ case IHEX_REC_EOF:
+ {
+ load_fw = 0;
+ err = 0;
+ break;
+ }
+ case IHEX_REC_EXT_SEG_ADD: /* Extended Segment Address */
+ {
+ IHEX_READ16(segaddr);
+ hwaddr = (haddr << 16) + (segaddr << 4);
+ break;
+ }
+ case IHEX_REC_EXT_LIN_ADD: /* Extended Linear Address */
+ {
+ IHEX_READ16(haddr);
+ hwaddr = (haddr << 16) + (segaddr << 4);
+ break;
+ }
+ case IHEX_REC_START_LIN_ADD: /* Start Linear Address */
+ {
+ u32 val;
+ IHEX_READ32(val); /* need to read for checksum */
+ break;
+ }
+ case IHEX_REC_START_SEG_ADD:
+ default:
+ {
+ dev_err(dev, "ihex: record type %d not supported\n", rec_type);
+ load_fw = 0;
+ }
+ }
+
+ /* Read and compare checksum */
+ IHEX_READ8(checksum, 0);
+ if (checksum != (u8)(~csum + 1))
+ goto end;
+ }
+
+#undef IHEX_READ8
+#undef IHEX_READ16
+#undef IHEX_READ32
+#undef IHEX_READ32_PAD
+
+ end:
+ release_firmware(fw);
+
+ if (err)
+ dev_err(dev, "%s: Invalid ihex record around line %d\n", filename, rec_idx);
+
+ return err;
+}
+#endif /* CONFIG_RWNX_TL4 */
+
+#ifndef CONFIG_RWNX_SDM
+/**
+ * rwnx_plat_get_rf() - Retrun the RF used in the platform
+ *
+ * @rwnx_plat: pointer to platform structure
+ */
+static u32 rwnx_plat_get_rf(struct rwnx_plat *rwnx_plat)
+{
+ u32 ver;
+ ver = RWNX_REG_READ(rwnx_plat, RWNX_ADDR_SYSTEM, MDM_HDMCONFIG_ADDR);
+
+ ver = __MDM_PHYCFG_FROM_VERS(ver);
+ WARN(((ver != MDM_PHY_CONFIG_TRIDENT) &&
+ (ver != MDM_PHY_CONFIG_ELMA) &&
+ (ver != MDM_PHY_CONFIG_KARST)),
+ "bad phy version 0x%08x\n", ver);
+
+ return ver;
+}
+
+/**
+ * rwnx_plat_stop_agcfsm() - Stop a AGC state machine
+ *
+ * @rwnx_plat: pointer to platform structure
+ * @agg_reg: Address of the agccntl register (within RWNX_ADDR_SYSTEM)
+ * @agcctl: Updated with value of the agccntl rgister before stop
+ * @memclk: Updated with value of the clock register before stop
+ * @agc_ver: Version of the AGC load procedure
+ * @clkctrladdr: Indicates which AGC clock register should be accessed
+ */
+static void rwnx_plat_stop_agcfsm(struct rwnx_plat *rwnx_plat, int agc_reg,
+ u32 *agcctl, u32 *memclk, u8 agc_ver,
+ u32 clkctrladdr)
+{
+ /* First read agcctnl and clock registers */
+ *memclk = RWNX_REG_READ(rwnx_plat, RWNX_ADDR_SYSTEM, clkctrladdr);
+
+ /* Stop state machine : xxAGCCNTL0[AGCFSMRESET]=1 */
+ *agcctl = RWNX_REG_READ(rwnx_plat, RWNX_ADDR_SYSTEM, agc_reg);
+ RWNX_REG_WRITE((*agcctl) | BIT(12), rwnx_plat, RWNX_ADDR_SYSTEM, agc_reg);
+
+ /* Force clock */
+ if (agc_ver > 0) {
+ /* CLKGATEFCTRL0[AGCCLKFORCE]=1 */
+ RWNX_REG_WRITE((*memclk) | BIT(29), rwnx_plat, RWNX_ADDR_SYSTEM,
+ clkctrladdr);
+ } else {
+ /* MEMCLKCTRL0[AGCMEMCLKCTRL]=0 */
+ RWNX_REG_WRITE((*memclk) & ~BIT(3), rwnx_plat, RWNX_ADDR_SYSTEM,
+ clkctrladdr);
+ }
+}
+
+
+/**
+ * rwnx_plat_start_agcfsm() - Restart a AGC state machine
+ *
+ * @rwnx_plat: pointer to platform structure
+ * @agg_reg: Address of the agccntl register (within RWNX_ADDR_SYSTEM)
+ * @agcctl: value of the agccntl register to restore
+ * @memclk: value of the clock register to restore
+ * @agc_ver: Version of the AGC load procedure
+ * @clkctrladdr: Indicates which AGC clock register should be accessed
+ */
+static void rwnx_plat_start_agcfsm(struct rwnx_plat *rwnx_plat, int agc_reg,
+ u32 agcctl, u32 memclk, u8 agc_ver,
+ u32 clkctrladdr)
+{
+
+ /* Release clock */
+ if (agc_ver > 0)
+ /* CLKGATEFCTRL0[AGCCLKFORCE]=0 */
+ RWNX_REG_WRITE(memclk & ~BIT(29), rwnx_plat, RWNX_ADDR_SYSTEM,
+ clkctrladdr);
+ else
+ /* MEMCLKCTRL0[AGCMEMCLKCTRL]=1 */
+ RWNX_REG_WRITE(memclk | BIT(3), rwnx_plat, RWNX_ADDR_SYSTEM,
+ clkctrladdr);
+
+ /* Restart state machine: xxAGCCNTL0[AGCFSMRESET]=0 */
+ RWNX_REG_WRITE(agcctl & ~BIT(12), rwnx_plat, RWNX_ADDR_SYSTEM, agc_reg);
+}
+#endif
+
+/**
+ * rwnx_plat_fcu_load() - Load FCU (Fith Chain Unit) ucode
+ *
+ * @rwnx_hw: main driver data
+ *
+ * c.f Modem UM (AGC/CCA initialization)
+ */
+static int rwnx_plat_fcu_load(struct rwnx_hw *rwnx_hw)
+{
+ int ret=0;
+#ifndef CONFIG_RWNX_SDM
+ struct rwnx_plat *rwnx_plat = rwnx_hw->plat;
+ u32 agcctl, memclk;
+
+#ifndef CONFIG_RWNX_FHOST
+ /* By default, we consider that there is only one RF in the system */
+ rwnx_hw->phy.cnt = 1;
+#endif // CONFIG_RWNX_FHOST
+
+ if (rwnx_plat_get_rf(rwnx_plat) != MDM_PHY_CONFIG_ELMA)
+ /* No FCU for PHYs other than Elma */
+ return 0;
+
+ agcctl = RWNX_REG_READ(rwnx_plat, RWNX_ADDR_SYSTEM, RIU_RWNXAGCCNTL_ADDR);
+ if (!__RIU_FCU_PRESENT(agcctl))
+ /* No FCU present in this version */
+ return 0;
+
+#ifndef CONFIG_RWNX_FHOST
+ /* FCU is present */
+ #ifdef USE_5G
+ rwnx_hw->phy.cnt = 2;
+ rwnx_hw->phy.sec_chan.band = NL80211_BAND_5GHZ;
+ rwnx_hw->phy.sec_chan.type = PHY_CHNL_BW_20;
+ rwnx_hw->phy.sec_chan.prim20_freq = 5500;
+ rwnx_hw->phy.sec_chan.center_freq1 = 5500;
+ rwnx_hw->phy.sec_chan.center_freq2 = 0;
+ #endif
+#endif // CONFIG_RWNX_FHOST
+
+ rwnx_plat_stop_agcfsm(rwnx_plat, FCU_RWNXFCAGCCNTL_ADDR, &agcctl, &memclk, 0,
+ MDM_MEMCLKCTRL0_ADDR);
+
+ ret = rwnx_plat_bin_fw_upload(rwnx_plat,
+ RWNX_ADDR(rwnx_plat, RWNX_ADDR_SYSTEM, PHY_FCU_UCODE_ADDR),
+ RWNX_FCU_FW_NAME);
+
+ rwnx_plat_start_agcfsm(rwnx_plat, FCU_RWNXFCAGCCNTL_ADDR, agcctl, memclk, 0,
+ MDM_MEMCLKCTRL0_ADDR);
+#endif
+
+ return ret;
+}
+
+/**
+ * rwnx_is_new_agc_load() - Return is new agc clock register should be used
+ *
+ * @rwnx_plat: platform data
+ * @rf: rf in used
+ *
+ * c.f Modem UM (AGC/CCA initialization)
+ */
+#ifndef CONFIG_RWNX_SDM
+static u8 rwnx_get_agc_load_version(struct rwnx_plat *rwnx_plat, u32 rf, u32 *clkctrladdr)
+{
+ u8 agc_load_ver = 0;
+ u32 agc_ver;
+ u32 regval;
+
+ /* Trident and Elma PHY use old method */
+ if (rf != MDM_PHY_CONFIG_KARST) {
+ *clkctrladdr = MDM_MEMCLKCTRL0_ADDR;
+ return 0;
+ }
+
+ /* Get the FPGA signature */
+ regval = RWNX_REG_READ(rwnx_plat, RWNX_ADDR_SYSTEM, SYSCTRL_SIGNATURE_ADDR);
+
+ if (__FPGA_TYPE(regval) == 0xC0CA)
+ *clkctrladdr = CRM_CLKGATEFCTRL0_ADDR;
+ else
+ *clkctrladdr = MDM_CLKGATEFCTRL0_ADDR;
+
+ /* Read RIU version register */
+ agc_ver = RWNX_REG_READ(rwnx_plat, RWNX_ADDR_SYSTEM, RIU_RWNXVERSION_ADDR);
+ agc_load_ver = __RIU_AGCLOAD_FROM_VERS(agc_ver);
+
+ return agc_load_ver;
+}
+#endif /* CONFIG_RWNX_SDM */
+
+/**
+ * rwnx_plat_agc_load() - Load AGC ucode
+ *
+ * @rwnx_plat: platform data
+ * c.f Modem UM (AGC/CCA initialization)
+ */
+static int rwnx_plat_agc_load(struct rwnx_plat *rwnx_plat)
+{
+ int ret = 0;
+#ifndef CONFIG_RWNX_SDM
+ u32 agc = 0, agcctl, memclk;
+ u32 clkctrladdr;
+ u32 rf = rwnx_plat_get_rf(rwnx_plat);
+ u8 agc_ver;
+
+ switch (rf) {
+ case MDM_PHY_CONFIG_TRIDENT:
+ agc = AGC_RWNXAGCCNTL_ADDR;
+ break;
+ case MDM_PHY_CONFIG_ELMA:
+ case MDM_PHY_CONFIG_KARST:
+ agc = RIU_RWNXAGCCNTL_ADDR;
+ break;
+ default:
+ return -1;
+ }
+
+ agc_ver = rwnx_get_agc_load_version(rwnx_plat, rf, &clkctrladdr);
+
+ rwnx_plat_stop_agcfsm(rwnx_plat, agc, &agcctl, &memclk, agc_ver, clkctrladdr);
+
+ ret = rwnx_plat_bin_fw_upload(rwnx_plat,
+ RWNX_ADDR(rwnx_plat, RWNX_ADDR_SYSTEM, PHY_AGC_UCODE_ADDR),
+ RWNX_AGC_FW_NAME);
+
+ if (!ret && (agc_ver == 1)) {
+ /* Run BIST to ensure that the AGC RAM was correctly loaded */
+ RWNX_REG_WRITE(BIT(28), rwnx_plat, RWNX_ADDR_SYSTEM,
+ RIU_RWNXDYNAMICCONFIG_ADDR);
+ while (RWNX_REG_READ(rwnx_plat, RWNX_ADDR_SYSTEM,
+ RIU_RWNXDYNAMICCONFIG_ADDR) & BIT(28));
+
+ if (!(RWNX_REG_READ(rwnx_plat, RWNX_ADDR_SYSTEM,
+ RIU_AGCMEMBISTSTAT_ADDR) & BIT(0))) {
+ dev_err(rwnx_platform_get_dev(rwnx_plat),
+ "AGC RAM not loaded correctly 0x%08x\n",
+ RWNX_REG_READ(rwnx_plat, RWNX_ADDR_SYSTEM,
+ RIU_AGCMEMSIGNATURESTAT_ADDR));
+ ret = -EIO;
+ }
+ }
+
+ rwnx_plat_start_agcfsm(rwnx_plat, agc, agcctl, memclk, agc_ver, clkctrladdr);
+
+#endif
+ return ret;
+}
+
+/**
+ * rwnx_ldpc_load() - Load LDPC RAM
+ *
+ * @rwnx_hw: Main driver data
+ * c.f Modem UM (LDPC initialization)
+ */
+static int rwnx_ldpc_load(struct rwnx_hw *rwnx_hw)
+{
+#ifndef CONFIG_RWNX_SDM
+ struct rwnx_plat *rwnx_plat = rwnx_hw->plat;
+ u32 rf = rwnx_plat_get_rf(rwnx_plat);
+ u32 phy_feat = RWNX_REG_READ(rwnx_plat, RWNX_ADDR_SYSTEM, MDM_HDMCONFIG_ADDR);
+
+ if ((rf != MDM_PHY_CONFIG_KARST) ||
+ (phy_feat & (MDM_LDPCDEC_BIT | MDM_LDPCENC_BIT)) !=
+ (MDM_LDPCDEC_BIT | MDM_LDPCENC_BIT)) {
+ goto disable_ldpc;
+ }
+
+ if (rwnx_plat_bin_fw_upload(rwnx_plat,
+ RWNX_ADDR(rwnx_plat, RWNX_ADDR_SYSTEM, PHY_LDPC_RAM_ADDR),
+ RWNX_LDPC_RAM_NAME)) {
+ goto disable_ldpc;
+ }
+
+ return 0;
+
+ disable_ldpc:
+ rwnx_hw->mod_params->ldpc_on = false;
+
+#endif /* CONFIG_RWNX_SDM */
+ return 0;
+}
+
+/**
+ * rwnx_plat_lmac_load() - Load FW code
+ *
+ * @rwnx_plat: platform data
+ */
+static int rwnx_plat_lmac_load(struct rwnx_plat *rwnx_plat)
+{
+ int ret;
+
+ #ifdef CONFIG_RWNX_TL4
+ ret = rwnx_plat_tl4_fw_upload(rwnx_plat,
+ RWNX_ADDR(rwnx_plat, RWNX_ADDR_CPU, RAM_LMAC_FW_ADDR),
+ RWNX_MAC_FW_NAME);
+ #else
+ ret = rwnx_plat_ihex_fw_upload(rwnx_plat,
+ RWNX_ADDR(rwnx_plat, RWNX_ADDR_CPU, RAM_LMAC_FW_ADDR),
+ RWNX_MAC_FW_NAME);
+ if (ret == -ENOENT)
+ {
+ ret = rwnx_plat_bin_fw_upload(rwnx_plat,
+ RWNX_ADDR(rwnx_plat, RWNX_ADDR_CPU, RAM_LMAC_FW_ADDR),
+ RWNX_MAC_FW_NAME2);
+ }
+ #endif
+
+ return ret;
+}
+#endif
+
+#ifndef CONFIG_ROM_PATCH_EN
+/**
+ * rwnx_plat_fmac_load() - Load FW code
+ *
+ * @rwnx_hw: Main driver data
+ */
+#if 0
+static int rwnx_plat_fmac_load(struct rwnx_hw *rwnx_hw)
+{
+ int ret;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+ #if defined(CONFIG_NANOPI_M4) || defined(CONFIG_PLATFORM_ALLWINNER)
+ ret = rwnx_plat_bin_fw_upload_android(rwnx_hw, RAM_FMAC_FW_ADDR, RWNX_MAC_FW_NAME2);
+ #else
+ ret = rwnx_plat_bin_fw_upload_2(rwnx_hw,
+ RAM_FMAC_FW_ADDR,
+ RWNX_MAC_FW_NAME2);
+ #endif
+ return ret;
+}
+#endif
+#endif /* !CONFIG_ROM_PATCH_EN */
+
+#if 0
+/**
+ * rwnx_plat_mpif_sel() - Select the MPIF according to the FPGA signature
+ *
+ * @rwnx_plat: platform data
+ */
+static void rwnx_plat_mpif_sel(struct rwnx_plat *rwnx_plat)
+{
+#ifndef CONFIG_RWNX_SDM
+ u32 regval;
+ u32 type;
+
+ /* Get the FPGA signature */
+ regval = RWNX_REG_READ(rwnx_plat, RWNX_ADDR_SYSTEM, SYSCTRL_SIGNATURE_ADDR);
+ type = __FPGA_TYPE(regval);
+
+ /* Check if we need to switch to the old MPIF or not */
+ if ((type != 0xCAFE) && (type != 0XC0CA) && (regval & 0xF) < 0x3)
+ {
+ /* A old FPGA A is used, so configure the FPGA B to use the old MPIF */
+ RWNX_REG_WRITE(0x3, rwnx_plat, RWNX_ADDR_SYSTEM, FPGAB_MPIF_SEL_ADDR);
+ }
+#endif
+}
+#endif
+#ifdef CONFIG_DPD
+int is_file_exist(char* name)
+{
+ char *path = NULL;
+ struct file *fp = NULL;
+ int len;
+
+ path = __getname();
+ if (!path) {
+ AICWFDBG(LOGINFO, "%s getname fail\n", __func__);
+ return -1;
+ }
+
+ len = snprintf(path, FW_PATH_MAX_LEN, "%s/%s", aic_fw_path, name);
+
+ fp = filp_open(path, O_RDONLY, 0);
+ if (IS_ERR(fp)) {
+ __putname(path);
+ fp = NULL;
+ return 0;
+ } else {
+ __putname(path);
+ filp_close(fp, NULL);
+ fp = NULL;
+ return 1;
+ }
+}
+#endif//CONFIG_DPD
+/**
+ * rwnx_plat_patch_load() - Load patch code
+ *
+ * @rwnx_hw: Main driver data
+ */
+#ifdef CONFIG_ROM_PATCH_EN
+
+
+static int rwnx_plat_patch_load(struct rwnx_hw *rwnx_hw)
+{
+ int ret = 0;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ if(rwnx_hw->usbdev->chipid == PRODUCT_ID_AIC8800DC ||
+ rwnx_hw->usbdev->chipid == PRODUCT_ID_AIC8800DW){
+#ifndef ANDROID_PLATFORM
+ sprintf(aic_fw_path, "%s/%s", aic_fw_path, "aic8800DC");
+#endif
+#ifdef CONFIG_DPD
+ if (chip_sub_id == 1) {
+ AICWFDBG(LOGINFO, "testmode=%d\n", testmode);
+ if (testmode == FW_NORMAL_MODE) {
+ if (is_file_exist(FW_DPDRESULT_NAME_8800DC) == 1) {
+ AICWFDBG(LOGINFO, "dpd bin load\n");
+ ret = aicwf_dpd_result_load_8800dc(rwnx_hw);
+ if (ret) {
+ AICWFDBG(LOGINFO, "load dpd bin fail: %d\n", ret);
+ return ret;
+ }
+ } else {
+ aicwf_misc_ram_init_8800dc(rwnx_hw);
+ }
+ } else if (testmode == FW_DPDCALIB_MODE) {
+ if (is_file_exist(FW_DPDRESULT_NAME_8800DC) == 0) {
+ uint32_t dpd_res[DPD_RESULT_SIZE_8800DC / 4] = {0,};
+ AICWFDBG(LOGINFO, "dpd calib & write\n");
+ ret = aicwf_dpd_calib_8800dc(rwnx_hw, &dpd_res[0]);
+ if (ret) {
+ AICWFDBG(LOGINFO, "dpd calib fail: %d\n", ret);
+ return ret;
+ }
+ ret = aicwf_dpd_result_write_8800dc((void *)dpd_res, DPD_RESULT_SIZE_8800DC);
+ if (ret) {
+ AICWFDBG(LOGINFO, "file write fail: %d\n", ret);
+ return ret;
+ }
+ }
+ return 1; // exit calib mode
+ }
+ }
+#else
+ if (chip_sub_id == 1) {
+ aicwf_misc_ram_init_8800dc(rwnx_hw);
+ }
+#endif
+
+ ret = aicwf_plat_patch_load_8800dc(rwnx_hw);
+ if (!ret) {
+ aicwf_patch_config_8800dc(rwnx_hw);
+ }
+ }
+
+ return ret;
+}
+#endif
+
+
+/**
+ * rwnx_platform_reset() - Reset the platform
+ *
+ * @rwnx_plat: platform data
+ */
+static int rwnx_platform_reset(struct rwnx_plat *rwnx_plat)
+{
+ u32 regval;
+
+#if defined(AICWF_USB_SUPPORT) || defined(AICWF_SDIO_SUPPORT)
+ return 0;
+#endif
+
+ /* the doc states that SOFT implies FPGA_B_RESET
+ * adding FPGA_B_RESET is clearer */
+ RWNX_REG_WRITE(SOFT_RESET | FPGA_B_RESET, rwnx_plat,
+ RWNX_ADDR_SYSTEM, SYSCTRL_MISC_CNTL_ADDR);
+ msleep(100);
+
+ regval = RWNX_REG_READ(rwnx_plat, RWNX_ADDR_SYSTEM, SYSCTRL_MISC_CNTL_ADDR);
+
+ if (regval & SOFT_RESET) {
+ dev_err(rwnx_platform_get_dev(rwnx_plat), "reset: failed\n");
+ return -EIO;
+ }
+
+ RWNX_REG_WRITE(regval & ~FPGA_B_RESET, rwnx_plat,
+ RWNX_ADDR_SYSTEM, SYSCTRL_MISC_CNTL_ADDR);
+ msleep(100);
+ return 0;
+}
+
+/**
+ * rwmx_platform_save_config() - Save hardware config before reload
+ *
+ * @rwnx_plat: Pointer to platform data
+ *
+ * Return configuration registers values.
+ */
+static void* rwnx_term_save_config(struct rwnx_plat *rwnx_plat)
+{
+ const u32 *reg_list;
+ u32 *reg_value, *res;
+ int i, size = 0;
+
+ if (rwnx_plat->get_config_reg) {
+ size = rwnx_plat->get_config_reg(rwnx_plat, &reg_list);
+ }
+
+ if (size <= 0)
+ return NULL;
+
+ res = kmalloc(sizeof(u32) * size, GFP_KERNEL);
+ if (!res)
+ return NULL;
+
+ reg_value = res;
+ for (i = 0; i < size; i++) {
+ *reg_value++ = RWNX_REG_READ(rwnx_plat, RWNX_ADDR_SYSTEM,
+ *reg_list++);
+ }
+
+ return res;
+}
+
+#if 0
+/**
+ * rwmx_platform_restore_config() - Restore hardware config after reload
+ *
+ * @rwnx_plat: Pointer to platform data
+ * @reg_value: Pointer of value to restore
+ * (obtained with rwmx_platform_save_config())
+ *
+ * Restore configuration registers value.
+ */
+static void rwnx_term_restore_config(struct rwnx_plat *rwnx_plat,
+ u32 *reg_value)
+{
+ const u32 *reg_list;
+ int i, size = 0;
+
+ if (!reg_value || !rwnx_plat->get_config_reg)
+ return;
+
+ size = rwnx_plat->get_config_reg(rwnx_plat, &reg_list);
+
+ for (i = 0; i < size; i++) {
+ RWNX_REG_WRITE(*reg_value++, rwnx_plat, RWNX_ADDR_SYSTEM,
+ *reg_list++);
+ }
+}
+#endif
+
+#ifndef CONFIG_RWNX_FHOST
+#if 0
+static int rwnx_check_fw_compatibility(struct rwnx_hw *rwnx_hw)
+{
+ struct ipc_shared_env_tag *shared = rwnx_hw->ipc_env->shared;
+ #ifdef CONFIG_RWNX_FULLMAC
+ struct wiphy *wiphy = rwnx_hw->wiphy;
+ #endif //CONFIG_RWNX_FULLMAC
+ #ifdef CONFIG_RWNX_OLD_IPC
+ int ipc_shared_version = 10;
+ #else //CONFIG_RWNX_OLD_IPC
+ int ipc_shared_version = 11;
+ #endif //CONFIG_RWNX_OLD_IPC
+ int res = 0;
+
+ if(shared->comp_info.ipc_shared_version != ipc_shared_version)
+ {
+ wiphy_err(wiphy, "Different versions of IPC shared version between driver and FW (%d != %d)\n ",
+ ipc_shared_version, shared->comp_info.ipc_shared_version);
+ res = -1;
+ }
+
+ if(shared->comp_info.radarbuf_cnt != IPC_RADARBUF_CNT)
+ {
+ wiphy_err(wiphy, "Different number of host buffers available for Radar events handling "\
+ "between driver and FW (%d != %d)\n", IPC_RADARBUF_CNT,
+ shared->comp_info.radarbuf_cnt);
+ res = -1;
+ }
+
+ if(shared->comp_info.unsuprxvecbuf_cnt != IPC_UNSUPRXVECBUF_CNT)
+ {
+ wiphy_err(wiphy, "Different number of host buffers available for unsupported Rx vectors "\
+ "handling between driver and FW (%d != %d)\n", IPC_UNSUPRXVECBUF_CNT,
+ shared->comp_info.unsuprxvecbuf_cnt);
+ res = -1;
+ }
+
+ #ifdef CONFIG_RWNX_FULLMAC
+ if(shared->comp_info.rxdesc_cnt != IPC_RXDESC_CNT)
+ {
+ wiphy_err(wiphy, "Different number of shared descriptors available for Data RX handling "\
+ "between driver and FW (%d != %d)\n", IPC_RXDESC_CNT,
+ shared->comp_info.rxdesc_cnt);
+ res = -1;
+ }
+ #endif /* CONFIG_RWNX_FULLMAC */
+
+ if(shared->comp_info.rxbuf_cnt != IPC_RXBUF_CNT)
+ {
+ wiphy_err(wiphy, "Different number of host buffers available for Data Rx handling "\
+ "between driver and FW (%d != %d)\n", IPC_RXBUF_CNT,
+ shared->comp_info.rxbuf_cnt);
+ res = -1;
+ }
+
+ if(shared->comp_info.msge2a_buf_cnt != IPC_MSGE2A_BUF_CNT)
+ {
+ wiphy_err(wiphy, "Different number of host buffers available for Emb->App MSGs "\
+ "sending between driver and FW (%d != %d)\n", IPC_MSGE2A_BUF_CNT,
+ shared->comp_info.msge2a_buf_cnt);
+ res = -1;
+ }
+
+ if(shared->comp_info.dbgbuf_cnt != IPC_DBGBUF_CNT)
+ {
+ wiphy_err(wiphy, "Different number of host buffers available for debug messages "\
+ "sending between driver and FW (%d != %d)\n", IPC_DBGBUF_CNT,
+ shared->comp_info.dbgbuf_cnt);
+ res = -1;
+ }
+
+ if(shared->comp_info.bk_txq != NX_TXDESC_CNT0)
+ {
+ wiphy_err(wiphy, "Driver and FW have different sizes of BK TX queue (%d != %d)\n",
+ NX_TXDESC_CNT0, shared->comp_info.bk_txq);
+ res = -1;
+ }
+
+ if(shared->comp_info.be_txq != NX_TXDESC_CNT1)
+ {
+ wiphy_err(wiphy, "Driver and FW have different sizes of BE TX queue (%d != %d)\n",
+ NX_TXDESC_CNT1, shared->comp_info.be_txq);
+ res = -1;
+ }
+
+ if(shared->comp_info.vi_txq != NX_TXDESC_CNT2)
+ {
+ wiphy_err(wiphy, "Driver and FW have different sizes of VI TX queue (%d != %d)\n",
+ NX_TXDESC_CNT2, shared->comp_info.vi_txq);
+ res = -1;
+ }
+
+ if(shared->comp_info.vo_txq != NX_TXDESC_CNT3)
+ {
+ wiphy_err(wiphy, "Driver and FW have different sizes of VO TX queue (%d != %d)\n",
+ NX_TXDESC_CNT3, shared->comp_info.vo_txq);
+ res = -1;
+ }
+
+ #if NX_TXQ_CNT == 5
+ if(shared->comp_info.bcn_txq != NX_TXDESC_CNT4)
+ {
+ wiphy_err(wiphy, "Driver and FW have different sizes of BCN TX queue (%d != %d)\n",
+ NX_TXDESC_CNT4, shared->comp_info.bcn_txq);
+ res = -1;
+ }
+ #else
+ if (shared->comp_info.bcn_txq > 0)
+ {
+ wiphy_err(wiphy, "BCMC enabled in firmware but disabled in driver\n");
+ res = -1;
+ }
+ #endif /* NX_TXQ_CNT == 5 */
+
+ if(shared->comp_info.ipc_shared_size != sizeof(ipc_shared_env))
+ {
+ wiphy_err(wiphy, "Different sizes of IPC shared between driver and FW (%zd != %d)\n",
+ sizeof(ipc_shared_env), shared->comp_info.ipc_shared_size);
+ res = -1;
+ }
+
+ if(shared->comp_info.msg_api != MSG_API_VER)
+ {
+ wiphy_warn(wiphy, "WARNING: Different supported message API versions between "\
+ "driver and FW (%d != %d)\n", MSG_API_VER, shared->comp_info.msg_api);
+ }
+
+ return res;
+}
+#endif
+#endif /* !CONFIG_RWNX_FHOST */
+
+int rwnx_atoi(char *value)
+{
+ int len = 0;
+ int i = 0;
+ int result = 0;
+ int flag = 1;
+
+ if (value[0] == '-') {
+ flag = -1;
+ value++;
+ }
+ len = strlen(value);
+
+ for (i = 0;i < len ;i++) {
+ result = result * 10;
+ if (value[i] >= 48 && value[i] <= 57) {
+ result += value[i] - 48;
+ } else {
+ result = 0;
+ break;
+ }
+ }
+
+ return result * flag;
+}
+
+void get_userconfig_txpwr_lvl_in_fdrv(txpwr_lvl_conf_t *txpwr_lvl)
+{
+ txpwr_lvl->enable = userconfig_info.txpwr_lvl.enable;
+ txpwr_lvl->dsss = userconfig_info.txpwr_lvl.dsss;
+ txpwr_lvl->ofdmlowrate_2g4 = userconfig_info.txpwr_lvl.ofdmlowrate_2g4;
+ txpwr_lvl->ofdm64qam_2g4 = userconfig_info.txpwr_lvl.ofdm64qam_2g4;
+ txpwr_lvl->ofdm256qam_2g4 = userconfig_info.txpwr_lvl.ofdm256qam_2g4;
+ txpwr_lvl->ofdm1024qam_2g4 = userconfig_info.txpwr_lvl.ofdm1024qam_2g4;
+ txpwr_lvl->ofdmlowrate_5g = userconfig_info.txpwr_lvl.ofdmlowrate_5g;
+ txpwr_lvl->ofdm64qam_5g = userconfig_info.txpwr_lvl.ofdm64qam_5g;
+ txpwr_lvl->ofdm256qam_5g = userconfig_info.txpwr_lvl.ofdm256qam_5g;
+ txpwr_lvl->ofdm1024qam_5g = userconfig_info.txpwr_lvl.ofdm1024qam_5g;
+
+ AICWFDBG(LOGINFO, "%s:enable:%d\r\n", __func__, txpwr_lvl->enable);
+ AICWFDBG(LOGINFO, "%s:dsss:%d\r\n", __func__, txpwr_lvl->dsss);
+ AICWFDBG(LOGINFO, "%s:ofdmlowrate_2g4:%d\r\n", __func__, txpwr_lvl->ofdmlowrate_2g4);
+ AICWFDBG(LOGINFO, "%s:ofdm64qam_2g4:%d\r\n", __func__, txpwr_lvl->ofdm64qam_2g4);
+ AICWFDBG(LOGINFO, "%s:ofdm256qam_2g4:%d\r\n", __func__, txpwr_lvl->ofdm256qam_2g4);
+ AICWFDBG(LOGINFO, "%s:ofdm1024qam_2g4:%d\r\n", __func__, txpwr_lvl->ofdm1024qam_2g4);
+ AICWFDBG(LOGINFO, "%s:ofdmlowrate_5g:%d\r\n", __func__, txpwr_lvl->ofdmlowrate_5g);
+ AICWFDBG(LOGINFO, "%s:ofdm64qam_5g:%d\r\n", __func__, txpwr_lvl->ofdm64qam_5g);
+ AICWFDBG(LOGINFO, "%s:ofdm256qam_5g:%d\r\n", __func__, txpwr_lvl->ofdm256qam_5g);
+ AICWFDBG(LOGINFO, "%s:ofdm1024qam_5g:%d\r\n", __func__, txpwr_lvl->ofdm1024qam_5g);
+}
+
+void get_userconfig_txpwr_lvl_v2_in_fdrv(txpwr_lvl_conf_v2_t *txpwr_lvl_v2)
+{
+ *txpwr_lvl_v2 = userconfig_info.txpwr_lvl_v2;
+
+ AICWFDBG(LOGINFO, "%s:enable:%d\r\n", __func__, txpwr_lvl_v2->enable);
+ AICWFDBG(LOGINFO, "%s:lvl_11b_11ag_1m_2g4:%d\r\n", __func__, txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[0]);
+ AICWFDBG(LOGINFO, "%s:lvl_11b_11ag_2m_2g4:%d\r\n", __func__, txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[1]);
+ AICWFDBG(LOGINFO, "%s:lvl_11b_11ag_5m5_2g4:%d\r\n", __func__, txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[2]);
+ AICWFDBG(LOGINFO, "%s:lvl_11b_11ag_11m_2g4:%d\r\n", __func__, txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[3]);
+ AICWFDBG(LOGINFO, "%s:lvl_11b_11ag_6m_2g4:%d\r\n", __func__, txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[4]);
+ AICWFDBG(LOGINFO, "%s:lvl_11b_11ag_9m_2g4:%d\r\n", __func__, txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[5]);
+ AICWFDBG(LOGINFO, "%s:lvl_11b_11ag_12m_2g4:%d\r\n", __func__, txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[6]);
+ AICWFDBG(LOGINFO, "%s:lvl_11b_11ag_18m_2g4:%d\r\n", __func__, txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[7]);
+ AICWFDBG(LOGINFO, "%s:lvl_11b_11ag_24m_2g4:%d\r\n", __func__, txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[8]);
+ AICWFDBG(LOGINFO, "%s:lvl_11b_11ag_36m_2g4:%d\r\n", __func__, txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[9]);
+ AICWFDBG(LOGINFO, "%s:lvl_11b_11ag_48m_2g4:%d\r\n", __func__, txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[10]);
+ AICWFDBG(LOGINFO, "%s:lvl_11b_11ag_54m_2g4:%d\r\n", __func__, txpwr_lvl_v2->pwrlvl_11b_11ag_2g4[11]);
+ AICWFDBG(LOGINFO, "%s:lvl_11n_11ac_mcs0_2g4:%d\r\n",__func__, txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[0]);
+ AICWFDBG(LOGINFO, "%s:lvl_11n_11ac_mcs1_2g4:%d\r\n",__func__, txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[1]);
+ AICWFDBG(LOGINFO, "%s:lvl_11n_11ac_mcs2_2g4:%d\r\n",__func__, txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[2]);
+ AICWFDBG(LOGINFO, "%s:lvl_11n_11ac_mcs3_2g4:%d\r\n",__func__, txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[3]);
+ AICWFDBG(LOGINFO, "%s:lvl_11n_11ac_mcs4_2g4:%d\r\n",__func__, txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[4]);
+ AICWFDBG(LOGINFO, "%s:lvl_11n_11ac_mcs5_2g4:%d\r\n",__func__, txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[5]);
+ AICWFDBG(LOGINFO, "%s:lvl_11n_11ac_mcs6_2g4:%d\r\n",__func__, txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[6]);
+ AICWFDBG(LOGINFO, "%s:lvl_11n_11ac_mcs7_2g4:%d\r\n",__func__, txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[7]);
+ AICWFDBG(LOGINFO, "%s:lvl_11n_11ac_mcs8_2g4:%d\r\n",__func__, txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[8]);
+ AICWFDBG(LOGINFO, "%s:lvl_11n_11ac_mcs9_2g4:%d\r\n",__func__, txpwr_lvl_v2->pwrlvl_11n_11ac_2g4[9]);
+ AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs0_2g4:%d\r\n", __func__, txpwr_lvl_v2->pwrlvl_11ax_2g4[0]);
+ AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs1_2g4:%d\r\n", __func__, txpwr_lvl_v2->pwrlvl_11ax_2g4[1]);
+ AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs2_2g4:%d\r\n", __func__, txpwr_lvl_v2->pwrlvl_11ax_2g4[2]);
+ AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs3_2g4:%d\r\n", __func__, txpwr_lvl_v2->pwrlvl_11ax_2g4[3]);
+ AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs4_2g4:%d\r\n", __func__, txpwr_lvl_v2->pwrlvl_11ax_2g4[4]);
+ AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs5_2g4:%d\r\n", __func__, txpwr_lvl_v2->pwrlvl_11ax_2g4[5]);
+ AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs6_2g4:%d\r\n", __func__, txpwr_lvl_v2->pwrlvl_11ax_2g4[6]);
+ AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs7_2g4:%d\r\n", __func__, txpwr_lvl_v2->pwrlvl_11ax_2g4[7]);
+ AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs8_2g4:%d\r\n", __func__, txpwr_lvl_v2->pwrlvl_11ax_2g4[8]);
+ AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs9_2g4:%d\r\n", __func__, txpwr_lvl_v2->pwrlvl_11ax_2g4[9]);
+ AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs10_2g4:%d\r\n", __func__, txpwr_lvl_v2->pwrlvl_11ax_2g4[10]);
+ AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs11_2g4:%d\r\n", __func__, txpwr_lvl_v2->pwrlvl_11ax_2g4[11]);
+}
+
+void get_userconfig_txpwr_lvl_v3_in_fdrv(txpwr_lvl_conf_v3_t *txpwr_lvl_v3)
+{
+ *txpwr_lvl_v3 = userconfig_info.txpwr_lvl_v3;
+
+ AICWFDBG(LOGINFO, "%s:enable:%d\r\n", __func__, txpwr_lvl_v3->enable);
+ AICWFDBG(LOGINFO, "%s:lvl_11b_11ag_1m_2g4:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11b_11ag_2g4[0]);
+ AICWFDBG(LOGINFO, "%s:lvl_11b_11ag_2m_2g4:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11b_11ag_2g4[1]);
+ AICWFDBG(LOGINFO, "%s:lvl_11b_11ag_5m5_2g4:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11b_11ag_2g4[2]);
+ AICWFDBG(LOGINFO, "%s:lvl_11b_11ag_11m_2g4:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11b_11ag_2g4[3]);
+ AICWFDBG(LOGINFO, "%s:lvl_11b_11ag_6m_2g4:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11b_11ag_2g4[4]);
+ AICWFDBG(LOGINFO, "%s:lvl_11b_11ag_9m_2g4:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11b_11ag_2g4[5]);
+ AICWFDBG(LOGINFO, "%s:lvl_11b_11ag_12m_2g4:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11b_11ag_2g4[6]);
+ AICWFDBG(LOGINFO, "%s:lvl_11b_11ag_18m_2g4:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11b_11ag_2g4[7]);
+ AICWFDBG(LOGINFO, "%s:lvl_11b_11ag_24m_2g4:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11b_11ag_2g4[8]);
+ AICWFDBG(LOGINFO, "%s:lvl_11b_11ag_36m_2g4:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11b_11ag_2g4[9]);
+ AICWFDBG(LOGINFO, "%s:lvl_11b_11ag_48m_2g4:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11b_11ag_2g4[10]);
+ AICWFDBG(LOGINFO, "%s:lvl_11b_11ag_54m_2g4:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11b_11ag_2g4[11]);
+ AICWFDBG(LOGINFO, "%s:lvl_11n_11ac_mcs0_2g4:%d\r\n",__func__, txpwr_lvl_v3->pwrlvl_11n_11ac_2g4[0]);
+ AICWFDBG(LOGINFO, "%s:lvl_11n_11ac_mcs1_2g4:%d\r\n",__func__, txpwr_lvl_v3->pwrlvl_11n_11ac_2g4[1]);
+ AICWFDBG(LOGINFO, "%s:lvl_11n_11ac_mcs2_2g4:%d\r\n",__func__, txpwr_lvl_v3->pwrlvl_11n_11ac_2g4[2]);
+ AICWFDBG(LOGINFO, "%s:lvl_11n_11ac_mcs3_2g4:%d\r\n",__func__, txpwr_lvl_v3->pwrlvl_11n_11ac_2g4[3]);
+ AICWFDBG(LOGINFO, "%s:lvl_11n_11ac_mcs4_2g4:%d\r\n",__func__, txpwr_lvl_v3->pwrlvl_11n_11ac_2g4[4]);
+ AICWFDBG(LOGINFO, "%s:lvl_11n_11ac_mcs5_2g4:%d\r\n",__func__, txpwr_lvl_v3->pwrlvl_11n_11ac_2g4[5]);
+ AICWFDBG(LOGINFO, "%s:lvl_11n_11ac_mcs6_2g4:%d\r\n",__func__, txpwr_lvl_v3->pwrlvl_11n_11ac_2g4[6]);
+ AICWFDBG(LOGINFO, "%s:lvl_11n_11ac_mcs7_2g4:%d\r\n",__func__, txpwr_lvl_v3->pwrlvl_11n_11ac_2g4[7]);
+ AICWFDBG(LOGINFO, "%s:lvl_11n_11ac_mcs8_2g4:%d\r\n",__func__, txpwr_lvl_v3->pwrlvl_11n_11ac_2g4[8]);
+ AICWFDBG(LOGINFO, "%s:lvl_11n_11ac_mcs9_2g4:%d\r\n",__func__, txpwr_lvl_v3->pwrlvl_11n_11ac_2g4[9]);
+ AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs0_2g4:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11ax_2g4[0]);
+ AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs1_2g4:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11ax_2g4[1]);
+ AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs2_2g4:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11ax_2g4[2]);
+ AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs3_2g4:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11ax_2g4[3]);
+ AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs4_2g4:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11ax_2g4[4]);
+ AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs5_2g4:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11ax_2g4[5]);
+ AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs6_2g4:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11ax_2g4[6]);
+ AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs7_2g4:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11ax_2g4[7]);
+ AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs8_2g4:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11ax_2g4[8]);
+ AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs9_2g4:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11ax_2g4[9]);
+ AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs10_2g4:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11ax_2g4[10]);
+ AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs11_2g4:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11ax_2g4[11]);
+
+ AICWFDBG(LOGINFO, "%s:lvl_11a_1m_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11a_5g[0]);
+ AICWFDBG(LOGINFO, "%s:lvl_11a_2m_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11a_5g[1]);
+ AICWFDBG(LOGINFO, "%s:lvl_11a_5m5_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11a_5g[2]);
+ AICWFDBG(LOGINFO, "%s:lvl_11a_11m_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11a_5g[3]);
+ AICWFDBG(LOGINFO, "%s:lvl_11a_6m_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11a_5g[4]);
+ AICWFDBG(LOGINFO, "%s:lvl_11a_9m_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11a_5g[5]);
+ AICWFDBG(LOGINFO, "%s:lvl_11a_12m_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11a_5g[6]);
+ AICWFDBG(LOGINFO, "%s:lvl_11a_18m_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11a_5g[7]);
+ AICWFDBG(LOGINFO, "%s:lvl_11a_24m_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11a_5g[8]);
+ AICWFDBG(LOGINFO, "%s:lvl_11a_36m_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11a_5g[9]);
+ AICWFDBG(LOGINFO, "%s:lvl_11a_48m_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11a_5g[10]);
+ AICWFDBG(LOGINFO, "%s:lvl_11a_54m_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11a_5g[11]);
+ AICWFDBG(LOGINFO, "%s:lvl_11n_11ac_mcs0_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11n_11ac_5g[0]);
+ AICWFDBG(LOGINFO, "%s:lvl_11n_11ac_mcs1_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11n_11ac_5g[1]);
+ AICWFDBG(LOGINFO, "%s:lvl_11n_11ac_mcs2_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11n_11ac_5g[2]);
+ AICWFDBG(LOGINFO, "%s:lvl_11n_11ac_mcs3_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11n_11ac_5g[3]);
+ AICWFDBG(LOGINFO, "%s:lvl_11n_11ac_mcs4_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11n_11ac_5g[4]);
+ AICWFDBG(LOGINFO, "%s:lvl_11n_11ac_mcs5_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11n_11ac_5g[5]);
+ AICWFDBG(LOGINFO, "%s:lvl_11n_11ac_mcs6_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11n_11ac_5g[6]);
+ AICWFDBG(LOGINFO, "%s:lvl_11n_11ac_mcs7_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11n_11ac_5g[7]);
+ AICWFDBG(LOGINFO, "%s:lvl_11n_11ac_mcs8_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11n_11ac_5g[8]);
+ AICWFDBG(LOGINFO, "%s:lvl_11n_11ac_mcs9_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11n_11ac_5g[9]);
+ AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs0_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11ax_5g[0]);
+ AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs1_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11ax_5g[1]);
+ AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs2_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11ax_5g[2]);
+ AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs3_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11ax_5g[3]);
+ AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs4_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11ax_5g[4]);
+ AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs5_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11ax_5g[5]);
+ AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs6_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11ax_5g[6]);
+ AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs7_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11ax_5g[7]);
+ AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs8_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11ax_5g[8]);
+ AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs9_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11ax_5g[9]);
+ AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs10_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11ax_5g[10]);
+ AICWFDBG(LOGINFO, "%s:lvl_11ax_mcs11_5g:%d\r\n", __func__, txpwr_lvl_v3->pwrlvl_11ax_5g[11]);
+}
+
+
+void get_userconfig_txpwr_ofst_in_fdrv(txpwr_ofst_conf_t *txpwr_ofst)
+{
+ txpwr_ofst->enable = userconfig_info.txpwr_ofst.enable;
+ txpwr_ofst->chan_1_4 = userconfig_info.txpwr_ofst.chan_1_4;
+ txpwr_ofst->chan_5_9 = userconfig_info.txpwr_ofst.chan_5_9;
+ txpwr_ofst->chan_10_13 = userconfig_info.txpwr_ofst.chan_10_13;
+ txpwr_ofst->chan_36_64 = userconfig_info.txpwr_ofst.chan_36_64;
+ txpwr_ofst->chan_100_120 = userconfig_info.txpwr_ofst.chan_100_120;
+ txpwr_ofst->chan_122_140 = userconfig_info.txpwr_ofst.chan_122_140;
+ txpwr_ofst->chan_142_165 = userconfig_info.txpwr_ofst.chan_142_165;
+
+ AICWFDBG(LOGINFO, "%s:enable :%d\r\n", __func__, txpwr_ofst->enable);
+ AICWFDBG(LOGINFO, "%s:chan_1_4 :%d\r\n", __func__, txpwr_ofst->chan_1_4);
+ AICWFDBG(LOGINFO, "%s:chan_5_9 :%d\r\n", __func__, txpwr_ofst->chan_5_9);
+ AICWFDBG(LOGINFO, "%s:chan_10_13 :%d\r\n", __func__, txpwr_ofst->chan_10_13);
+ AICWFDBG(LOGINFO, "%s:chan_36_64 :%d\r\n", __func__, txpwr_ofst->chan_36_64);
+ AICWFDBG(LOGINFO, "%s:chan_100_120:%d\r\n", __func__, txpwr_ofst->chan_100_120);
+ AICWFDBG(LOGINFO, "%s:chan_122_140:%d\r\n", __func__, txpwr_ofst->chan_122_140);
+ AICWFDBG(LOGINFO, "%s:chan_142_165:%d\r\n", __func__, txpwr_ofst->chan_142_165);
+}
+
+void get_userconfig_txpwr_loss(txpwr_loss_conf_t *txpwr_loss)
+{
+ txpwr_loss->loss_enable = userconfig_info.txpwr_loss.loss_enable;
+ txpwr_loss->loss_value = userconfig_info.txpwr_loss.loss_value;
+
+ AICWFDBG(LOGINFO, "%s:loss_enable:%d\r\n", __func__, txpwr_loss->loss_enable);
+ AICWFDBG(LOGINFO, "%s:loss_value:%d\r\n", __func__, txpwr_loss->loss_value);
+}
+
+void get_userconfig_xtal_cap(xtal_cap_conf_t *xtal_cap)
+{
+ *xtal_cap = userconfig_info.xtal_cap;
+
+ AICWFDBG(LOGINFO, "%s:enable :%d\r\n", __func__, xtal_cap->enable);
+ AICWFDBG(LOGINFO, "%s:xtal_cap :%d\r\n", __func__, xtal_cap->xtal_cap);
+ AICWFDBG(LOGINFO, "%s:xtal_cap_fine:%d\r\n", __func__, xtal_cap->xtal_cap_fine);
+}
+
+void rwnx_plat_nvram_set_value(char *command, char *value)
+{
+ //TODO send command
+ AICWFDBG(LOGINFO, "%s:command=%s value=%s\n", __func__, command, value);
+ if (!strcmp(command, "enable")) {
+ userconfig_info.txpwr_lvl.enable = rwnx_atoi(value);
+ userconfig_info.txpwr_lvl_v2.enable = rwnx_atoi(value);
+ userconfig_info.txpwr_lvl_v3.enable = rwnx_atoi(value);
+ } else if (!strcmp(command, "dsss")) {
+ userconfig_info.txpwr_lvl.dsss = rwnx_atoi(value);
+ } else if (!strcmp(command, "ofdmlowrate_2g4")) {
+ userconfig_info.txpwr_lvl.ofdmlowrate_2g4 = rwnx_atoi(value);
+ } else if (!strcmp(command, "ofdm64qam_2g4")) {
+ userconfig_info.txpwr_lvl.ofdm64qam_2g4 = rwnx_atoi(value);
+ } else if (!strcmp(command, "ofdm256qam_2g4")) {
+ userconfig_info.txpwr_lvl.ofdm256qam_2g4 = rwnx_atoi(value);
+ } else if (!strcmp(command, "ofdm1024qam_2g4")) {
+ userconfig_info.txpwr_lvl.ofdm1024qam_2g4 = rwnx_atoi(value);
+ } else if (!strcmp(command, "ofdmlowrate_5g")) {
+ userconfig_info.txpwr_lvl.ofdmlowrate_5g = rwnx_atoi(value);
+ } else if (!strcmp(command, "ofdm64qam_5g")) {
+ userconfig_info.txpwr_lvl.ofdm64qam_5g = rwnx_atoi(value);
+ } else if (!strcmp(command, "ofdm256qam_5g")) {
+ userconfig_info.txpwr_lvl.ofdm256qam_5g = rwnx_atoi(value);
+ } else if (!strcmp(command, "ofdm1024qam_5g")) {
+ userconfig_info.txpwr_lvl.ofdm1024qam_5g = rwnx_atoi(value);
+ } else if (!strcmp(command, "lvl_11b_11ag_1m_2g4")) {
+ userconfig_info.txpwr_lvl_v2.pwrlvl_11b_11ag_2g4[0] = rwnx_atoi(value);
+ userconfig_info.txpwr_lvl_v3.pwrlvl_11b_11ag_2g4[0] = rwnx_atoi(value);
+ } else if (!strcmp(command, "lvl_11b_11ag_2m_2g4")) {
+ userconfig_info.txpwr_lvl_v2.pwrlvl_11b_11ag_2g4[1] = rwnx_atoi(value);
+ userconfig_info.txpwr_lvl_v3.pwrlvl_11b_11ag_2g4[1] = rwnx_atoi(value);
+ } else if (!strcmp(command, "lvl_11b_11ag_5m5_2g4")) {
+ userconfig_info.txpwr_lvl_v2.pwrlvl_11b_11ag_2g4[2] = rwnx_atoi(value);
+ userconfig_info.txpwr_lvl_v3.pwrlvl_11b_11ag_2g4[2] = rwnx_atoi(value);
+ } else if (!strcmp(command, "lvl_11b_11ag_11m_2g4")) {
+ userconfig_info.txpwr_lvl_v2.pwrlvl_11b_11ag_2g4[3] = rwnx_atoi(value);
+ userconfig_info.txpwr_lvl_v3.pwrlvl_11b_11ag_2g4[3] = rwnx_atoi(value);
+ } else if (!strcmp(command, "lvl_11b_11ag_6m_2g4")) {
+ userconfig_info.txpwr_lvl_v2.pwrlvl_11b_11ag_2g4[4] = rwnx_atoi(value);
+ userconfig_info.txpwr_lvl_v3.pwrlvl_11b_11ag_2g4[4] = rwnx_atoi(value);
+ } else if (!strcmp(command, "lvl_11b_11ag_9m_2g4")) {
+ userconfig_info.txpwr_lvl_v2.pwrlvl_11b_11ag_2g4[5] = rwnx_atoi(value);
+ userconfig_info.txpwr_lvl_v3.pwrlvl_11b_11ag_2g4[5] = rwnx_atoi(value);
+ } else if (!strcmp(command, "lvl_11b_11ag_12m_2g4")) {
+ userconfig_info.txpwr_lvl_v2.pwrlvl_11b_11ag_2g4[6] = rwnx_atoi(value);
+ userconfig_info.txpwr_lvl_v3.pwrlvl_11b_11ag_2g4[6] = rwnx_atoi(value);
+ } else if (!strcmp(command, "lvl_11b_11ag_18m_2g4")) {
+ userconfig_info.txpwr_lvl_v2.pwrlvl_11b_11ag_2g4[7] = rwnx_atoi(value);
+ userconfig_info.txpwr_lvl_v3.pwrlvl_11b_11ag_2g4[7] = rwnx_atoi(value);
+ } else if (!strcmp(command, "lvl_11b_11ag_24m_2g4")) {
+ userconfig_info.txpwr_lvl_v2.pwrlvl_11b_11ag_2g4[8] = rwnx_atoi(value);
+ userconfig_info.txpwr_lvl_v3.pwrlvl_11b_11ag_2g4[8] = rwnx_atoi(value);
+ } else if (!strcmp(command, "lvl_11b_11ag_36m_2g4")) {
+ userconfig_info.txpwr_lvl_v2.pwrlvl_11b_11ag_2g4[9] = rwnx_atoi(value);
+ userconfig_info.txpwr_lvl_v3.pwrlvl_11b_11ag_2g4[9] = rwnx_atoi(value);
+ } else if (!strcmp(command, "lvl_11b_11ag_48m_2g4")) {
+ userconfig_info.txpwr_lvl_v2.pwrlvl_11b_11ag_2g4[10] = rwnx_atoi(value);
+ userconfig_info.txpwr_lvl_v3.pwrlvl_11b_11ag_2g4[10] = rwnx_atoi(value);
+ } else if (!strcmp(command, "lvl_11b_11ag_54m_2g4")) {
+ userconfig_info.txpwr_lvl_v2.pwrlvl_11b_11ag_2g4[11] = rwnx_atoi(value);
+ userconfig_info.txpwr_lvl_v3.pwrlvl_11b_11ag_2g4[11] = rwnx_atoi(value);
+ } else if (!strcmp(command, "lvl_11n_11ac_mcs0_2g4")) {
+ userconfig_info.txpwr_lvl_v2.pwrlvl_11n_11ac_2g4[0] = rwnx_atoi(value);
+ userconfig_info.txpwr_lvl_v3.pwrlvl_11n_11ac_2g4[0] = rwnx_atoi(value);
+ } else if (!strcmp(command, "lvl_11n_11ac_mcs1_2g4")) {
+ userconfig_info.txpwr_lvl_v2.pwrlvl_11n_11ac_2g4[1] = rwnx_atoi(value);
+ userconfig_info.txpwr_lvl_v3.pwrlvl_11n_11ac_2g4[1] = rwnx_atoi(value);
+ } else if (!strcmp(command, "lvl_11n_11ac_mcs2_2g4")) {
+ userconfig_info.txpwr_lvl_v2.pwrlvl_11n_11ac_2g4[2] = rwnx_atoi(value);
+ userconfig_info.txpwr_lvl_v3.pwrlvl_11n_11ac_2g4[2] = rwnx_atoi(value);
+ } else if (!strcmp(command, "lvl_11n_11ac_mcs3_2g4")) {
+ userconfig_info.txpwr_lvl_v2.pwrlvl_11n_11ac_2g4[3] = rwnx_atoi(value);
+ userconfig_info.txpwr_lvl_v3.pwrlvl_11n_11ac_2g4[3] = rwnx_atoi(value);
+ } else if (!strcmp(command, "lvl_11n_11ac_mcs4_2g4")) {
+ userconfig_info.txpwr_lvl_v2.pwrlvl_11n_11ac_2g4[4] = rwnx_atoi(value);
+ userconfig_info.txpwr_lvl_v3.pwrlvl_11n_11ac_2g4[4] = rwnx_atoi(value);
+ } else if (!strcmp(command, "lvl_11n_11ac_mcs5_2g4")) {
+ userconfig_info.txpwr_lvl_v2.pwrlvl_11n_11ac_2g4[5] = rwnx_atoi(value);
+ userconfig_info.txpwr_lvl_v3.pwrlvl_11n_11ac_2g4[5] = rwnx_atoi(value);
+ } else if (!strcmp(command, "lvl_11n_11ac_mcs6_2g4")) {
+ userconfig_info.txpwr_lvl_v2.pwrlvl_11n_11ac_2g4[6] = rwnx_atoi(value);
+ userconfig_info.txpwr_lvl_v3.pwrlvl_11n_11ac_2g4[6] = rwnx_atoi(value);
+ } else if (!strcmp(command, "lvl_11n_11ac_mcs7_2g4")) {
+ userconfig_info.txpwr_lvl_v2.pwrlvl_11n_11ac_2g4[7] = rwnx_atoi(value);
+ userconfig_info.txpwr_lvl_v3.pwrlvl_11n_11ac_2g4[7] = rwnx_atoi(value);
+ } else if (!strcmp(command, "lvl_11n_11ac_mcs8_2g4")) {
+ userconfig_info.txpwr_lvl_v2.pwrlvl_11n_11ac_2g4[8] = rwnx_atoi(value);
+ userconfig_info.txpwr_lvl_v3.pwrlvl_11n_11ac_2g4[8] = rwnx_atoi(value);
+ } else if (!strcmp(command, "lvl_11n_11ac_mcs9_2g4")) {
+ userconfig_info.txpwr_lvl_v2.pwrlvl_11n_11ac_2g4[9] = rwnx_atoi(value);
+ userconfig_info.txpwr_lvl_v3.pwrlvl_11n_11ac_2g4[9] = rwnx_atoi(value);
+ } else if (!strcmp(command, "lvl_11ax_mcs0_2g4")) {
+ userconfig_info.txpwr_lvl_v2.pwrlvl_11ax_2g4[0] = rwnx_atoi(value);
+ userconfig_info.txpwr_lvl_v3.pwrlvl_11ax_2g4[0] = rwnx_atoi(value);
+ } else if (!strcmp(command, "lvl_11ax_mcs1_2g4")) {
+ userconfig_info.txpwr_lvl_v2.pwrlvl_11ax_2g4[1] = rwnx_atoi(value);
+ userconfig_info.txpwr_lvl_v3.pwrlvl_11ax_2g4[1] = rwnx_atoi(value);
+ } else if (!strcmp(command, "lvl_11ax_mcs2_2g4")) {
+ userconfig_info.txpwr_lvl_v2.pwrlvl_11ax_2g4[2] = rwnx_atoi(value);
+ userconfig_info.txpwr_lvl_v3.pwrlvl_11ax_2g4[2] = rwnx_atoi(value);
+ } else if (!strcmp(command, "lvl_11ax_mcs3_2g4")) {
+ userconfig_info.txpwr_lvl_v2.pwrlvl_11ax_2g4[3] = rwnx_atoi(value);
+ userconfig_info.txpwr_lvl_v3.pwrlvl_11ax_2g4[3] = rwnx_atoi(value);
+ } else if (!strcmp(command, "lvl_11ax_mcs4_2g4")) {
+ userconfig_info.txpwr_lvl_v2.pwrlvl_11ax_2g4[4] = rwnx_atoi(value);
+ userconfig_info.txpwr_lvl_v3.pwrlvl_11ax_2g4[4] = rwnx_atoi(value);
+ } else if (!strcmp(command, "lvl_11ax_mcs5_2g4")) {
+ userconfig_info.txpwr_lvl_v2.pwrlvl_11ax_2g4[5] = rwnx_atoi(value);
+ userconfig_info.txpwr_lvl_v3.pwrlvl_11ax_2g4[5] = rwnx_atoi(value);
+ } else if (!strcmp(command, "lvl_11ax_mcs6_2g4")) {
+ userconfig_info.txpwr_lvl_v2.pwrlvl_11ax_2g4[6] = rwnx_atoi(value);
+ userconfig_info.txpwr_lvl_v3.pwrlvl_11ax_2g4[6] = rwnx_atoi(value);
+ } else if (!strcmp(command, "lvl_11ax_mcs7_2g4")) {
+ userconfig_info.txpwr_lvl_v2.pwrlvl_11ax_2g4[7] = rwnx_atoi(value);
+ userconfig_info.txpwr_lvl_v3.pwrlvl_11ax_2g4[7] = rwnx_atoi(value);
+ } else if (!strcmp(command, "lvl_11ax_mcs8_2g4")) {
+ userconfig_info.txpwr_lvl_v2.pwrlvl_11ax_2g4[8] = rwnx_atoi(value);
+ userconfig_info.txpwr_lvl_v3.pwrlvl_11ax_2g4[8] = rwnx_atoi(value);
+ } else if (!strcmp(command, "lvl_11ax_mcs9_2g4")) {
+ userconfig_info.txpwr_lvl_v2.pwrlvl_11ax_2g4[9] = rwnx_atoi(value);
+ userconfig_info.txpwr_lvl_v3.pwrlvl_11ax_2g4[9] = rwnx_atoi(value);
+ } else if (!strcmp(command, "lvl_11ax_mcs10_2g4")) {
+ userconfig_info.txpwr_lvl_v2.pwrlvl_11ax_2g4[10] = rwnx_atoi(value);
+ userconfig_info.txpwr_lvl_v3.pwrlvl_11ax_2g4[10] = rwnx_atoi(value);
+ } else if (!strcmp(command, "lvl_11ax_mcs11_2g4")) {
+ userconfig_info.txpwr_lvl_v2.pwrlvl_11ax_2g4[11] = rwnx_atoi(value);
+ userconfig_info.txpwr_lvl_v3.pwrlvl_11ax_2g4[11] = rwnx_atoi(value);
+ } else if (!strcmp(command, "lvl_11a_1m_5g")) {
+ userconfig_info.txpwr_lvl_v3.pwrlvl_11a_5g[0] = rwnx_atoi(value);
+ } else if (!strcmp(command, "lvl_11a_2m_5g")) {
+ userconfig_info.txpwr_lvl_v3.pwrlvl_11a_5g[1] = rwnx_atoi(value);
+ } else if (!strcmp(command, "lvl_11a_5m5_5g")) {
+ userconfig_info.txpwr_lvl_v3.pwrlvl_11a_5g[2] = rwnx_atoi(value);
+ } else if (!strcmp(command, "lvl_11a_11m_5g")) {
+ userconfig_info.txpwr_lvl_v3.pwrlvl_11a_5g[3] = rwnx_atoi(value);
+ } else if (!strcmp(command, "lvl_11a_6m_5g")) {
+ userconfig_info.txpwr_lvl_v3.pwrlvl_11a_5g[4] = rwnx_atoi(value);
+ } else if (!strcmp(command, "lvl_11a_9m_5g")) {
+ userconfig_info.txpwr_lvl_v3.pwrlvl_11a_5g[5] = rwnx_atoi(value);
+ } else if (!strcmp(command, "lvl_11a_12m_5g")) {
+ userconfig_info.txpwr_lvl_v3.pwrlvl_11a_5g[6] = rwnx_atoi(value);
+ } else if (!strcmp(command, "lvl_11a_18m_5g")) {
+ userconfig_info.txpwr_lvl_v3.pwrlvl_11a_5g[7] = rwnx_atoi(value);
+ } else if (!strcmp(command, "lvl_11a_24m_5g")) {
+ userconfig_info.txpwr_lvl_v3.pwrlvl_11a_5g[8] = rwnx_atoi(value);
+ } else if (!strcmp(command, "lvl_11a_36m_5g")) {
+ userconfig_info.txpwr_lvl_v3.pwrlvl_11a_5g[9] = rwnx_atoi(value);
+ } else if (!strcmp(command, "lvl_11a_48m_5g")) {
+ userconfig_info.txpwr_lvl_v3.pwrlvl_11a_5g[10] = rwnx_atoi(value);
+ } else if (!strcmp(command, "lvl_11a_54m_5g")) {
+ userconfig_info.txpwr_lvl_v3.pwrlvl_11a_5g[11] = rwnx_atoi(value);
+ } else if (!strcmp(command, "lvl_11n_11ac_mcs0_5g")) {
+ userconfig_info.txpwr_lvl_v3.pwrlvl_11n_11ac_5g[0] = rwnx_atoi(value);
+ } else if (!strcmp(command, "lvl_11n_11ac_mcs1_5g")) {
+ userconfig_info.txpwr_lvl_v3.pwrlvl_11n_11ac_5g[1] = rwnx_atoi(value);
+ } else if (!strcmp(command, "lvl_11n_11ac_mcs2_5g")) {
+ userconfig_info.txpwr_lvl_v3.pwrlvl_11n_11ac_5g[2] = rwnx_atoi(value);
+ } else if (!strcmp(command, "lvl_11n_11ac_mcs3_5g")) {
+ userconfig_info.txpwr_lvl_v3.pwrlvl_11n_11ac_5g[3] = rwnx_atoi(value);
+ } else if (!strcmp(command, "lvl_11n_11ac_mcs4_5g")) {
+ userconfig_info.txpwr_lvl_v3.pwrlvl_11n_11ac_5g[4] = rwnx_atoi(value);
+ } else if (!strcmp(command, "lvl_11n_11ac_mcs5_5g")) {
+ userconfig_info.txpwr_lvl_v3.pwrlvl_11n_11ac_5g[5] = rwnx_atoi(value);
+ } else if (!strcmp(command, "lvl_11n_11ac_mcs6_5g")) {
+ userconfig_info.txpwr_lvl_v3.pwrlvl_11n_11ac_5g[6] = rwnx_atoi(value);
+ } else if (!strcmp(command, "lvl_11n_11ac_mcs7_5g")) {
+ userconfig_info.txpwr_lvl_v3.pwrlvl_11n_11ac_5g[7] = rwnx_atoi(value);
+ } else if (!strcmp(command, "lvl_11n_11ac_mcs8_5g")) {
+ userconfig_info.txpwr_lvl_v3.pwrlvl_11n_11ac_5g[8] = rwnx_atoi(value);
+ } else if (!strcmp(command, "lvl_11n_11ac_mcs9_5g")) {
+ userconfig_info.txpwr_lvl_v3.pwrlvl_11n_11ac_5g[9] = rwnx_atoi(value);
+ } else if (!strcmp(command, "lvl_11ax_mcs0_5g")) {
+ userconfig_info.txpwr_lvl_v3.pwrlvl_11ax_5g[0] = rwnx_atoi(value);
+ } else if (!strcmp(command, "lvl_11ax_mcs1_5g")) {
+ userconfig_info.txpwr_lvl_v3.pwrlvl_11ax_5g[1] = rwnx_atoi(value);
+ } else if (!strcmp(command, "lvl_11ax_mcs2_5g")) {
+ userconfig_info.txpwr_lvl_v3.pwrlvl_11ax_5g[2] = rwnx_atoi(value);
+ } else if (!strcmp(command, "lvl_11ax_mcs3_5g")) {
+ userconfig_info.txpwr_lvl_v3.pwrlvl_11ax_5g[3] = rwnx_atoi(value);
+ } else if (!strcmp(command, "lvl_11ax_mcs4_5g")) {
+ userconfig_info.txpwr_lvl_v3.pwrlvl_11ax_5g[4] = rwnx_atoi(value);
+ } else if (!strcmp(command, "lvl_11ax_mcs5_5g")) {
+ userconfig_info.txpwr_lvl_v3.pwrlvl_11ax_5g[5] = rwnx_atoi(value);
+ } else if (!strcmp(command, "lvl_11ax_mcs6_5g")) {
+ userconfig_info.txpwr_lvl_v3.pwrlvl_11ax_5g[6] = rwnx_atoi(value);
+ } else if (!strcmp(command, "lvl_11ax_mcs7_5g")) {
+ userconfig_info.txpwr_lvl_v3.pwrlvl_11ax_5g[7] = rwnx_atoi(value);
+ } else if (!strcmp(command, "lvl_11ax_mcs8_5g")) {
+ userconfig_info.txpwr_lvl_v3.pwrlvl_11ax_5g[8] = rwnx_atoi(value);
+ } else if (!strcmp(command, "lvl_11ax_mcs9_5g")) {
+ userconfig_info.txpwr_lvl_v3.pwrlvl_11ax_5g[9] = rwnx_atoi(value);
+ } else if (!strcmp(command, "lvl_11ax_mcs10_5g")) {
+ userconfig_info.txpwr_lvl_v3.pwrlvl_11ax_5g[10] = rwnx_atoi(value);
+ } else if (!strcmp(command, "lvl_11ax_mcs11_5g")) {
+ userconfig_info.txpwr_lvl_v3.pwrlvl_11ax_5g[11] = rwnx_atoi(value);
+ } else if (!strcmp(command, "loss_enable")) {
+ userconfig_info.txpwr_loss.loss_enable = rwnx_atoi(value);
+ } else if (!strcmp(command, "loss_value")) {
+ userconfig_info.txpwr_loss.loss_value = rwnx_atoi(value);
+ } else if (!strcmp(command, "ofst_enable")) {
+ userconfig_info.txpwr_ofst.enable = rwnx_atoi(value);
+ } else if (!strcmp(command, "ofst_chan_1_4")) {
+ userconfig_info.txpwr_ofst.chan_1_4 = rwnx_atoi(value);
+ } else if (!strcmp(command, "ofst_chan_5_9")) {
+ userconfig_info.txpwr_ofst.chan_5_9 = rwnx_atoi(value);
+ } else if (!strcmp(command, "ofst_chan_10_13")) {
+ userconfig_info.txpwr_ofst.chan_10_13 = rwnx_atoi(value);
+ } else if (!strcmp(command, "ofst_chan_36_64")) {
+ userconfig_info.txpwr_ofst.chan_36_64 = rwnx_atoi(value);
+ } else if (!strcmp(command, "ofst_chan_100_120")) {
+ userconfig_info.txpwr_ofst.chan_100_120 = rwnx_atoi(value);
+ } else if (!strcmp(command, "ofst_chan_122_140")) {
+ userconfig_info.txpwr_ofst.chan_122_140 = rwnx_atoi(value);
+ } else if (!strcmp(command, "ofst_chan_142_165")) {
+ userconfig_info.txpwr_ofst.chan_142_165 = rwnx_atoi(value);
+ } else if (!strcmp(command, "xtal_enable")) {
+ userconfig_info.xtal_cap.enable = rwnx_atoi(value);
+ } else if (!strcmp(command, "xtal_cap")) {
+ userconfig_info.xtal_cap.xtal_cap = rwnx_atoi(value);
+ } else if (!strcmp(command, "xtal_cap_fine")) {
+ userconfig_info.xtal_cap.xtal_cap_fine = rwnx_atoi(value);
+ } else {
+ AICWFDBG(LOGERROR, "invalid cmd: %s\n", command);
+ }
+
+}
+
+void rwnx_plat_userconfig_parsing(char *buffer, int size)
+{
+ int i = 0;
+ int parse_state = 0;
+ char command[30];
+ char value[100];
+ int char_counter = 0;
+
+ memset(command, 0, 30);
+ memset(value, 0, 100);
+
+ for (i = 0; i < size; i++) {
+ //Send command or print nvram log when char is \r or \n
+ if (buffer[i] == 0x0a || buffer[i] == 0x0d) {
+ if (command[0] != 0 && value[0] != 0) {
+ if (parse_state == PRINT) {
+ AICWFDBG(LOGINFO, "%s:%s\r\n", __func__, value);
+ } else if (parse_state == GET_VALUE) {
+ rwnx_plat_nvram_set_value(command, value);
+ }
+ }
+ //Reset command value and char_counter
+ memset(command, 0, 30);
+ memset(value, 0, 100);
+ char_counter = 0;
+ parse_state = INIT;
+ continue;
+ }
+
+ //Switch parser state
+ if (parse_state == INIT) {
+ if (buffer[i] == '#') {
+ parse_state = PRINT;
+ continue;
+ } else if (buffer[i] == 0x0a || buffer[i] == 0x0d) {
+ parse_state = INIT;
+ continue;
+ } else {
+ parse_state = CMD;
+ }
+ }
+
+ //Fill data to command and value
+ if (parse_state == PRINT) {
+ command[0] = 0x01;
+ value[char_counter] = buffer[i];
+ char_counter++;
+ } else if (parse_state == CMD) {
+ if (command[0] != 0 && buffer[i] == '=') {
+ parse_state = GET_VALUE;
+ char_counter = 0;
+ continue;
+ }
+ command[char_counter] = buffer[i];
+ char_counter++;
+ } else if (parse_state == GET_VALUE) {
+ value[char_counter] = buffer[i];
+ char_counter++;
+ }
+ }
+}
+
+/**
+ * rwnx_plat_userconfig_load ---Load aic_userconfig.txt
+ *@filename name of config
+*/
+static int rwnx_plat_userconfig_load(struct rwnx_hw *rwnx_hw) {
+
+ if(rwnx_hw->usbdev->chipid == PRODUCT_ID_AIC8800DC){
+ rwnx_plat_userconfig_load_8800dc(rwnx_hw);
+ }else if(rwnx_hw->usbdev->chipid == PRODUCT_ID_AIC8800DW){
+ rwnx_plat_userconfig_load_8800dw(rwnx_hw);
+ }else if(rwnx_hw->usbdev->chipid == PRODUCT_ID_AIC8800D81){
+ rwnx_plat_userconfig_load_8800d80(rwnx_hw);
+ }
+
+ return 0;
+}
+
+
+/**
+ * rwnx_platform_on() - Start the platform
+ *
+ * @rwnx_hw: Main driver data
+ * @config: Config to restore (NULL if nothing to restore)
+ *
+ * It starts the platform :
+ * - load fw and ucodes
+ * - initialize IPC
+ * - boot the fw
+ * - enable link communication/IRQ
+ *
+ * Called by 802.11 part
+ */
+int rwnx_platform_on(struct rwnx_hw *rwnx_hw, void *config)
+{
+ #if 0
+ u8 *shared_ram;
+ #endif
+#ifdef CONFIG_ROM_PATCH_EN
+ int ret = 0;
+#endif
+
+ struct rwnx_plat *rwnx_plat = rwnx_hw->plat;
+
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ if (rwnx_plat->enabled)
+ return 0;
+
+ #if 0
+ if (rwnx_platform_reset(rwnx_plat))
+ return -1;
+
+ rwnx_plat_mpif_sel(rwnx_plat);
+
+ if ((ret = rwnx_plat_fcu_load(rwnx_hw)))
+ return ret;
+ if ((ret = rwnx_plat_agc_load(rwnx_plat)))
+ return ret;
+ if ((ret = rwnx_ldpc_load(rwnx_hw)))
+ return ret;
+ if ((ret = rwnx_plat_lmac_load(rwnx_plat)))
+ return ret;
+
+ shared_ram = RWNX_ADDR(rwnx_plat, RWNX_ADDR_SYSTEM, SHARED_RAM_START_ADDR);
+ if ((ret = rwnx_ipc_init(rwnx_hw, shared_ram)))
+ return ret;
+
+ if ((ret = rwnx_plat->enable(rwnx_hw)))
+ return ret;
+ RWNX_REG_WRITE(BOOTROM_ENABLE, rwnx_plat,
+ RWNX_ADDR_SYSTEM, SYSCTRL_MISC_CNTL_ADDR);
+
+ #if 0
+ if ((ret = rwnx_fw_trace_config_filters(rwnx_get_shared_trace_buf(rwnx_hw),
+ rwnx_ipc_fw_trace_desc_get(rwnx_hw),
+ rwnx_hw->mod_params->ftl)))
+ #endif
+
+ #ifndef CONFIG_RWNX_FHOST
+ if ((ret = rwnx_check_fw_compatibility(rwnx_hw)))
+ {
+ rwnx_hw->plat->disable(rwnx_hw);
+ tasklet_kill(&rwnx_hw->task);
+ rwnx_ipc_deinit(rwnx_hw);
+ return ret;
+ }
+ #endif /* !CONFIG_RWNX_FHOST */
+
+ if (config)
+ rwnx_term_restore_config(rwnx_plat, config);
+
+ rwnx_ipc_start(rwnx_hw);
+ #else
+ #ifndef CONFIG_ROM_PATCH_EN
+ #ifdef CONFIG_DOWNLOAD_FW
+ if ((ret = rwnx_plat_fmac_load(rwnx_hw)))
+ return ret;
+ #endif /* !CONFIG_ROM_PATCH_EN */
+ #endif
+ #endif
+
+#ifdef CONFIG_ROM_PATCH_EN
+ ret = rwnx_plat_patch_load(rwnx_hw);
+ if (ret) {
+ return ret;
+ }
+#endif
+
+ rwnx_plat_userconfig_load(rwnx_hw);
+
+
+ //rwnx_plat->enabled = true;
+
+ return 0;
+}
+
+/**
+ * rwnx_platform_off() - Stop the platform
+ *
+ * @rwnx_hw: Main driver data
+ * @config: Updated with pointer to config, to be able to restore it with
+ * rwnx_platform_on(). It's up to the caller to free the config. Set to NULL
+ * if configuration is not needed.
+ *
+ * Called by 802.11 part
+ */
+void rwnx_platform_off(struct rwnx_hw *rwnx_hw, void **config)
+{
+#if defined(AICWF_USB_SUPPORT) || defined(AICWF_SDIO_SUPPORT)
+ tasklet_kill(&rwnx_hw->task);
+ rwnx_hw->plat->enabled = false;
+ return ;
+#endif
+
+ if (!rwnx_hw->plat->enabled) {
+ if (config)
+ *config = NULL;
+ return;
+ }
+
+#ifdef AICWF_PCIE_SUPPORT
+ rwnx_ipc_stop(rwnx_hw);
+#endif
+
+ if (config)
+ *config = rwnx_term_save_config(rwnx_hw->plat);
+
+ rwnx_hw->plat->disable(rwnx_hw);
+
+ tasklet_kill(&rwnx_hw->task);
+
+#ifdef AICWF_PCIE_SUPPORT
+ rwnx_ipc_deinit(rwnx_hw);
+#endif
+
+
+ rwnx_platform_reset(rwnx_hw->plat);
+
+ rwnx_hw->plat->enabled = false;
+}
+
+/**
+ * rwnx_platform_init() - Initialize the platform
+ *
+ * @rwnx_plat: platform data (already updated by platform driver)
+ * @platform_data: Pointer to store the main driver data pointer (aka rwnx_hw)
+ * That will be set as driver data for the platform driver
+ * Return: 0 on success, < 0 otherwise
+ *
+ * Called by the platform driver after it has been probed
+ */
+int rwnx_platform_init(struct rwnx_plat *rwnx_plat, void **platform_data)
+{
+ int ret = 0;
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+ rwnx_plat->enabled = false;
+ rwnx_plat->wait_disconnect_cb = false;
+ g_rwnx_plat = rwnx_plat;
+
+#if defined CONFIG_RWNX_FULLMAC
+ AICWFDBG(LOGINFO, "%s rwnx_cfg80211_init enter \r\n", __func__);
+ ret = rwnx_cfg80211_init(rwnx_plat, platform_data);
+ AICWFDBG(LOGINFO, "%s rwnx_cfg80211_init exit \r\n", __func__);
+#if defined(AICWF_USB_SUPPORT) && defined(CONFIG_VENDOR_GPIO)
+ // initialize gpiob2, gpiob3, gpiob5 to output mode and set output to 0
+ rwnx_send_dbg_gpio_init_req(rwnx_plat->usbdev->rwnx_hw, 2, 1, 0);//gpiob 2 = 0
+ rwnx_send_dbg_gpio_init_req(rwnx_plat->usbdev->rwnx_hw, 3, 1, 0);//gpiob 3 = 0
+ rwnx_send_dbg_gpio_init_req(rwnx_plat->usbdev->rwnx_hw, 5, 1, 0);//gpiob 5 = 0
+
+ // read gpiob2
+ //struct dbg_gpio_read_cfm gpio_rd_cfm;
+ //rwnx_send_dbg_gpio_read_req(rwnx_plat->usbdev->rwnx_hw, 2, &gpio_rd_cfm);
+ //AICWFDBG(LOGINFO, "gpio_rd_cfm idx:%d val:%d\n", gpio_rd_cfm.gpio_idx, gpio_rd_cfm.gpio_val);
+
+ // set gpiob2 output to 1
+ //rwnx_send_dbg_gpio_write_req(rwnx_plat->usbdev->rwnx_hw, 2, 1);
+#endif
+
+ return ret;
+#elif defined CONFIG_RWNX_FHOST
+ return rwnx_fhost_init(rwnx_plat, platform_data);
+#endif
+}
+
+/**
+ * rwnx_platform_deinit() - Deinitialize the platform
+ *
+ * @rwnx_hw: main driver data
+ *
+ * Called by the platform driver after it is removed
+ */
+void rwnx_platform_deinit(struct rwnx_hw *rwnx_hw)
+{
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+
+#if defined CONFIG_RWNX_FULLMAC
+ rwnx_cfg80211_deinit(rwnx_hw);
+#elif defined CONFIG_RWNX_FHOST
+ rwnx_fhost_deinit(rwnx_hw);
+#endif
+}
+
+/**
+ * rwnx_platform_register_drv() - Register all possible platform drivers
+ */
+int rwnx_platform_register_drv(void)
+{
+ return rwnx_pci_register_drv();
+}
+
+
+/**
+ * rwnx_platform_unregister_drv() - Unegister all platform drivers
+ */
+void rwnx_platform_unregister_drv(void)
+{
+ return rwnx_pci_unregister_drv();
+}
+
+struct device *rwnx_platform_get_dev(struct rwnx_plat *rwnx_plat)
+{
+#ifdef AICWF_SDIO_SUPPORT
+ return rwnx_plat->sdiodev->dev;
+#endif
+#ifdef AICWF_USB_SUPPORT
+ return rwnx_plat->usbdev->dev;
+#endif
+ return &(rwnx_plat->pci_dev->dev);
+}
+
+
+#ifndef CONFIG_RWNX_SDM
+MODULE_FIRMWARE(RWNX_AGC_FW_NAME);
+MODULE_FIRMWARE(RWNX_FCU_FW_NAME);
+MODULE_FIRMWARE(RWNX_LDPC_RAM_NAME);
+#endif
+MODULE_FIRMWARE(RWNX_MAC_FW_NAME);
+#ifndef CONFIG_RWNX_TL4
+MODULE_FIRMWARE(RWNX_MAC_FW_NAME2);
+#endif
+
+
diff --git a/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_platform.h b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_platform.h
new file mode 100644
index 000000000000..2df790ce8d2c
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_platform.h
@@ -0,0 +1,142 @@
+/**
+ ******************************************************************************
+ *
+ * @file rwnx_platorm.h
+ *
+ * Copyright (C) RivieraWaves 2012-2019
+ *
+ ******************************************************************************
+ */
+
+#ifndef _RWNX_PLATFORM_H_
+#define _RWNX_PLATFORM_H_
+
+#include <linux/pci.h>
+#include "lmac_msg.h"
+
+
+#define RWNX_CONFIG_FW_NAME "rwnx_settings.ini"
+#define RWNX_PHY_CONFIG_TRD_NAME "rwnx_trident.ini"
+#define RWNX_PHY_CONFIG_KARST_NAME "rwnx_karst.ini"
+#define RWNX_AGC_FW_NAME "agcram.bin"
+#define RWNX_LDPC_RAM_NAME "ldpcram.bin"
+#ifdef CONFIG_RWNX_FULLMAC
+#define RWNX_MAC_FW_BASE_NAME "fmacfw"
+#elif defined CONFIG_RWNX_FHOST
+#define RWNX_MAC_FW_BASE_NAME "fhostfw"
+#endif /* CONFIG_RWNX_FULLMAC */
+
+#ifdef CONFIG_RWNX_TL4
+#define RWNX_MAC_FW_NAME RWNX_MAC_FW_BASE_NAME".hex"
+#else
+#define RWNX_MAC_FW_NAME RWNX_MAC_FW_BASE_NAME".ihex"
+#define RWNX_MAC_FW_NAME2 RWNX_MAC_FW_BASE_NAME".bin"
+#endif
+
+#define RWNX_FCU_FW_NAME "fcuram.bin"
+#ifdef CONFIG_DPD
+#define FW_DPDRESULT_NAME_8800DC "aic_dpdresult_8800dc.bin"
+#endif
+
+enum {
+ FW_NORMAL_MODE = 0,
+ FW_RFTEST_MODE = 1,
+ FW_BLE_SCAN_WAKEUP_MODE = 2,
+ FW_M2D_OTA_MODE = 3,
+ FW_DPDCALIB_MODE = 4,
+ FW_BLE_SCAN_AD_FILTER_MODE = 5,
+};
+
+
+/**
+ * Type of memory to access (cf rwnx_plat.get_address)
+ *
+ * @RWNX_ADDR_CPU To access memory of the embedded CPU
+ * @RWNX_ADDR_SYSTEM To access memory/registers of one subsystem of the
+ * embedded system
+ *
+ */
+enum rwnx_platform_addr {
+ RWNX_ADDR_CPU,
+ RWNX_ADDR_SYSTEM,
+ RWNX_ADDR_MAX,
+};
+
+struct rwnx_hw;
+
+/**
+ * struct rwnx_plat - Operation pointers for RWNX PCI platform
+ *
+ * @pci_dev: pointer to pci dev
+ * @enabled: Set if embedded platform has been enabled (i.e. fw loaded and
+ * ipc started)
+ * @enable: Configure communication with the fw (i.e. configure the transfers
+ * enable and register interrupt)
+ * @disable: Stop communication with the fw
+ * @deinit: Free all ressources allocated for the embedded platform
+ * @get_address: Return the virtual address to access the requested address on
+ * the platform.
+ * @ack_irq: Acknowledge the irq at link level.
+ * @get_config_reg: Return the list (size + pointer) of registers to restore in
+ * order to reload the platform while keeping the current configuration.
+ *
+ * @priv Private data for the link driver
+ */
+struct rwnx_plat {
+ struct pci_dev *pci_dev;
+
+#ifdef AICWF_SDIO_SUPPORT
+ struct aic_sdio_dev *sdiodev;
+#endif
+
+#ifdef AICWF_USB_SUPPORT
+ struct aic_usb_dev *usbdev;
+#endif
+ bool enabled;
+ bool wait_disconnect_cb;
+
+ int (*enable)(struct rwnx_hw *rwnx_hw);
+ int (*disable)(struct rwnx_hw *rwnx_hw);
+ void (*deinit)(struct rwnx_plat *rwnx_plat);
+ u8* (*get_address)(struct rwnx_plat *rwnx_plat, int addr_name,
+ unsigned int offset);
+ void (*ack_irq)(struct rwnx_plat *rwnx_plat);
+ int (*get_config_reg)(struct rwnx_plat *rwnx_plat, const u32 **list);
+
+ u8 priv[0] __aligned(sizeof(void *));
+};
+
+#define RWNX_ADDR(plat, base, offset) \
+ plat->get_address(plat, base, offset)
+
+#define RWNX_REG_READ(plat, base, offset) \
+ readl(plat->get_address(plat, base, offset))
+
+#define RWNX_REG_WRITE(val, plat, base, offset) \
+ writel(val, plat->get_address(plat, base, offset))
+
+extern struct rwnx_plat *g_rwnx_plat;
+
+int rwnx_platform_init(struct rwnx_plat *rwnx_plat, void **platform_data);
+void rwnx_platform_deinit(struct rwnx_hw *rwnx_hw);
+
+int rwnx_platform_on(struct rwnx_hw *rwnx_hw, void *config);
+void rwnx_platform_off(struct rwnx_hw *rwnx_hw, void **config);
+
+int is_file_exist(char* name);
+void get_userconfig_txpwr_lvl_in_fdrv(txpwr_lvl_conf_t *txpwr_lvl);
+void get_userconfig_txpwr_lvl_v2_in_fdrv(txpwr_lvl_conf_v2_t *txpwr_lvl_v2);
+void get_userconfig_txpwr_lvl_v3_in_fdrv(txpwr_lvl_conf_v3_t *txpwr_lvl_v3);
+void get_userconfig_txpwr_ofst_in_fdrv(txpwr_ofst_conf_t *txpwr_ofst);
+void get_userconfig_txpwr_loss(txpwr_loss_conf_t *txpwr_loss);
+int rwnx_platform_register_drv(void);
+void rwnx_platform_unregister_drv(void);
+
+extern struct device *rwnx_platform_get_dev(struct rwnx_plat *rwnx_plat);
+
+static inline unsigned int rwnx_platform_get_irq(struct rwnx_plat *rwnx_plat)
+{
+ return rwnx_plat->pci_dev->irq;
+}
+
+#endif /* _RWNX_PLATFORM_H_ */
diff --git a/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_prof.h b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_prof.h
new file mode 100644
index 000000000000..fa59221575dc
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_prof.h
@@ -0,0 +1,133 @@
+/**
+ ****************************************************************************************
+ *
+ * @file rwnx_prof.h
+ *
+ * Copyright (C) RivieraWaves 2012-2019
+ *
+ ****************************************************************************************
+ */
+
+#ifndef _RWNX_PROF_H_
+#define _RWNX_PROF_H_
+
+#include "reg_access.h"
+#include "rwnx_platform.h"
+
+static inline void rwnx_prof_set(struct rwnx_hw *rwnx_hw, int val)
+{
+ struct rwnx_plat *rwnx_plat = rwnx_hw->plat;
+ RWNX_REG_WRITE(val, rwnx_plat, RWNX_ADDR_SYSTEM, NXMAC_SW_SET_PROFILING_ADDR);
+}
+
+static inline void rwnx_prof_clear(struct rwnx_hw *rwnx_hw, int val)
+{
+ struct rwnx_plat *rwnx_plat = rwnx_hw->plat;
+ RWNX_REG_WRITE(val, rwnx_plat, RWNX_ADDR_SYSTEM, NXMAC_SW_CLEAR_PROFILING_ADDR);
+}
+
+#if 0
+/* Defines for SW Profiling registers values */
+enum {
+ TX_IPC_IRQ,
+ TX_IPC_EVT,
+ TX_PREP_EVT,
+ TX_DMA_IRQ,
+ TX_MAC_IRQ,
+ TX_PAYL_HDL,
+ TX_CFM_EVT,
+ TX_IPC_CFM,
+ RX_MAC_IRQ, // 8
+ RX_TRIGGER_EVT,
+ RX_DMA_IRQ,
+ RX_DMA_EVT,
+ RX_IPC_IND,
+ RX_MPDU_XFER,
+ DBG_PROF_MAX
+};
+#endif
+
+enum {
+ SW_PROF_HOSTBUF_IDX = 12,
+ /****** IPC IRQs related signals ******/
+ /* E2A direction */
+ SW_PROF_IRQ_E2A_RXDESC = 16, // to make sure we let 16 bits available for LMAC FW
+ SW_PROF_IRQ_E2A_TXCFM,
+ SW_PROF_IRQ_E2A_DBG,
+ SW_PROF_IRQ_E2A_MSG,
+ SW_PROF_IPC_MSGPUSH,
+ SW_PROF_MSGALLOC,
+ SW_PROF_MSGIND,
+ SW_PROF_DBGIND,
+
+ /* A2E direction */
+ SW_PROF_IRQ_A2E_TXCFM_BACK,
+
+ /****** Driver functions related signals ******/
+ SW_PROF_WAIT_QUEUE_STOP,
+ SW_PROF_WAIT_QUEUE_WAKEUP,
+ SW_PROF_RWNXDATAIND,
+ SW_PROF_RWNX_IPC_IRQ_HDLR,
+ SW_PROF_RWNX_IPC_THR_IRQ_HDLR,
+ SW_PROF_IEEE80211RX,
+ SW_PROF_RWNX_PATTERN,
+ SW_PROF_MAX
+};
+
+// [LT]For debug purpose only
+#if (0)
+#define SW_PROF_CHAN_CTXT_CFM_HDL_BIT (21)
+#define SW_PROF_CHAN_CTXT_CFM_BIT (22)
+#define SW_PROF_CHAN_CTXT_CFM_SWDONE_BIT (23)
+#define SW_PROF_CHAN_CTXT_PUSH_BIT (24)
+#define SW_PROF_CHAN_CTXT_QUEUE_BIT (25)
+#define SW_PROF_CHAN_CTXT_TX_BIT (26)
+#define SW_PROF_CHAN_CTXT_TX_PAUSE_BIT (27)
+#define SW_PROF_CHAN_CTXT_PSWTCH_BIT (28)
+#define SW_PROF_CHAN_CTXT_SWTCH_BIT (29)
+
+// TO DO: update this
+
+#define REG_SW_SET_PROFILING_CHAN(env, bit) \
+ rwnx_prof_set((struct rwnx_hw*)env, BIT(bit))
+
+#define REG_SW_CLEAR_PROFILING_CHAN(env, bit) \
+ rwnx_prof_clear((struct rwnx_hw*)env, BIT(bit))
+
+#else
+#define SW_PROF_CHAN_CTXT_CFM_HDL_BIT (0)
+#define SW_PROF_CHAN_CTXT_CFM_BIT (0)
+#define SW_PROF_CHAN_CTXT_CFM_SWDONE_BIT (0)
+#define SW_PROF_CHAN_CTXT_PUSH_BIT (0)
+#define SW_PROF_CHAN_CTXT_QUEUE_BIT (0)
+#define SW_PROF_CHAN_CTXT_TX_BIT (0)
+#define SW_PROF_CHAN_CTXT_TX_PAUSE_BIT (0)
+#define SW_PROF_CHAN_CTXT_PSWTCH_BIT (0)
+#define SW_PROF_CHAN_CTXT_SWTCH_BIT (0)
+
+#define REG_SW_SET_PROFILING_CHAN(env, bit) do {} while (0)
+#define REG_SW_CLEAR_PROFILING_CHAN(env, bit) do {} while (0)
+#endif
+
+#ifdef CONFIG_RWNX_SW_PROFILING
+/* Macros for SW PRofiling registers access */
+#define REG_SW_SET_PROFILING(env, bit) \
+ rwnx_prof_set((struct rwnx_hw*)env, BIT(bit))
+
+#define REG_SW_SET_HOSTBUF_IDX_PROFILING(env, val) \
+ rwnx_prof_set((struct rwnx_hw*)env, val<<(SW_PROF_HOSTBUF_IDX))
+
+#define REG_SW_CLEAR_PROFILING(env, bit) \
+ rwnx_prof_clear((struct rwnx_hw*)env, BIT(bit))
+
+#define REG_SW_CLEAR_HOSTBUF_IDX_PROFILING(env) \
+ rwnx_prof_clear((struct rwnx_hw*)env,0x0F<<(SW_PROF_HOSTBUF_IDX))
+
+#else
+#define REG_SW_SET_PROFILING(env, value) do {} while (0)
+#define REG_SW_CLEAR_PROFILING(env, value) do {} while (0)
+#define REG_SW_SET_HOSTBUF_IDX_PROFILING(env, val) do {} while (0)
+#define REG_SW_CLEAR_HOSTBUF_IDX_PROFILING(env) do {} while (0)
+#endif
+
+#endif /* _RWNX_PROF_H_ */
diff --git a/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_radar.c b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_radar.c
new file mode 100644
index 000000000000..e6dc578ab855
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_radar.c
@@ -0,0 +1,1644 @@
+/**
+******************************************************************************
+ *
+ * @file rwnx_radar.c
+ *
+ * @brief Functions to handle radar detection
+ * Radar detection is copied (and adapted) from ath driver source code.
+ *
+ * Copyright (c) 2012 Neratec Solutions AG
+ * Copyright (C) RivieraWaves 2015-2019
+ *
+ ******************************************************************************
+ */
+#include <linux/list.h>
+#include <linux/kernel.h>
+#include <linux/ktime.h>
+#include <net/mac80211.h>
+
+#include "rwnx_radar.h"
+#include "rwnx_defs.h"
+#include "rwnx_msg_tx.h"
+#include "rwnx_events.h"
+#include "rwnx_compat.h"
+
+/*
+ * tolerated deviation of radar time stamp in usecs on both sides
+ * TODO: this might need to be HW-dependent
+ */
+#define PRI_TOLERANCE 16
+
+/**
+ * struct radar_types - contains array of patterns defined for one DFS domain
+ * @domain: DFS regulatory domain
+ * @num_radar_types: number of radar types to follow
+ * @radar_types: radar types array
+ */
+struct radar_types {
+ enum nl80211_dfs_regions region;
+ u32 num_radar_types;
+ const struct radar_detector_specs *spec_riu;
+ const struct radar_detector_specs *spec_fcu;
+};
+
+/**
+ * Type of radar waveform:
+ * RADAR_WAVEFORM_SHORT : waveform defined by
+ * - pulse width
+ * - pulse interval in a burst (pri)
+ * - number of pulses in a burst (ppb)
+ *
+ * RADAR_WAVEFORM_WEATHER :
+ * same than SHORT except that ppb is dependent of pri
+ *
+ * RADAR_WAVEFORM_INTERLEAVED :
+ * same than SHORT except there are several value of pri (interleaved)
+ *
+ * RADAR_WAVEFORM_LONG :
+ *
+ */
+enum radar_waveform_type {
+ RADAR_WAVEFORM_SHORT,
+ RADAR_WAVEFORM_WEATHER,
+ RADAR_WAVEFORM_INTERLEAVED,
+ RADAR_WAVEFORM_LONG
+};
+
+/**
+ * struct radar_detector_specs - detector specs for a radar pattern type
+ * @type_id: pattern type, as defined by regulatory
+ * @width_min: minimum radar pulse width in [us]
+ * @width_max: maximum radar pulse width in [us]
+ * @pri_min: minimum pulse repetition interval in [us] (including tolerance)
+ * @pri_max: minimum pri in [us] (including tolerance)
+ * @num_pri: maximum number of different pri for this type
+ * @ppb: pulses per bursts for this type
+ * @ppb_thresh: number of pulses required to trigger detection
+ * @max_pri_tolerance: pulse time stamp tolerance on both sides [us]
+ * @type: Type of radar waveform
+ */
+struct radar_detector_specs {
+ u8 type_id;
+ u8 width_min;
+ u8 width_max;
+ u16 pri_min;
+ u16 pri_max;
+ u8 num_pri;
+ u8 ppb;
+ u8 ppb_thresh;
+ u8 max_pri_tolerance;
+ enum radar_waveform_type type;
+};
+
+
+/* percentage on ppb threshold to trigger detection */
+#define MIN_PPB_THRESH 50
+#define PPB_THRESH(PPB) ((PPB * MIN_PPB_THRESH + 50) / 100)
+#define PRF2PRI(PRF) ((1000000 + PRF / 2) / PRF)
+
+/* width tolerance */
+#define WIDTH_TOLERANCE 2
+#define WIDTH_LOWER(X) (X)
+#define WIDTH_UPPER(X) (X)
+
+#define ETSI_PATTERN_SHORT(ID, WMIN, WMAX, PMIN, PMAX, PPB) \
+ { \
+ ID, WIDTH_LOWER(WMIN), WIDTH_UPPER(WMAX), \
+ (PRF2PRI(PMAX) - PRI_TOLERANCE), \
+ (PRF2PRI(PMIN) + PRI_TOLERANCE), 1, PPB, \
+ PPB_THRESH(PPB), PRI_TOLERANCE, RADAR_WAVEFORM_SHORT \
+ }
+
+#define ETSI_PATTERN_INTERLEAVED(ID, WMIN, WMAX, PMIN, PMAX, PRFMIN, PRFMAX, PPB) \
+ { \
+ ID, WIDTH_LOWER(WMIN), WIDTH_UPPER(WMAX), \
+ (PRF2PRI(PMAX) * PRFMIN- PRI_TOLERANCE), \
+ (PRF2PRI(PMIN) * PRFMAX + PRI_TOLERANCE), \
+ PRFMAX, PPB * PRFMAX, \
+ PPB_THRESH(PPB), PRI_TOLERANCE, RADAR_WAVEFORM_INTERLEAVED \
+ }
+
+/* radar types as defined by ETSI EN-301-893 v1.7.1 */
+static const struct radar_detector_specs etsi_radar_ref_types_v17_riu[] = {
+ ETSI_PATTERN_SHORT(0, 0, 8, 700, 700, 18),
+ ETSI_PATTERN_SHORT(1, 0, 10, 200, 1000, 10),
+ ETSI_PATTERN_SHORT(2, 0, 22, 200, 1600, 15),
+ ETSI_PATTERN_SHORT(3, 0, 22, 2300, 4000, 25),
+ ETSI_PATTERN_SHORT(4, 20, 38, 2000, 4000, 20),
+ ETSI_PATTERN_INTERLEAVED(5, 0, 8, 300, 400, 2, 3, 10),
+ ETSI_PATTERN_INTERLEAVED(6, 0, 8, 400, 1200, 2, 3, 15),
+};
+
+static const struct radar_detector_specs etsi_radar_ref_types_v17_fcu[] = {
+ ETSI_PATTERN_SHORT(0, 0, 8, 700, 700, 18),
+ ETSI_PATTERN_SHORT(1, 0, 8, 200, 1000, 10),
+ ETSI_PATTERN_SHORT(2, 0, 16, 200, 1600, 15),
+ ETSI_PATTERN_SHORT(3, 0, 16, 2300, 4000, 25),
+ ETSI_PATTERN_SHORT(4, 20, 34, 2000, 4000, 20),
+ ETSI_PATTERN_INTERLEAVED(5, 0, 8, 300, 400, 2, 3, 10),
+ ETSI_PATTERN_INTERLEAVED(6, 0, 8, 400, 1200, 2, 3, 15),
+};
+
+static const struct radar_types etsi_radar_types_v17 = {
+ .region = NL80211_DFS_ETSI,
+ .num_radar_types = ARRAY_SIZE(etsi_radar_ref_types_v17_riu),
+ .spec_riu = etsi_radar_ref_types_v17_riu,
+ .spec_fcu = etsi_radar_ref_types_v17_fcu,
+};
+
+#define FCC_PATTERN(ID, WMIN, WMAX, PMIN, PMAX, PRF, PPB, TYPE) \
+ { \
+ ID, WIDTH_LOWER(WMIN), WIDTH_UPPER(WMAX), \
+ PMIN - PRI_TOLERANCE, \
+ PMAX * PRF + PRI_TOLERANCE, PRF, PPB * PRF, \
+ PPB_THRESH(PPB), PRI_TOLERANCE, TYPE \
+ }
+
+static const struct radar_detector_specs fcc_radar_ref_types_riu[] = {
+ FCC_PATTERN(0, 0, 8, 1428, 1428, 1, 18, RADAR_WAVEFORM_SHORT),
+ FCC_PATTERN(1, 0, 8, 518, 3066, 1, 102, RADAR_WAVEFORM_WEATHER),
+ FCC_PATTERN(2, 0, 8, 150, 230, 1, 23, RADAR_WAVEFORM_SHORT),
+ FCC_PATTERN(3, 6, 20, 200, 500, 1, 16, RADAR_WAVEFORM_SHORT),
+ FCC_PATTERN(4, 10, 28, 200, 500, 1, 12, RADAR_WAVEFORM_SHORT),
+ FCC_PATTERN(5, 50, 110, 1000, 2000, 1, 8, RADAR_WAVEFORM_LONG),
+ FCC_PATTERN(6, 0, 8, 333, 333, 1, 9, RADAR_WAVEFORM_SHORT),
+};
+
+static const struct radar_detector_specs fcc_radar_ref_types_fcu[] = {
+ FCC_PATTERN(0, 0, 8, 1428, 1428, 1, 18, RADAR_WAVEFORM_SHORT),
+ FCC_PATTERN(1, 0, 8, 518, 3066, 1, 102, RADAR_WAVEFORM_WEATHER),
+ FCC_PATTERN(2, 0, 8, 150, 230, 1, 23, RADAR_WAVEFORM_SHORT),
+ FCC_PATTERN(3, 6, 12, 200, 500, 1, 16, RADAR_WAVEFORM_SHORT),
+ FCC_PATTERN(4, 10, 22, 200, 500, 1, 12, RADAR_WAVEFORM_SHORT),
+ FCC_PATTERN(5, 50, 104, 1000, 2000, 1, 8, RADAR_WAVEFORM_LONG),
+ FCC_PATTERN(6, 0, 8, 333, 333, 1, 9, RADAR_WAVEFORM_SHORT),
+};
+
+static const struct radar_types fcc_radar_types = {
+ .region = NL80211_DFS_FCC,
+ .num_radar_types = ARRAY_SIZE(fcc_radar_ref_types_riu),
+ .spec_riu = fcc_radar_ref_types_riu,
+ .spec_fcu = fcc_radar_ref_types_fcu,
+};
+
+#define JP_PATTERN FCC_PATTERN
+static const struct radar_detector_specs jp_radar_ref_types_riu[] = {
+ JP_PATTERN(0, 0, 8, 1428, 1428, 1, 18, RADAR_WAVEFORM_SHORT),
+ JP_PATTERN(1, 2, 8, 3846, 3846, 1, 18, RADAR_WAVEFORM_SHORT),
+ JP_PATTERN(2, 0, 8, 1388, 1388, 1, 18, RADAR_WAVEFORM_SHORT),
+ JP_PATTERN(3, 0, 8, 4000, 4000, 1, 18, RADAR_WAVEFORM_SHORT),
+ JP_PATTERN(4, 0, 8, 150, 230, 1, 23, RADAR_WAVEFORM_SHORT),
+ JP_PATTERN(5, 6, 20, 200, 500, 1, 16, RADAR_WAVEFORM_SHORT),
+ JP_PATTERN(6, 10, 28, 200, 500, 1, 12, RADAR_WAVEFORM_SHORT),
+ JP_PATTERN(7, 50, 110, 1000, 2000, 1, 8, RADAR_WAVEFORM_LONG),
+ JP_PATTERN(8, 0, 8, 333, 333, 1, 9, RADAR_WAVEFORM_SHORT),
+};
+
+static const struct radar_detector_specs jp_radar_ref_types_fcu[] = {
+ JP_PATTERN(0, 0, 8, 1428, 1428, 1, 18, RADAR_WAVEFORM_SHORT),
+ JP_PATTERN(1, 2, 6, 3846, 3846, 1, 18, RADAR_WAVEFORM_SHORT),
+ JP_PATTERN(2, 0, 8, 1388, 1388, 1, 18, RADAR_WAVEFORM_SHORT),
+ JP_PATTERN(3, 2, 2, 4000, 4000, 1, 18, RADAR_WAVEFORM_SHORT),
+ JP_PATTERN(4, 0, 8, 150, 230, 1, 23, RADAR_WAVEFORM_SHORT),
+ JP_PATTERN(5, 6, 12, 200, 500, 1, 16, RADAR_WAVEFORM_SHORT),
+ JP_PATTERN(6, 10, 22, 200, 500, 1, 12, RADAR_WAVEFORM_SHORT),
+ JP_PATTERN(7, 50, 104, 1000, 2000, 1, 8, RADAR_WAVEFORM_LONG),
+ JP_PATTERN(8, 0, 8, 333, 333, 1, 9, RADAR_WAVEFORM_SHORT),
+};
+
+static const struct radar_types jp_radar_types = {
+ .region = NL80211_DFS_JP,
+ .num_radar_types = ARRAY_SIZE(jp_radar_ref_types_riu),
+ .spec_riu = jp_radar_ref_types_riu,
+ .spec_fcu = jp_radar_ref_types_fcu,
+};
+
+static const struct radar_types *dfs_domains[] = {
+ &etsi_radar_types_v17,
+ &fcc_radar_types,
+ &jp_radar_types,
+};
+
+
+/**
+ * struct pri_sequence - sequence of pulses matching one PRI
+ * @head: list_head
+ * @pri: pulse repetition interval (PRI) in usecs
+ * @dur: duration of sequence in usecs
+ * @count: number of pulses in this sequence
+ * @count_falses: number of not matching pulses in this sequence
+ * @first_ts: time stamp of first pulse in usecs
+ * @last_ts: time stamp of last pulse in usecs
+ * @deadline_ts: deadline when this sequence becomes invalid (first_ts + dur)
+ * @ppb_thresh: Number of pulses to validate detection
+ * (need for weather radar whose value depends of pri)
+ */
+struct pri_sequence {
+ struct list_head head;
+ u32 pri;
+ u32 dur;
+ u32 count;
+ u32 count_falses;
+ u64 first_ts;
+ u64 last_ts;
+ u64 deadline_ts;
+ u8 ppb_thresh;
+};
+
+
+/**
+ * struct pulse_elem - elements in pulse queue
+ * @ts: time stamp in usecs
+ */
+struct pulse_elem {
+ struct list_head head;
+ u64 ts;
+};
+
+/**
+ * struct pri_detector - PRI detector element for a dedicated radar type
+ * @head:
+ * @rs: detector specs for this detector element
+ * @last_ts: last pulse time stamp considered for this element in usecs
+ * @sequences: list_head holding potential pulse sequences
+ * @pulses: list connecting pulse_elem objects
+ * @count: number of pulses in queue
+ * @max_count: maximum number of pulses to be queued
+ * @window_size: window size back from newest pulse time stamp in usecs
+ * @freq:
+ */
+struct pri_detector {
+ struct list_head head;
+ const struct radar_detector_specs *rs;
+ u64 last_ts;
+ struct list_head sequences;
+ struct list_head pulses;
+ u32 count;
+ u32 max_count;
+ u32 window_size;
+ struct pri_detector_ops *ops;
+ u16 freq;
+};
+
+/**
+ * struct pri_detector_ops - PRI detector ops (dependent of waveform type)
+ * @init : Initialize pri_detector structure
+ * @add_pulse : Add a pulse to the pri-detector
+ * @reset_on_pri_overflow : Should the pri_detector be resetted when pri overflow
+ */
+struct pri_detector_ops {
+ void (*init)(struct pri_detector *pde);
+ struct pri_sequence * (*add_pulse)(struct pri_detector *pde, u16 len, u64 ts, u16 pri);
+ int reset_on_pri_overflow;
+};
+
+
+/******************************************************************************
+ * PRI (pulse repetition interval) sequence detection
+ *****************************************************************************/
+/**
+ * Singleton Pulse and Sequence Pools
+ *
+ * Instances of pri_sequence and pulse_elem are kept in singleton pools to
+ * reduce the number of dynamic allocations. They are shared between all
+ * instances and grow up to the peak number of simultaneously used objects.
+ *
+ * Memory is freed after all references to the pools are released.
+ */
+static u32 singleton_pool_references;
+static LIST_HEAD(pulse_pool);
+static LIST_HEAD(pseq_pool);
+static DEFINE_SPINLOCK(pool_lock);
+
+static void pool_register_ref(void)
+{
+ spin_lock_bh(&pool_lock);
+ singleton_pool_references++;
+ spin_unlock_bh(&pool_lock);
+}
+
+static void pool_deregister_ref(void)
+{
+ spin_lock_bh(&pool_lock);
+ singleton_pool_references--;
+ if (singleton_pool_references == 0) {
+ /* free singleton pools with no references left */
+ struct pri_sequence *ps, *ps0;
+ struct pulse_elem *p, *p0;
+
+ list_for_each_entry_safe(p, p0, &pulse_pool, head) {
+ list_del(&p->head);
+ kfree(p);
+ }
+ list_for_each_entry_safe(ps, ps0, &pseq_pool, head) {
+ list_del(&ps->head);
+ kfree(ps);
+ }
+ }
+ spin_unlock_bh(&pool_lock);
+}
+
+static void pool_put_pulse_elem(struct pulse_elem *pe)
+{
+ spin_lock_bh(&pool_lock);
+ list_add(&pe->head, &pulse_pool);
+ spin_unlock_bh(&pool_lock);
+}
+
+static void pool_put_pseq_elem(struct pri_sequence *pse)
+{
+ spin_lock_bh(&pool_lock);
+ list_add(&pse->head, &pseq_pool);
+ spin_unlock_bh(&pool_lock);
+}
+
+static struct pri_sequence *pool_get_pseq_elem(void)
+{
+ struct pri_sequence *pse = NULL;
+ spin_lock_bh(&pool_lock);
+ if (!list_empty(&pseq_pool)) {
+ pse = list_first_entry(&pseq_pool, struct pri_sequence, head);
+ list_del(&pse->head);
+ }
+ spin_unlock_bh(&pool_lock);
+
+ if (pse == NULL) {
+ pse = kmalloc(sizeof(*pse), GFP_ATOMIC);
+ }
+
+ return pse;
+}
+
+static struct pulse_elem *pool_get_pulse_elem(void)
+{
+ struct pulse_elem *pe = NULL;
+ spin_lock_bh(&pool_lock);
+ if (!list_empty(&pulse_pool)) {
+ pe = list_first_entry(&pulse_pool, struct pulse_elem, head);
+ list_del(&pe->head);
+ }
+ spin_unlock_bh(&pool_lock);
+ return pe;
+}
+
+static struct pulse_elem *pulse_queue_get_tail(struct pri_detector *pde)
+{
+ struct list_head *l = &pde->pulses;
+ if (list_empty(l))
+ return NULL;
+ return list_entry(l->prev, struct pulse_elem, head);
+}
+
+static bool pulse_queue_dequeue(struct pri_detector *pde)
+{
+ struct pulse_elem *p = pulse_queue_get_tail(pde);
+ if (p != NULL) {
+ list_del_init(&p->head);
+ pde->count--;
+ /* give it back to pool */
+ pool_put_pulse_elem(p);
+ }
+ return (pde->count > 0);
+}
+
+/**
+ * pulse_queue_check_window - remove pulses older than window
+ * @pde: pointer on pri_detector
+ *
+ * dequeue pulse that are too old.
+ */
+static
+void pulse_queue_check_window(struct pri_detector *pde)
+{
+ u64 min_valid_ts;
+ struct pulse_elem *p;
+
+ /* there is no delta time with less than 2 pulses */
+ if (pde->count < 2)
+ return;
+
+ if (pde->last_ts <= pde->window_size)
+ return;
+
+ min_valid_ts = pde->last_ts - pde->window_size;
+ while ((p = pulse_queue_get_tail(pde)) != NULL) {
+ if (p->ts >= min_valid_ts)
+ return;
+ pulse_queue_dequeue(pde);
+ }
+}
+
+/**
+ * pulse_queue_enqueue - Queue one pulse
+ * @pde: pointer on pri_detector
+ *
+ * Add one pulse to the list. If the maximum number of pulses
+ * if reached, remove oldest one.
+ */
+static
+bool pulse_queue_enqueue(struct pri_detector *pde, u64 ts)
+{
+ struct pulse_elem *p = pool_get_pulse_elem();
+ if (p == NULL) {
+ p = kmalloc(sizeof(*p), GFP_ATOMIC);
+ if (p == NULL) {
+ return false;
+ }
+ }
+ INIT_LIST_HEAD(&p->head);
+ p->ts = ts;
+ list_add(&p->head, &pde->pulses);
+ pde->count++;
+ pde->last_ts = ts;
+ pulse_queue_check_window(pde);
+ if (pde->count >= pde->max_count)
+ pulse_queue_dequeue(pde);
+
+ return true;
+}
+
+
+/***************************************************************************
+ * Short waveform
+ **************************************************************************/
+/**
+ * pde_get_multiple() - get number of multiples considering a given tolerance
+ * @return factor if abs(val - factor*fraction) <= tolerance, 0 otherwise
+ */
+static
+u32 pde_get_multiple(u32 val, u32 fraction, u32 tolerance)
+{
+ u32 remainder;
+ u32 factor;
+ u32 delta;
+
+ if (fraction == 0)
+ return 0;
+
+ delta = (val < fraction) ? (fraction - val) : (val - fraction);
+
+ if (delta <= tolerance)
+ /* val and fraction are within tolerance */
+ return 1;
+
+ factor = val / fraction;
+ remainder = val % fraction;
+ if (remainder > tolerance) {
+ /* no exact match */
+ if ((fraction - remainder) <= tolerance)
+ /* remainder is within tolerance */
+ factor++;
+ else
+ factor = 0;
+ }
+ return factor;
+}
+
+/**
+ * pde_short_create_sequences - create_sequences function for
+ * SHORT/WEATHER/INTERLEAVED radar waveform
+ * @pde: pointer on pri_detector
+ * @ts: timestamp of the pulse
+ * @min_count: Minimum number of pulse to be present in the sequence.
+ * (With this pulse there is already a sequence with @min_count
+ * pulse, so if we can't create a sequence with more pulse don't
+ * create it)
+ * @return: false if an error occured (memory allocation) true otherwise
+ *
+ * For each pulses queued check if we can create a sequence with
+ * pri = (ts - pulse_queued.ts) which contains more than @min_count pulses.
+ *
+ */
+static
+bool pde_short_create_sequences(struct pri_detector *pde,
+ u64 ts, u32 min_count)
+{
+ struct pulse_elem *p;
+ u16 pulse_idx = 0;
+
+ list_for_each_entry(p, &pde->pulses, head) {
+ struct pri_sequence ps, *new_ps;
+ struct pulse_elem *p2;
+ u32 tmp_false_count;
+ u64 min_valid_ts;
+ u32 delta_ts = ts - p->ts;
+ pulse_idx++;
+
+ if (delta_ts < pde->rs->pri_min)
+ /* ignore too small pri */
+ continue;
+
+ if (delta_ts > pde->rs->pri_max)
+ /* stop on too large pri (sorted list) */
+ break;
+
+ /* build a new sequence with new potential pri */
+ ps.count = 2;
+ ps.count_falses = pulse_idx - 1;
+ ps.first_ts = p->ts;
+ ps.last_ts = ts;
+ ps.pri = ts - p->ts;
+ ps.dur = ps.pri * (pde->rs->ppb - 1)
+ + 2 * pde->rs->max_pri_tolerance;
+
+ p2 = p;
+ tmp_false_count = 0;
+ if (ps.dur > ts)
+ min_valid_ts = 0;
+ else
+ min_valid_ts = ts - ps.dur;
+ /* check which past pulses are candidates for new sequence */
+ list_for_each_entry_continue(p2, &pde->pulses, head) {
+ u32 factor;
+ if (p2->ts < min_valid_ts)
+ /* stop on crossing window border */
+ break;
+ /* check if pulse match (multi)PRI */
+ factor = pde_get_multiple(ps.last_ts - p2->ts, ps.pri,
+ pde->rs->max_pri_tolerance);
+ if (factor > 0) {
+ ps.count++;
+ ps.first_ts = p2->ts;
+ /*
+ * on match, add the intermediate falses
+ * and reset counter
+ */
+ ps.count_falses += tmp_false_count;
+ tmp_false_count = 0;
+ } else {
+ /* this is a potential false one */
+ tmp_false_count++;
+ }
+ }
+ if (ps.count <= min_count) {
+ /* did not reach minimum count, drop sequence */
+ continue;
+ }
+ /* this is a valid one, add it */
+ ps.deadline_ts = ps.first_ts + ps.dur;
+ if (pde->rs->type == RADAR_WAVEFORM_WEATHER) {
+ ps.ppb_thresh = 19000000 / (360 * ps.pri);
+ ps.ppb_thresh = PPB_THRESH(ps.ppb_thresh);
+ } else {
+ ps.ppb_thresh = pde->rs->ppb_thresh;
+ }
+
+ new_ps = pool_get_pseq_elem();
+ if (new_ps == NULL) {
+ return false;
+ }
+ memcpy(new_ps, &ps, sizeof(ps));
+ INIT_LIST_HEAD(&new_ps->head);
+ list_add(&new_ps->head, &pde->sequences);
+ }
+ return true;
+}
+
+/**
+ * pde_short_add_to_existing_seqs - add_to_existing_seqs function for
+ * SHORT/WEATHER/INTERLEAVED radar waveform
+ * @pde: pointer on pri_detector
+ * @ts: timestamp of the pulse
+ *
+ * Check all sequemces created for this pde.
+ * - If the sequence is too old delete it.
+ * - Else if the delta with the previous pulse match the pri of the sequence
+ * add the pulse to this sequence. If the pulse cannot be added it is added
+ * to the false pulses for this sequence
+ *
+ * @return the length of the longest sequence in which the pulse has been added
+ */
+static
+u32 pde_short_add_to_existing_seqs(struct pri_detector *pde, u64 ts)
+{
+ u32 max_count = 0;
+ struct pri_sequence *ps, *ps2;
+ list_for_each_entry_safe(ps, ps2, &pde->sequences, head) {
+ u32 delta_ts;
+ u32 factor;
+
+ /* first ensure that sequence is within window */
+ if (ts > ps->deadline_ts) {
+ list_del_init(&ps->head);
+ pool_put_pseq_elem(ps);
+ continue;
+ }
+
+ delta_ts = ts - ps->last_ts;
+ factor = pde_get_multiple(delta_ts, ps->pri,
+ pde->rs->max_pri_tolerance);
+
+ if (factor > 0) {
+ ps->last_ts = ts;
+ ps->count++;
+
+ if (max_count < ps->count)
+ max_count = ps->count;
+ } else {
+ ps->count_falses++;
+ }
+ }
+ return max_count;
+}
+
+
+/**
+ * pde_short_check_detection - check_detection function for
+ * SHORT/WEATHER/INTERLEAVED radar waveform
+ * @pde: pointer on pri_detector
+ *
+ * Check all sequemces created for this pde.
+ * - If a sequence contains more pulses than the threshold and more matching
+ * that false pulses.
+ *
+ * @return The first complete sequence, and NULL if no sequence is complete.
+ */
+static
+struct pri_sequence * pde_short_check_detection(struct pri_detector *pde)
+{
+ struct pri_sequence *ps;
+
+ if (list_empty(&pde->sequences))
+ return NULL;
+
+ list_for_each_entry(ps, &pde->sequences, head) {
+ /*
+ * we assume to have enough matching confidence if we
+ * 1) have enough pulses
+ * 2) have more matching than false pulses
+ */
+ if ((ps->count >= ps->ppb_thresh) &&
+ (ps->count * pde->rs->num_pri > ps->count_falses)) {
+ return ps;
+ }
+ }
+ return NULL;
+}
+
+/**
+ * pde_short_init - init function for
+ * SHORT/WEATHER/INTERLEAVED radar waveform
+ * @pde: pointer on pri_detector
+ *
+ * Initialize pri_detector window size to the maximun size of one burst
+ * for the radar specification associated.
+ */
+static
+void pde_short_init(struct pri_detector *pde)
+{
+ pde->window_size = pde->rs->pri_max * pde->rs->ppb * pde->rs->num_pri;
+ pde->max_count = pde->rs->ppb * 2;
+}
+
+static void pri_detector_reset(struct pri_detector *pde, u64 ts);
+/**
+ * pde_short_add_pulse - Add pulse to a pri_detector for
+ * SHORT/WEATHER/INTERLEAVED radar waveform
+ *
+ * @pde : pointer on pri_detector
+ * @len : width of the pulse
+ * @ts : timestamp of the pulse received
+ * @pri : Delta in us with the previous pulse.
+ * (0 means that delta in bigger than 65535 us)
+ *
+ * Process on pulse within this pri_detector
+ * - First try to add it to existing sequence
+ * - Then try to create a new and longest sequence
+ * - Check if this pulse complete a sequence
+ * - If not save this pulse in the list
+ */
+static
+struct pri_sequence *pde_short_add_pulse(struct pri_detector *pde,
+ u16 len, u64 ts, u16 pri)
+{
+ u32 max_updated_seq;
+ struct pri_sequence *ps;
+ const struct radar_detector_specs *rs = pde->rs;
+
+ if (pde->count == 0) {
+ /* This is the first pulse after reset, no need to check sequences */
+ pulse_queue_enqueue(pde, ts);
+ return NULL;
+ }
+
+ if ((ts - pde->last_ts) < rs->max_pri_tolerance) {
+ /* if delta to last pulse is too short, don't use this pulse */
+ return NULL;
+ }
+
+ max_updated_seq = pde_short_add_to_existing_seqs(pde, ts);
+
+ if (!pde_short_create_sequences(pde, ts, max_updated_seq)) {
+ pri_detector_reset(pde, ts);
+ return NULL;
+ }
+
+ ps = pde_short_check_detection(pde);
+
+ if (ps == NULL)
+ pulse_queue_enqueue(pde, ts);
+
+ return ps;
+}
+
+
+
+/**
+ * pri detector ops to detect short radar waveform
+ * A Short waveform is defined by :
+ * The width of pulses.
+ * The interval between two pulses inside a burst (called pri)
+ * (some waveform may have or 2/3 interleaved pri)
+ * The number of pulses per burst (ppb)
+ */
+static struct pri_detector_ops pri_detector_short = {
+ .init = pde_short_init,
+ .add_pulse = pde_short_add_pulse,
+ .reset_on_pri_overflow = 1,
+};
+
+
+/***************************************************************************
+ * Long waveform
+ **************************************************************************/
+#define LONG_RADAR_DURATION 12000000
+#define LONG_RADAR_BURST_MIN_DURATION (12000000 / 20)
+#define LONG_RADAR_MAX_BURST 20
+
+/**
+ * pde_long_init - init function for LONG radar waveform
+ * @pde: pointer on pri_detector
+ *
+ * Initialize pri_detector window size to the long waveform radar
+ * waveform (ie. 12s) and max_count
+ */
+static
+void pde_long_init(struct pri_detector *pde)
+{
+ pde->window_size = LONG_RADAR_DURATION;
+ pde->max_count = LONG_RADAR_MAX_BURST; /* only count burst not pulses */
+}
+
+
+/**
+ * pde_long_add_pulse - Add pulse to a pri_detector for
+ * LONG radar waveform
+ *
+ * @pde : pointer on pri_detector
+ * @len : width of the pulse
+ * @ts : timestamp of the pulse received
+ * @pri : Delta in us with the previous pulse.
+ *
+ *
+ * For long pulse we only handle one sequence. Since each burst
+ * have a different set of parameters (number of pulse, pri) than
+ * the previous one we only use pulse width to add the pulse in the
+ * sequence.
+ * We only queue one pulse per burst and valid the radar when enough burst
+ * has been detected.
+ */
+static
+struct pri_sequence *pde_long_add_pulse(struct pri_detector *pde,
+ u16 len, u64 ts, u16 pri)
+{
+ struct pri_sequence *ps;
+ const struct radar_detector_specs *rs = pde->rs;
+
+ if (list_empty(&pde->sequences)) {
+ /* First pulse, create a new sequence */
+ ps = pool_get_pseq_elem();
+ if (ps == NULL) {
+ return NULL;
+ }
+
+ /*For long waveform, "count" represents the number of burst detected */
+ ps->count = 1;
+ /*"count_false" represents the number of pulse in the current burst */
+ ps->count_falses = 1;
+ ps->first_ts = ts;
+ ps->last_ts = ts;
+ ps->deadline_ts = ts + pde->window_size;
+ ps->pri = 0;
+ INIT_LIST_HEAD(&ps->head);
+ list_add(&ps->head, &pde->sequences);
+ pulse_queue_enqueue(pde, ts);
+ } else {
+ u32 delta_ts;
+
+ ps = (struct pri_sequence *)pde->sequences.next;
+
+ delta_ts = ts - ps->last_ts;
+ ps->last_ts = ts;
+
+ if (delta_ts < rs->pri_max) {
+ /* ignore pulse too close from previous one */
+ } else if ((delta_ts >= rs->pri_min) &&
+ (delta_ts <= rs->pri_max)) {
+ /* this is a new pulse in the current burst, ignore it
+ (i.e don't queue it) */
+ ps->count_falses++;
+ } else if ((ps->count > 2) &&
+ (ps->dur + delta_ts) < LONG_RADAR_BURST_MIN_DURATION) {
+ /* not enough time between burst, ignore pulse */
+ } else {
+ /* a new burst */
+ ps->count++;
+ ps->count_falses = 1;
+
+ /* reset the start of the sequence if deadline reached */
+ if (ts > ps->deadline_ts) {
+ struct pulse_elem *p;
+ u64 min_valid_ts;
+
+ min_valid_ts = ts - pde->window_size;
+ while ((p = pulse_queue_get_tail(pde)) != NULL) {
+ if (p->ts >= min_valid_ts) {
+ ps->first_ts = p->ts;
+ ps->deadline_ts = p->ts + pde->window_size;
+ break;
+ }
+ pulse_queue_dequeue(pde);
+ ps->count--;
+ }
+ }
+
+ /* valid radar if enough burst detected and delta with first burst
+ is at least duration/2 */
+ if (ps->count > pde->rs->ppb_thresh &&
+ (ts - ps->first_ts) > (pde->window_size / 2)) {
+ return ps;
+ } else {
+ pulse_queue_enqueue(pde, ts);
+ ps->dur = delta_ts;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * pri detector ops to detect long radar waveform
+ */
+static struct pri_detector_ops pri_detector_long = {
+ .init = pde_long_init,
+ .add_pulse = pde_long_add_pulse,
+ .reset_on_pri_overflow = 0,
+};
+
+
+/***************************************************************************
+ * PRI detector init/reset/exit/get
+ **************************************************************************/
+/**
+ * pri_detector_init- Create a new pri_detector
+ *
+ * @dpd: dfs_pattern_detector instance pointer
+ * @radar_type: index of radar pattern
+ * @freq: Frequency of the pri detector
+ */
+struct pri_detector *pri_detector_init(struct dfs_pattern_detector *dpd,
+ u16 radar_type, u16 freq)
+{
+ struct pri_detector *pde;
+
+ pde = kzalloc(sizeof(*pde), GFP_ATOMIC);
+ if (pde == NULL)
+ return NULL;
+
+ INIT_LIST_HEAD(&pde->sequences);
+ INIT_LIST_HEAD(&pde->pulses);
+ INIT_LIST_HEAD(&pde->head);
+ list_add(&pde->head, &dpd->detectors[radar_type]);
+
+ pde->rs = &dpd->radar_spec[radar_type];
+ pde->freq = freq;
+
+ if (pde->rs->type == RADAR_WAVEFORM_LONG) {
+ /* for LONG WAVEFORM */
+ pde->ops = &pri_detector_long;
+ } else {
+ /* for SHORT, WEATHER and INTERLEAVED */
+ pde->ops = &pri_detector_short;
+ }
+
+ /* Init dependent of specs */
+ pde->ops->init(pde);
+
+ pool_register_ref();
+ return pde;
+}
+
+/**
+ * pri_detector_reset - Reset pri_detector
+ *
+ * @pde: pointer on pri_detector
+ * @ts: New ts reference for the pri_detector
+ *
+ * free pulse queue and sequences list and give objects back to pools
+ */
+static
+void pri_detector_reset(struct pri_detector *pde, u64 ts)
+{
+ struct pri_sequence *ps, *ps0;
+ struct pulse_elem *p, *p0;
+ list_for_each_entry_safe(ps, ps0, &pde->sequences, head) {
+ list_del_init(&ps->head);
+ pool_put_pseq_elem(ps);
+ }
+ list_for_each_entry_safe(p, p0, &pde->pulses, head) {
+ list_del_init(&p->head);
+ pool_put_pulse_elem(p);
+ }
+ pde->count = 0;
+ pde->last_ts = ts;
+}
+
+/**
+ * pri_detector_exit - Delete pri_detector
+ *
+ * @pde: pointer on pri_detector
+ */
+static
+void pri_detector_exit(struct pri_detector *pde)
+{
+ pri_detector_reset(pde, 0);
+ pool_deregister_ref();
+ list_del(&pde->head);
+ kfree(pde);
+}
+
+/**
+ * pri_detector_get() - get pri detector for a given frequency and type
+ * @dpd: dfs_pattern_detector instance pointer
+ * @freq: frequency in MHz
+ * @radar_type: index of radar pattern
+ * @return pointer to pri detector on success, NULL otherwise
+ *
+ * Return existing pri detector for the given frequency or return a
+ * newly create one.
+ * Pri detector are "merged" by frequency so that if a pri detector for a freq
+ * of +/- 2Mhz already exists don't create a new one.
+ *
+ * Maybe will need to adapt frequency merge for pattern with chirp.
+ */
+static struct pri_detector *
+pri_detector_get(struct dfs_pattern_detector *dpd, u16 freq, u16 radar_type)
+{
+ struct pri_detector *pde, *cur = NULL;
+ list_for_each_entry(pde, &dpd->detectors[radar_type], head) {
+ if (pde->freq == freq) {
+ if (pde->count)
+ return pde;
+ else
+ cur = pde;
+ } else if (pde->freq - 2 == freq && pde->count) {
+ return pde;
+ } else if (pde->freq + 2 == freq && pde->count) {
+ return pde;
+ }
+ }
+
+ if (cur)
+ return cur;
+ else
+ return pri_detector_init(dpd, radar_type, freq);
+}
+
+
+/******************************************************************************
+ * DFS Pattern Detector
+ *****************************************************************************/
+/**
+ * dfs_pattern_detector_reset() - reset all channel detectors
+ *
+ * @dpd: dfs_pattern_detector
+ */
+static void dfs_pattern_detector_reset(struct dfs_pattern_detector *dpd)
+{
+ struct pri_detector *pde;
+ int i;
+
+ for (i = 0; i < dpd->num_radar_types; i++) {
+ if (!list_empty(&dpd->detectors[i]))
+ list_for_each_entry(pde, &dpd->detectors[i], head)
+ pri_detector_reset(pde, dpd->last_pulse_ts);
+ }
+
+ dpd->last_pulse_ts = 0;
+ dpd->prev_jiffies = jiffies;
+}
+
+/**
+ * dfs_pattern_detector_reset() - delete all channel detectors
+ *
+ * @dpd: dfs_pattern_detector
+ */
+static void dfs_pattern_detector_exit(struct dfs_pattern_detector *dpd)
+{
+ struct pri_detector *pde, *pde0;
+ int i;
+
+ for (i = 0; i < dpd->num_radar_types; i++) {
+ if (!list_empty(&dpd->detectors[i]))
+ list_for_each_entry_safe(pde, pde0, &dpd->detectors[i], head)
+ pri_detector_exit(pde);
+ }
+
+ kfree(dpd);
+}
+
+/**
+ * dfs_pattern_detector_pri_overflow - reset all channel detectors on pri
+ * overflow
+ * @dpd: dfs_pattern_detector
+ */
+static void dfs_pattern_detector_pri_overflow(struct dfs_pattern_detector *dpd)
+{
+ struct pri_detector *pde;
+ int i;
+
+ for (i = 0; i < dpd->num_radar_types; i++) {
+ if (!list_empty(&dpd->detectors[i]))
+ list_for_each_entry(pde, &dpd->detectors[i], head)
+ if (pde->ops->reset_on_pri_overflow)
+ pri_detector_reset(pde, dpd->last_pulse_ts);
+ }
+}
+
+/**
+ * dfs_pattern_detector_add_pulse - Process one pulse
+ *
+ * @dpd: dfs_pattern_detector
+ * @chain: Chain that correspond to this pattern_detector (only for debug)
+ * @freq: frequency of the pulse
+ * @pri: Delta with previous pulse. (0 if delta is too big for u16)
+ * @len: width of the pulse
+ * @now: jiffies value when pulse was received
+ *
+ * Get (or create) the channel_detector for this frequency. Then add the pulse
+ * in each pri_detector created in this channel_detector.
+ *
+ *
+ * @return True is the pulse complete a radar pattern, false otherwise
+ */
+static bool dfs_pattern_detector_add_pulse(struct dfs_pattern_detector *dpd,
+ enum rwnx_radar_chain chain,
+ u16 freq, u16 pri, u16 len, u32 now)
+{
+ u32 i;
+
+ /*
+ * pulses received for a non-supported or un-initialized
+ * domain are treated as detected radars for fail-safety
+ */
+ if (dpd->region == NL80211_DFS_UNSET)
+ return true;
+
+ /* Compute pulse time stamp */
+ if (pri == 0) {
+ u32 delta_jiffie;
+ if (unlikely(now < dpd->prev_jiffies)) {
+ delta_jiffie = 0xffffffff - dpd->prev_jiffies + now;
+ } else {
+ delta_jiffie = now - dpd->prev_jiffies;
+ }
+ dpd->last_pulse_ts += jiffies_to_usecs(delta_jiffie);
+ dpd->prev_jiffies = now;
+ dfs_pattern_detector_pri_overflow(dpd);
+ } else {
+ dpd->last_pulse_ts += pri;
+ }
+
+ for (i = 0; i < dpd->num_radar_types; i++) {
+ struct pri_sequence *ps;
+ struct pri_detector *pde;
+ const struct radar_detector_specs *rs = &dpd->radar_spec[i];
+
+ /* no need to look up for pde if len is not within range */
+ if ((rs->width_min > len) ||
+ (rs->width_max < len)) {
+ continue;
+ }
+
+ pde = pri_detector_get(dpd, freq, i);
+ ps = pde->ops->add_pulse(pde, len, dpd->last_pulse_ts, pri);
+
+ if (ps != NULL) {
+#ifdef CREATE_TRACE_POINTS
+ trace_radar_detected(chain, dpd->region, pde->freq, i, ps->pri);
+#endif
+ // reset everything instead of just the channel detector
+ dfs_pattern_detector_reset(dpd);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/**
+ * get_dfs_domain_radar_types() - get radar types for a given DFS domain
+ * @param domain DFS domain
+ * @return radar_types ptr on success, NULL if DFS domain is not supported
+ */
+static const struct radar_types *
+get_dfs_domain_radar_types(enum nl80211_dfs_regions region)
+{
+ u32 i;
+ for (i = 0; i < ARRAY_SIZE(dfs_domains); i++) {
+ if (dfs_domains[i]->region == region)
+ return dfs_domains[i];
+ }
+ return NULL;
+}
+
+/**
+ * get_dfs_max_radar_types() - get maximum radar types for all supported domain
+ * @return the maximum number of radar pattern supported by on region
+ */
+static u16 get_dfs_max_radar_types(void)
+{
+ u32 i;
+ u16 max = 0;
+ for (i = 0; i < ARRAY_SIZE(dfs_domains); i++) {
+ if (dfs_domains[i]->num_radar_types > max)
+ max = dfs_domains[i]->num_radar_types;
+ }
+ return max;
+}
+
+/**
+ * dfs_pattern_detector_set_domain - set DFS domain
+ *
+ * @dpd: dfs_pattern_detector
+ * @region: DFS region
+ *
+ * set DFS domain, resets detector lines upon domain changes
+ */
+static
+bool dfs_pattern_detector_set_domain(struct dfs_pattern_detector *dpd,
+ enum nl80211_dfs_regions region, u8 chain)
+{
+ const struct radar_types *rt;
+ struct pri_detector *pde, *pde0;
+ int i;
+
+ if (dpd->region == region)
+ return true;
+
+ dpd->region = NL80211_DFS_UNSET;
+
+ rt = get_dfs_domain_radar_types(region);
+ if (rt == NULL)
+ return false;
+
+ /* delete all pri detectors for previous DFS domain */
+ for (i = 0; i < dpd->num_radar_types; i++) {
+ if (!list_empty(&dpd->detectors[i]))
+ list_for_each_entry_safe(pde, pde0, &dpd->detectors[i], head)
+ pri_detector_exit(pde);
+ }
+
+ if (chain == RWNX_RADAR_RIU)
+ dpd->radar_spec = rt->spec_riu;
+ else
+ dpd->radar_spec = rt->spec_fcu;
+ dpd->num_radar_types = rt->num_radar_types;
+
+ dpd->region = region;
+ return true;
+}
+
+/**
+ * dfs_pattern_detector_init - Initialize dfs_pattern_detector
+ *
+ * @region: DFS region
+ * @return: pointer on dfs_pattern_detector
+ *
+ */
+static struct dfs_pattern_detector *
+dfs_pattern_detector_init(enum nl80211_dfs_regions region, u8 chain)
+{
+ struct dfs_pattern_detector *dpd;
+ u16 i, max_radar_type = get_dfs_max_radar_types();
+
+ dpd = kmalloc(sizeof(*dpd) + max_radar_type * sizeof(dpd->detectors[0]),
+ GFP_KERNEL);
+ if (dpd == NULL)
+ return NULL;
+
+ dpd->region = NL80211_DFS_UNSET;
+ dpd->enabled = RWNX_RADAR_DETECT_DISABLE;
+ dpd->last_pulse_ts = 0;
+ dpd->prev_jiffies = jiffies;
+ dpd->num_radar_types = 0;
+ for (i = 0; i < max_radar_type; i++)
+ INIT_LIST_HEAD(&dpd->detectors[i]);
+
+ if (dfs_pattern_detector_set_domain(dpd, region, chain))
+ return dpd;
+
+ kfree(dpd);
+ return NULL;
+}
+
+
+/******************************************************************************
+ * driver interface
+ *****************************************************************************/
+static u16 rwnx_radar_get_center_freq(struct rwnx_hw *rwnx_hw, u8 chain)
+{
+ if (chain == RWNX_RADAR_FCU)
+ return rwnx_hw->phy.sec_chan.center_freq1;
+
+ if (chain == RWNX_RADAR_RIU) {
+#ifdef CONFIG_RWNX_FULLMAC
+ if (!rwnx_chanctx_valid(rwnx_hw, rwnx_hw->cur_chanctx)) {
+ WARN(1, "Radar pulse without channel information");
+ } else
+ return rwnx_hw->chanctx_table[rwnx_hw->cur_chanctx].chan_def.center_freq1;
+#endif /* CONFIG_RWNX_FULLMAC */
+ }
+
+ return 0;
+}
+
+static void rwnx_radar_detected(struct rwnx_hw *rwnx_hw)
+{
+#ifdef CONFIG_RWNX_FULLMAC
+ struct cfg80211_chan_def chan_def;
+
+ if (!rwnx_chanctx_valid(rwnx_hw, rwnx_hw->cur_chanctx)) {
+ WARN(1, "Radar detected without channel information");
+ return;
+ }
+
+ /*
+ recopy chan_def in local variable because rwnx_radar_cancel_cac may
+ clean the variable (if in CAC and it's the only vif using this context)
+ and CAC should be aborted before reporting the radar.
+ */
+ chan_def = rwnx_hw->chanctx_table[rwnx_hw->cur_chanctx].chan_def;
+
+ rwnx_radar_cancel_cac(&rwnx_hw->radar);
+ cfg80211_radar_event(rwnx_hw->wiphy, &chan_def, GFP_KERNEL);
+
+#endif /* CONFIG_RWNX_FULLMAC */
+}
+
+static void rwnx_radar_process_pulse(struct work_struct *ws)
+{
+ struct rwnx_radar *radar = container_of(ws, struct rwnx_radar,
+ detection_work);
+ struct rwnx_hw *rwnx_hw = container_of(radar, struct rwnx_hw, radar);
+ int chain;
+ u32 pulses[RWNX_RADAR_LAST][RWNX_RADAR_PULSE_MAX];
+ u16 pulses_count[RWNX_RADAR_LAST];
+ u32 now = jiffies; /* would be better to store jiffies value in IT handler */
+
+ /* recopy pulses locally to avoid too long spin_lock */
+ spin_lock_bh(&radar->lock);
+ for (chain = RWNX_RADAR_RIU; chain < RWNX_RADAR_LAST; chain++) {
+ int start, count;
+
+ count = radar->pulses[chain].count;
+ start = radar->pulses[chain].index - count;
+ if (start < 0)
+ start += RWNX_RADAR_PULSE_MAX;
+
+ pulses_count[chain] = count;
+ if (count == 0)
+ continue;
+
+ if ((start + count) > RWNX_RADAR_PULSE_MAX) {
+ u16 count1 = (RWNX_RADAR_PULSE_MAX - start);
+ memcpy(&(pulses[chain][0]),
+ &(radar->pulses[chain].buffer[start]),
+ count1 * sizeof(struct radar_pulse));
+ memcpy(&(pulses[chain][count1]),
+ &(radar->pulses[chain].buffer[0]),
+ (count - count1) * sizeof(struct radar_pulse));
+ } else {
+ memcpy(&(pulses[chain][0]),
+ &(radar->pulses[chain].buffer[start]),
+ count * sizeof(struct radar_pulse));
+ }
+ radar->pulses[chain].count = 0;
+ }
+ spin_unlock_bh(&radar->lock);
+
+
+ /* now process pulses */
+ for (chain = RWNX_RADAR_RIU; chain < RWNX_RADAR_LAST; chain++) {
+ int i;
+ u16 freq;
+
+ if (pulses_count[chain] == 0)
+ continue;
+
+ freq = rwnx_radar_get_center_freq(rwnx_hw, chain);
+
+ for (i = 0; i < pulses_count[chain] ; i++) {
+ struct radar_pulse *p = (struct radar_pulse *)&pulses[chain][i];
+#ifdef CREATE_TRACE_POINTS
+ trace_radar_pulse(chain, p);
+#endif
+ if (dfs_pattern_detector_add_pulse(radar->dpd[chain], chain,
+ (s16)freq + (2 * p->freq),
+ p->rep, (p->len * 2), now)) {
+ u16 idx = radar->detected[chain].index;
+
+ if (chain == RWNX_RADAR_RIU) {
+ /* operating chain, inform upper layer to change channel */
+ if (radar->dpd[chain]->enabled == RWNX_RADAR_DETECT_REPORT) {
+ rwnx_radar_detected(rwnx_hw);
+ /* no need to report new radar until upper layer set a
+ new channel. This prevent warning if a new radar is
+ detected while mac80211 is changing channel */
+ rwnx_radar_detection_enable(radar,
+ RWNX_RADAR_DETECT_DISABLE,
+ chain);
+ /* purge any event received since the beginning of the
+ function (we are sure not to interfer with tasklet
+ as we disable detection just before) */
+ radar->pulses[chain].count = 0;
+ }
+ } else {
+ /* secondary radar detection chain, simply report info in
+ debugfs for now */
+ }
+
+ radar->detected[chain].freq[idx] = (s16)freq + (2 * p->freq);
+ radar->detected[chain].time[idx] = ktime_get_real_seconds();
+ radar->detected[chain].index = ((idx + 1 ) %
+ NX_NB_RADAR_DETECTED);
+ radar->detected[chain].count++;
+ /* no need to process next pulses for this chain */
+ break;
+ }
+ }
+ }
+}
+
+#ifdef CONFIG_RWNX_FULLMAC
+static void rwnx_radar_cac_work(struct work_struct *ws)
+{
+ struct delayed_work *dw = container_of(ws, struct delayed_work, work);
+ struct rwnx_radar *radar = container_of(dw, struct rwnx_radar, cac_work);
+ struct rwnx_hw *rwnx_hw = container_of(radar, struct rwnx_hw, radar);
+ struct rwnx_chanctx *ctxt;
+
+ if (radar->cac_vif == NULL) {
+ WARN(1, "CAC finished but no vif set");
+ return;
+ }
+
+ ctxt = &rwnx_hw->chanctx_table[radar->cac_vif->ch_index];
+ cfg80211_cac_event(radar->cac_vif->ndev,
+ #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
+ &ctxt->chan_def,
+ #endif
+ NL80211_RADAR_CAC_FINISHED, GFP_KERNEL);
+ rwnx_send_apm_stop_cac_req(rwnx_hw, radar->cac_vif);
+ rwnx_chanctx_unlink(radar->cac_vif);
+
+ radar->cac_vif = NULL;
+}
+#endif /* CONFIG_RWNX_FULLMAC */
+
+bool rwnx_radar_detection_init(struct rwnx_radar *radar)
+{
+ spin_lock_init(&radar->lock);
+
+ radar->dpd[RWNX_RADAR_RIU] = dfs_pattern_detector_init(NL80211_DFS_UNSET,
+ RWNX_RADAR_RIU);
+ if (radar->dpd[RWNX_RADAR_RIU] == NULL)
+ return false;
+
+ radar->dpd[RWNX_RADAR_FCU] = dfs_pattern_detector_init(NL80211_DFS_UNSET,
+ RWNX_RADAR_FCU);
+ if (radar->dpd[RWNX_RADAR_FCU] == NULL) {
+ rwnx_radar_detection_deinit(radar);
+ return false;
+ }
+
+ INIT_WORK(&radar->detection_work, rwnx_radar_process_pulse);
+#ifdef CONFIG_RWNX_FULLMAC
+ INIT_DELAYED_WORK(&radar->cac_work, rwnx_radar_cac_work);
+ radar->cac_vif = NULL;
+#endif /* CONFIG_RWNX_FULLMAC */
+ return true;
+}
+
+void rwnx_radar_detection_deinit(struct rwnx_radar *radar)
+{
+ if (radar->dpd[RWNX_RADAR_RIU]) {
+ dfs_pattern_detector_exit(radar->dpd[RWNX_RADAR_RIU]);
+ radar->dpd[RWNX_RADAR_RIU] = NULL;
+ }
+ if (radar->dpd[RWNX_RADAR_FCU]) {
+ dfs_pattern_detector_exit(radar->dpd[RWNX_RADAR_FCU]);
+ radar->dpd[RWNX_RADAR_FCU] = NULL;
+ }
+}
+
+bool rwnx_radar_set_domain(struct rwnx_radar *radar,
+ enum nl80211_dfs_regions region)
+{
+ if (radar->dpd[0] == NULL)
+ return false;
+#ifdef CREATE_TRACE_POINTS
+ trace_radar_set_region(region);
+#endif
+ return (dfs_pattern_detector_set_domain(radar->dpd[RWNX_RADAR_RIU],
+ region, RWNX_RADAR_RIU) &&
+ dfs_pattern_detector_set_domain(radar->dpd[RWNX_RADAR_FCU],
+ region, RWNX_RADAR_FCU));
+}
+
+void rwnx_radar_detection_enable(struct rwnx_radar *radar, u8 enable, u8 chain)
+{
+ if (chain < RWNX_RADAR_LAST ) {
+#ifdef CREATE_TRACE_POINTS
+ trace_radar_enable_detection(radar->dpd[chain]->region, enable, chain);
+#endif
+ spin_lock_bh(&radar->lock);
+ radar->dpd[chain]->enabled = enable;
+ spin_unlock_bh(&radar->lock);
+ }
+}
+
+bool rwnx_radar_detection_is_enable(struct rwnx_radar *radar, u8 chain)
+{
+ return radar->dpd[chain]->enabled != RWNX_RADAR_DETECT_DISABLE;
+}
+
+#ifdef CONFIG_RWNX_FULLMAC
+void rwnx_radar_start_cac(struct rwnx_radar *radar, u32 cac_time_ms,
+ struct rwnx_vif *vif)
+{
+ WARN(radar->cac_vif != NULL, "CAC already in progress");
+ radar->cac_vif = vif;
+ schedule_delayed_work(&radar->cac_work, msecs_to_jiffies(cac_time_ms));
+}
+
+void rwnx_radar_cancel_cac(struct rwnx_radar *radar)
+{
+ struct rwnx_hw *rwnx_hw = container_of(radar, struct rwnx_hw, radar);
+
+ if (radar->cac_vif == NULL) {
+ return;
+ }
+
+ if (cancel_delayed_work(&radar->cac_work)) {
+ struct rwnx_chanctx *ctxt;
+ ctxt = &rwnx_hw->chanctx_table[radar->cac_vif->ch_index];
+ rwnx_send_apm_stop_cac_req(rwnx_hw, radar->cac_vif);
+ cfg80211_cac_event(radar->cac_vif->ndev,
+ #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
+ &ctxt->chan_def,
+ #endif
+ NL80211_RADAR_CAC_ABORTED, GFP_KERNEL);
+ rwnx_chanctx_unlink(radar->cac_vif);
+ }
+
+ radar->cac_vif = NULL;
+}
+
+void rwnx_radar_detection_enable_on_cur_channel(struct rwnx_hw *rwnx_hw)
+{
+ struct rwnx_chanctx *ctxt;
+
+ /* If no information on current channel do nothing */
+ if (!rwnx_chanctx_valid(rwnx_hw, rwnx_hw->cur_chanctx))
+ return;
+
+ ctxt = &rwnx_hw->chanctx_table[rwnx_hw->cur_chanctx];
+ if (ctxt->chan_def.chan->flags & IEEE80211_CHAN_RADAR) {
+ rwnx_radar_detection_enable(&rwnx_hw->radar,
+ RWNX_RADAR_DETECT_REPORT,
+ RWNX_RADAR_RIU);
+ } else {
+ rwnx_radar_detection_enable(&rwnx_hw->radar,
+ RWNX_RADAR_DETECT_DISABLE,
+ RWNX_RADAR_RIU);
+ }
+}
+#endif /* CONFIG_RWNX_FULLMAC */
+
+/*****************************************************************************
+ * Debug functions
+ *****************************************************************************/
+static
+int rwnx_radar_dump_pri_detector(char *buf, size_t len,
+ struct pri_detector *pde)
+{
+ char freq_info[] = "Freq = %3.dMhz\n";
+ char seq_info[] = " pri | count | false \n";
+ struct pri_sequence *seq;
+ int res, write = 0;
+
+ if (list_empty(&pde->sequences)) {
+ return 0;
+ }
+
+ if (buf == NULL) {
+ int nb_seq = 1;
+ list_for_each_entry(seq, &pde->sequences, head) {
+ nb_seq++;
+ }
+
+ return (sizeof(freq_info) + nb_seq * sizeof(seq_info));
+ }
+
+ res = scnprintf(buf, len, freq_info, pde->freq);
+ write += res;
+ len -= res;
+
+ res = scnprintf(&buf[write], len, "%s", seq_info);
+ write += res;
+ len -= res;
+
+ list_for_each_entry(seq, &pde->sequences, head) {
+ res = scnprintf(&buf[write], len, " %6.d | %2.d | %.2d \n",
+ seq->pri, seq->count, seq->count_falses);
+ write += res;
+ len -= res;
+ }
+
+ return write;
+}
+
+int rwnx_radar_dump_pattern_detector(char *buf, size_t len,
+ struct rwnx_radar *radar, u8 chain)
+{
+ struct dfs_pattern_detector *dpd = radar->dpd[chain];
+ char info[] = "Type = %3.d\n";
+ struct pri_detector *pde;
+ int i, res, write = 0;
+
+ /* if buf is NULL return size needed for dump */
+ if (buf == NULL) {
+ int size_needed = 0;
+
+ for (i = 0; i < dpd->num_radar_types; i++) {
+ list_for_each_entry(pde, &dpd->detectors[i], head) {
+ size_needed += rwnx_radar_dump_pri_detector(NULL, 0, pde);
+ }
+ size_needed += sizeof(info);
+
+ return size_needed;
+ }
+ }
+
+ /* */
+ for (i = 0; i < dpd->num_radar_types; i++) {
+ res = scnprintf(&buf[write], len, info, i);
+
+ write += res;
+ len -= res;
+ list_for_each_entry(pde, &dpd->detectors[i], head) {
+ res = rwnx_radar_dump_pri_detector(&buf[write], len, pde);
+ write += res;
+ len -= res;
+ }
+ }
+
+ return write;
+}
+
+
+int rwnx_radar_dump_radar_detected(char *buf, size_t len,
+ struct rwnx_radar *radar, u8 chain)
+{
+ struct rwnx_radar_detected *detect = &(radar->detected[chain]);
+ char info[] = "2001/02/02 - 02:20 5126MHz\n";
+ int idx, i, res, write = 0;
+ int count = detect->count;
+
+ if (count > NX_NB_RADAR_DETECTED)
+ count = NX_NB_RADAR_DETECTED;
+
+ if (buf == NULL) {
+ return (count * sizeof(info)) + 1;
+ }
+
+ idx = (detect->index - detect->count) % NX_NB_RADAR_DETECTED;
+
+ for (i = 0; i < count; i++) {
+ struct tm tm;
+ time64_to_tm(detect->time[idx], 0, &tm);
+
+ res = scnprintf(&buf[write], len,
+ "%.4d/%.2d/%.2d - %.2d:%.2d %4.4dMHz\n",
+ (int)tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
+ tm.tm_hour, tm.tm_min, detect->freq[idx]);
+ write += res;
+ len -= res;
+
+ idx = (idx + 1 ) % NX_NB_RADAR_DETECTED;
+ }
+
+ return write;
+}
diff --git a/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_radar.h b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_radar.h
new file mode 100644
index 000000000000..355320fd1d25
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_radar.h
@@ -0,0 +1,160 @@
+/**
+ ******************************************************************************
+ *
+ * @file rwnx_radar.h
+ *
+ * @brief Functions to handle radar detection
+ *
+ *
+ * Copyright (C) RivieraWaves 2012-2019
+ *
+ ******************************************************************************
+ */
+#ifndef _RWNX_RADAR_H_
+#define _RWNX_RADAR_H_
+
+#include <linux/nl80211.h>
+
+struct rwnx_vif;
+struct rwnx_hw;
+
+enum rwnx_radar_chain {
+ RWNX_RADAR_RIU = 0,
+ RWNX_RADAR_FCU,
+ RWNX_RADAR_LAST
+};
+
+enum rwnx_radar_detector {
+ RWNX_RADAR_DETECT_DISABLE = 0, /* Ignore radar pulses */
+ RWNX_RADAR_DETECT_ENABLE = 1, /* Process pattern detection but do not
+ report radar to upper layer (for test) */
+ RWNX_RADAR_DETECT_REPORT = 2 /* Process pattern detection and report
+ radar to upper layer. */
+};
+
+#ifdef CONFIG_RWNX_RADAR
+#include <linux/workqueue.h>
+#include <linux/spinlock.h>
+
+#define RWNX_RADAR_PULSE_MAX 32
+
+/**
+ * struct rwnx_radar_pulses - List of pulses reported by HW
+ * @index: write index
+ * @count: number of valid pulses
+ * @buffer: buffer of pulses
+ */
+struct rwnx_radar_pulses {
+ /* Last radar pulses received */
+ int index;
+ int count;
+ u32 buffer[RWNX_RADAR_PULSE_MAX];
+};
+
+/**
+ * struct dfs_pattern_detector - DFS pattern detector
+ * @region: active DFS region, NL80211_DFS_UNSET until set
+ * @num_radar_types: number of different radar types
+ * @last_pulse_ts: time stamp of last valid pulse in usecs
+ * @prev_jiffies:
+ * @radar_detector_specs: array of radar detection specs
+ * @channel_detectors: list connecting channel_detector elements
+ */
+struct dfs_pattern_detector {
+ u8 enabled;
+ enum nl80211_dfs_regions region;
+ u8 num_radar_types;
+ u64 last_pulse_ts;
+ u32 prev_jiffies;
+ const struct radar_detector_specs *radar_spec;
+ struct list_head detectors[];
+};
+
+#define NX_NB_RADAR_DETECTED 4
+
+/**
+ * struct rwnx_radar_detected - List of radar detected
+ */
+struct rwnx_radar_detected {
+ u16 index;
+ u16 count;
+ s64 time[NX_NB_RADAR_DETECTED];
+ s16 freq[NX_NB_RADAR_DETECTED];
+};
+
+
+struct rwnx_radar {
+ struct rwnx_radar_pulses pulses[RWNX_RADAR_LAST];
+ struct dfs_pattern_detector *dpd[RWNX_RADAR_LAST];
+ struct rwnx_radar_detected detected[RWNX_RADAR_LAST];
+ struct work_struct detection_work; /* Work used to process radar pulses */
+ spinlock_t lock; /* lock for pulses processing */
+
+ /* In softmac cac is handled by mac80211 */
+#ifdef CONFIG_RWNX_FULLMAC
+ struct delayed_work cac_work; /* Work used to handle CAC */
+ struct rwnx_vif *cac_vif; /* vif on which we started CAC */
+#endif
+};
+
+bool rwnx_radar_detection_init(struct rwnx_radar *radar);
+void rwnx_radar_detection_deinit(struct rwnx_radar *radar);
+bool rwnx_radar_set_domain(struct rwnx_radar *radar,
+ enum nl80211_dfs_regions region);
+void rwnx_radar_detection_enable(struct rwnx_radar *radar, u8 enable, u8 chain);
+bool rwnx_radar_detection_is_enable(struct rwnx_radar *radar, u8 chain);
+void rwnx_radar_start_cac(struct rwnx_radar *radar, u32 cac_time_ms,
+ struct rwnx_vif *vif);
+void rwnx_radar_cancel_cac(struct rwnx_radar *radar);
+void rwnx_radar_detection_enable_on_cur_channel(struct rwnx_hw *rwnx_hw);
+int rwnx_radar_dump_pattern_detector(char *buf, size_t len,
+ struct rwnx_radar *radar, u8 chain);
+int rwnx_radar_dump_radar_detected(char *buf, size_t len,
+ struct rwnx_radar *radar, u8 chain);
+
+#else
+
+struct rwnx_radar {
+};
+
+static inline bool rwnx_radar_detection_init(struct rwnx_radar *radar)
+{return true;}
+
+static inline void rwnx_radar_detection_deinit(struct rwnx_radar *radar)
+{}
+
+static inline bool rwnx_radar_set_domain(struct rwnx_radar *radar,
+ enum nl80211_dfs_regions region)
+{return true;}
+
+static inline void rwnx_radar_detection_enable(struct rwnx_radar *radar,
+ u8 enable, u8 chain)
+{}
+
+static inline bool rwnx_radar_detection_is_enable(struct rwnx_radar *radar,
+ u8 chain)
+{return false;}
+
+static inline void rwnx_radar_start_cac(struct rwnx_radar *radar,
+ u32 cac_time_ms, struct rwnx_vif *vif)
+{}
+
+static inline void rwnx_radar_cancel_cac(struct rwnx_radar *radar)
+{}
+
+static inline void rwnx_radar_detection_enable_on_cur_channel(struct rwnx_hw *rwnx_hw)
+{}
+
+static inline int rwnx_radar_dump_pattern_detector(char *buf, size_t len,
+ struct rwnx_radar *radar,
+ u8 chain)
+{return 0;}
+
+static inline int rwnx_radar_dump_radar_detected(char *buf, size_t len,
+ struct rwnx_radar *radar,
+ u8 chain)
+{return 0;}
+
+#endif /* CONFIG_RWNX_RADAR */
+
+#endif // _RWNX_RADAR_H_
diff --git a/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_rx.c b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_rx.c
new file mode 100644
index 000000000000..3dbbc0b9d8c5
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_rx.c
@@ -0,0 +1,2219 @@
+/**
+ ******************************************************************************
+ *
+ * @file rwnx_rx.c
+ *
+ * Copyright (C) RivieraWaves 2012-2019
+ *
+ ******************************************************************************
+ */
+#include <linux/dma-mapping.h>
+#include <linux/ieee80211.h>
+#include <linux/etherdevice.h>
+#include <net/ieee80211_radiotap.h>
+
+#include "rwnx_defs.h"
+#include "rwnx_rx.h"
+#include "rwnx_tx.h"
+#include "rwnx_prof.h"
+#include "ipc_host.h"
+#include "rwnx_events.h"
+#include "rwnx_compat.h"
+#include "aicwf_txrxif.h"
+#ifdef AICWF_ARP_OFFLOAD
+#include <linux/ip.h>
+#include <linux/udp.h>
+#include "rwnx_msg_tx.h"
+#endif
+
+#ifndef IEEE80211_MAX_CHAINS
+#define IEEE80211_MAX_CHAINS 4
+#endif
+
+u8 dhcped = 0;
+
+u16 tx_legrates_lut_rate[] = {
+ 10 ,
+ 20 ,
+ 55 ,
+ 110 ,
+ 60 ,
+ 90 ,
+ 120,
+ 180,
+ 240,
+ 360,
+ 480,
+ 540
+};
+
+
+u16 legrates_lut_rate[] = {
+ 10 ,
+ 20 ,
+ 55 ,
+ 110 ,
+ 0 ,
+ 0 ,
+ 0 ,
+ 0 ,
+ 480 ,
+ 240 ,
+ 120 ,
+ 60 ,
+ 540 ,
+ 360 ,
+ 180 ,
+ 90
+};
+
+
+const u8 legrates_lut[] = {
+ 0, /* 0 */
+ 1, /* 1 */
+ 2, /* 2 */
+ 3, /* 3 */
+ -1, /* 4 */
+ -1, /* 5 */
+ -1, /* 6 */
+ -1, /* 7 */
+ 10, /* 8 */
+ 8, /* 9 */
+ 6, /* 10 */
+ 4, /* 11 */
+ 11, /* 12 */
+ 9, /* 13 */
+ 7, /* 14 */
+ 5 /* 15 */
+};
+
+struct vendor_radiotap_hdr {
+ u8 oui[3];
+ u8 subns;
+ u16 len;
+ u8 data[];
+};
+
+void rwnx_skb_align_8bytes(struct sk_buff *skb);
+
+
+/**
+ * rwnx_rx_get_vif - Return pointer to the destination vif
+ *
+ * @rwnx_hw: main driver data
+ * @vif_idx: vif index present in rx descriptor
+ *
+ * Select the vif that should receive this frame. Returns NULL if the destination
+ * vif is not active or vif is not specified in the descriptor.
+ */
+static inline
+struct rwnx_vif *rwnx_rx_get_vif(struct rwnx_hw *rwnx_hw, int vif_idx)
+{
+ struct rwnx_vif *rwnx_vif = NULL;
+
+ if (vif_idx < NX_VIRT_DEV_MAX) {
+ rwnx_vif = rwnx_hw->vif_table[vif_idx];
+ if (!rwnx_vif || !rwnx_vif->up)
+ return NULL;
+ }
+
+ return rwnx_vif;
+}
+
+/**
+ * rwnx_rx_vector_convert - Convert a legacy RX vector into a new RX vector format
+ *
+ * @rwnx_hw: main driver data.
+ * @rx_vect1: Rx vector 1 descriptor of the received frame.
+ * @rx_vect2: Rx vector 2 descriptor of the received frame.
+ */
+static void rwnx_rx_vector_convert(struct rwnx_hw *rwnx_hw,
+ struct rx_vector_1 *rx_vect1,
+ struct rx_vector_2 *rx_vect2)
+{
+ struct rx_vector_1_old rx_vect1_leg;
+ struct rx_vector_2_old rx_vect2_leg;
+ u32_l phy_vers = rwnx_hw->version_cfm.version_phy_2;
+
+ // Check if we need to do the conversion. Only if old modem is used
+ if (__MDM_MAJOR_VERSION(phy_vers) > 0) {
+ rx_vect1->rssi1 = rx_vect1->rssi_leg;
+ return;
+ }
+
+ // Copy the received vector locally
+ memcpy(&rx_vect1_leg, rx_vect1, sizeof(struct rx_vector_1_old));
+
+ // Reset it
+ memset(rx_vect1, 0, sizeof(struct rx_vector_1));
+
+ // Perform the conversion
+ rx_vect1->format_mod = rx_vect1_leg.format_mod;
+ rx_vect1->ch_bw = rx_vect1_leg.ch_bw;
+ rx_vect1->antenna_set = rx_vect1_leg.antenna_set;
+ rx_vect1->leg_length = rx_vect1_leg.leg_length;
+ rx_vect1->leg_rate = rx_vect1_leg.leg_rate;
+ rx_vect1->rssi1 = rx_vect1_leg.rssi1;
+
+ switch (rx_vect1->format_mod) {
+ case FORMATMOD_NON_HT:
+ case FORMATMOD_NON_HT_DUP_OFDM:
+ rx_vect1->leg.lsig_valid = rx_vect1_leg.lsig_valid;
+ rx_vect1->leg.chn_bw_in_non_ht = rx_vect1_leg.num_extn_ss;
+ rx_vect1->leg.dyn_bw_in_non_ht = rx_vect1_leg.dyn_bw;
+ break;
+ case FORMATMOD_HT_MF:
+ case FORMATMOD_HT_GF:
+ rx_vect1->ht.aggregation = rx_vect1_leg.aggregation;
+ rx_vect1->ht.fec = rx_vect1_leg.fec_coding;
+ rx_vect1->ht.lsig_valid = rx_vect1_leg.lsig_valid;
+ rx_vect1->ht.length = rx_vect1_leg.ht_length;
+ rx_vect1->ht.mcs = rx_vect1_leg.mcs;
+ rx_vect1->ht.num_extn_ss = rx_vect1_leg.num_extn_ss;
+ rx_vect1->ht.short_gi = rx_vect1_leg.short_gi;
+ rx_vect1->ht.smoothing = rx_vect1_leg.smoothing;
+ rx_vect1->ht.sounding = rx_vect1_leg.sounding;
+ rx_vect1->ht.stbc = rx_vect1_leg.stbc;
+ break;
+ case FORMATMOD_VHT:
+ rx_vect1->vht.beamformed = !rx_vect1_leg.smoothing;
+ rx_vect1->vht.fec = rx_vect1_leg.fec_coding;
+ rx_vect1->vht.length = rx_vect1_leg.ht_length | rx_vect1_leg._ht_length << 8;
+ rx_vect1->vht.mcs = rx_vect1_leg.mcs & 0x0F;
+ rx_vect1->vht.nss = rx_vect1_leg.stbc ? rx_vect1_leg.n_sts/2 : rx_vect1_leg.n_sts;
+ rx_vect1->vht.doze_not_allowed = rx_vect1_leg.doze_not_allowed;
+ rx_vect1->vht.short_gi = rx_vect1_leg.short_gi;
+ rx_vect1->vht.sounding = rx_vect1_leg.sounding;
+ rx_vect1->vht.stbc = rx_vect1_leg.stbc;
+ rx_vect1->vht.group_id = rx_vect1_leg.group_id;
+ rx_vect1->vht.partial_aid = rx_vect1_leg.partial_aid;
+ rx_vect1->vht.first_user = rx_vect1_leg.first_user;
+ break;
+ }
+
+ if (!rx_vect2)
+ return;
+
+ // Copy the received vector 2 locally
+ memcpy(&rx_vect2_leg, rx_vect2, sizeof(struct rx_vector_2_old));
+
+ // Reset it
+ memset(rx_vect2, 0, sizeof(struct rx_vector_2));
+
+ rx_vect2->rcpi1 = rx_vect2_leg.rcpi;
+ rx_vect2->rcpi2 = rx_vect2_leg.rcpi;
+ rx_vect2->rcpi3 = rx_vect2_leg.rcpi;
+ rx_vect2->rcpi4 = rx_vect2_leg.rcpi;
+
+ rx_vect2->evm1 = rx_vect2_leg.evm1;
+ rx_vect2->evm2 = rx_vect2_leg.evm2;
+ rx_vect2->evm3 = rx_vect2_leg.evm3;
+ rx_vect2->evm4 = rx_vect2_leg.evm4;
+}
+
+/**
+ * rwnx_rx_statistic - save some statistics about received frames
+ *
+ * @rwnx_hw: main driver data.
+ * @hw_rxhdr: Rx Hardware descriptor of the received frame.
+ * @sta: STA that sent the frame.
+ */
+static void rwnx_rx_statistic(struct rwnx_hw *rwnx_hw, struct hw_rxhdr *hw_rxhdr,
+ struct rwnx_sta *sta)
+{
+#if 1//def CONFIG_RWNX_DEBUGFS
+ struct rwnx_stats *stats = &rwnx_hw->stats;
+ struct rwnx_rx_rate_stats *rate_stats = &sta->stats.rx_rate;
+ struct rx_vector_1 *rxvect = &hw_rxhdr->hwvect.rx_vect1;
+ int mpdu, ampdu, mpdu_prev, rate_idx;
+
+ /* save complete hwvect */
+ sta->stats.last_rx = hw_rxhdr->hwvect;
+
+ /* update ampdu rx stats */
+ mpdu = hw_rxhdr->hwvect.mpdu_cnt;
+ ampdu = hw_rxhdr->hwvect.ampdu_cnt;
+ mpdu_prev = stats->ampdus_rx_map[ampdu];
+
+ if (mpdu_prev < mpdu ) {
+ stats->ampdus_rx_miss += mpdu - mpdu_prev - 1;
+ } else {
+ stats->ampdus_rx[mpdu_prev]++;
+ }
+ stats->ampdus_rx_map[ampdu] = mpdu;
+
+ /* update rx rate statistic */
+ if (!rate_stats->size)
+ return;
+
+ if (rxvect->format_mod > FORMATMOD_NON_HT_DUP_OFDM) {
+ int mcs;
+ int bw = rxvect->ch_bw;
+ int sgi;
+ int nss;
+ switch (rxvect->format_mod) {
+ case FORMATMOD_HT_MF:
+ case FORMATMOD_HT_GF:
+ mcs = rxvect->ht.mcs % 8;
+ nss = rxvect->ht.mcs / 8;
+ sgi = rxvect->ht.short_gi;
+ rate_idx = N_CCK + N_OFDM + nss * 32 + mcs * 4 + bw * 2 + sgi;
+ break;
+ case FORMATMOD_VHT:
+ mcs = rxvect->vht.mcs;
+ nss = rxvect->vht.nss;
+ sgi = rxvect->vht.short_gi;
+ rate_idx = N_CCK + N_OFDM + N_HT + nss * 80 + mcs * 8 + bw * 2 + sgi;
+ break;
+ case FORMATMOD_HE_SU:
+ mcs = rxvect->he.mcs;
+ nss = rxvect->he.nss;
+ sgi = rxvect->he.gi_type;
+ rate_idx = N_CCK + N_OFDM + N_HT + N_VHT + nss * 144 + mcs * 12 + bw * 3 + sgi;
+ break;
+ default:
+ mcs = rxvect->he.mcs;
+ nss = rxvect->he.nss;
+ sgi = rxvect->he.gi_type;
+ rate_idx = N_CCK + N_OFDM + N_HT + N_VHT + N_HE_SU
+ + nss * 216 + mcs * 18 + rxvect->he.ru_size * 3 + sgi;
+ break;
+ }
+ } else {
+ int idx = legrates_lut[rxvect->leg_rate];
+ if (idx < 4) {
+ rate_idx = idx * 2 + rxvect->pre_type;
+ } else {
+ rate_idx = N_CCK + idx - 4;
+ }
+ }
+ if (rate_idx < rate_stats->size) {
+ if (!rate_stats->table[rate_idx])
+ rate_stats->rate_cnt++;
+ rate_stats->table[rate_idx]++;
+ rate_stats->cpt++;
+ } else {
+ wiphy_err(rwnx_hw->wiphy, "RX: Invalid index conversion => %d/%d\n",
+ rate_idx, rate_stats->size);
+ }
+#endif
+}
+
+/**
+ * rwnx_rx_data_skb - Process one data frame
+ *
+ * @rwnx_hw: main driver data
+ * @rwnx_vif: vif that received the buffer
+ * @skb: skb received
+ * @rxhdr: HW rx descriptor
+ * @return: true if buffer has been forwarded to upper layer
+ *
+ * If buffer is amsdu , it is first split into a list of skb.
+ * Then each skb may be:
+ * - forwarded to upper layer
+ * - resent on wireless interface
+ *
+ * When vif is a STA interface, every skb is only forwarded to upper layer.
+ * When vif is an AP interface, multicast skb are forwarded and resent, whereas
+ * skb for other BSS's STA are only resent.
+ */
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0))
+#define RAISE_RX_SOFTIRQ() \
+ cpu_raise_softirq(smp_processor_id(), NET_RX_SOFTIRQ)
+#endif /* LINUX_VERSION_CODE */
+
+void rwnx_rx_data_skb_resend(struct rwnx_hw *rwnx_hw, struct rwnx_vif *rwnx_vif,
+ struct sk_buff *skb, struct hw_rxhdr *rxhdr)
+{
+ struct sk_buff *rx_skb = skb;
+ //bool amsdu = rxhdr->flags_is_amsdu;
+ const struct ethhdr *eth;
+ struct sk_buff *skb_copy;
+
+ rx_skb->dev = rwnx_vif->ndev;
+ skb_reset_mac_header(rx_skb);
+ eth = eth_hdr(rx_skb);
+
+ //printk("resend\n");
+ /* resend pkt on wireless interface */
+ /* always need to copy buffer when forward=0 to get enough headrom for tsdesc */
+ skb_copy = skb_copy_expand(rx_skb, sizeof(struct rwnx_txhdr) +
+ RWNX_SWTXHDR_ALIGN_SZ + 3 + 24 + 8, 0, GFP_ATOMIC);
+
+ if (skb_copy) {
+ int res;
+ skb_copy->protocol = htons(ETH_P_802_3);
+ skb_reset_network_header(skb_copy);
+ skb_reset_mac_header(skb_copy);
+
+ rwnx_vif->is_resending = true;
+ res = dev_queue_xmit(skb_copy);
+ rwnx_vif->is_resending = false;
+ /* note: buffer is always consummed by dev_queue_xmit */
+ if (res == NET_XMIT_DROP) {
+ rwnx_vif->net_stats.rx_dropped++;
+ rwnx_vif->net_stats.tx_dropped++;
+ } else if (res != NET_XMIT_SUCCESS) {
+ netdev_err(rwnx_vif->ndev,
+ "Failed to re-send buffer to driver (res=%d)",
+ res);
+ rwnx_vif->net_stats.tx_errors++;
+ }
+ } else {
+ netdev_err(rwnx_vif->ndev, "Failed to copy skb");
+ }
+}
+#ifdef AICWF_RX_REORDER
+static void rwnx_rx_data_skb_forward(struct rwnx_hw *rwnx_hw, struct rwnx_vif *rwnx_vif,
+ struct sk_buff *skb, struct hw_rxhdr *rxhdr)
+{
+ struct sk_buff *rx_skb;
+
+ rx_skb = skb;
+ rx_skb->dev = rwnx_vif->ndev;
+ skb_reset_mac_header(rx_skb);
+
+ /* Update statistics */
+ rwnx_vif->net_stats.rx_packets++;
+ rwnx_vif->net_stats.rx_bytes += rx_skb->len;
+
+ //printk("forward\n");
+#ifdef CONFIG_BR_SUPPORT
+ void *br_port = NULL;
+
+ if (1) {//(check_fwstate(pmlmepriv, WIFI_STATION_STATE | WIFI_ADHOC_STATE) == _TRUE) {
+ /* Insert NAT2.5 RX here! */
+ #if (LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 35))
+ br_port = rwnx_vif->ndev->br_port;
+ #else
+ rcu_read_lock();
+ br_port = rcu_dereference(rwnx_vif->ndev->rx_handler_data);
+ rcu_read_unlock();
+ #endif
+
+ if (br_port) {
+ int nat25_handle_frame(struct rwnx_vif *vif, struct sk_buff *skb);
+
+ if (nat25_handle_frame(rwnx_vif, rx_skb) == -1) {
+ /* priv->ext_stats.rx_data_drops++; */
+ /* DEBUG_ERR("RX DROP: nat25_handle_frame fail!\n"); */
+ /* return FAIL; */
+
+ }
+ }
+ }
+#endif /* CONFIG_BR_SUPPORT */
+
+ rwnx_skb_align_8bytes(rx_skb);
+
+ rx_skb->protocol = eth_type_trans(rx_skb, rwnx_vif->ndev);
+ memset(rx_skb->cb, 0, sizeof(rx_skb->cb));
+ REG_SW_SET_PROFILING(rwnx_hw, SW_PROF_IEEE80211RX);
+ #ifdef CONFIG_RX_NETIF_RECV_SKB //modify by aic
+ local_bh_disable();
+ netif_receive_skb(rx_skb);
+ local_bh_enable();
+ #else
+ if (in_interrupt()) {
+ netif_rx(rx_skb);
+ } else {
+ /*
+ * If the receive is not processed inside an ISR, the softirqd must be woken explicitly to service the NET_RX_SOFTIRQ.
+ * * In 2.6 kernels, this is handledby netif_rx_ni(), but in earlier kernels, we need to do it manually.
+ */
+ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
+ netif_rx_ni(rx_skb);
+ #else
+ ulong flags;
+ netif_rx(rx_skb);
+ local_irq_save(flags);
+ RAISE_RX_SOFTIRQ();
+ local_irq_restore(flags);
+ #endif
+ }
+ #endif
+ REG_SW_CLEAR_PROFILING(rwnx_hw, SW_PROF_IEEE80211RX);
+
+ rwnx_hw->stats.last_rx = jiffies;
+}
+#endif
+
+
+static bool rwnx_rx_data_skb(struct rwnx_hw *rwnx_hw, struct rwnx_vif *rwnx_vif,
+ struct sk_buff *skb, struct hw_rxhdr *rxhdr)
+{
+ struct sk_buff_head list;
+ struct sk_buff *rx_skb;
+ bool amsdu = rxhdr->flags_is_amsdu;
+ bool resend = false, forward = true;
+
+ skb->dev = rwnx_vif->ndev;
+
+ __skb_queue_head_init(&list);
+
+ if (amsdu) {
+ int count;
+ ieee80211_amsdu_to_8023s(skb, &list, rwnx_vif->ndev->dev_addr,
+ RWNX_VIF_TYPE(rwnx_vif), 0, NULL, NULL, false);
+
+ count = skb_queue_len(&list);
+ if (count > ARRAY_SIZE(rwnx_hw->stats.amsdus_rx))
+ count = ARRAY_SIZE(rwnx_hw->stats.amsdus_rx);
+ rwnx_hw->stats.amsdus_rx[count - 1]++;
+ } else {
+ rwnx_hw->stats.amsdus_rx[0]++;
+ __skb_queue_head(&list, skb);
+ }
+
+ if (((RWNX_VIF_TYPE(rwnx_vif) == NL80211_IFTYPE_AP) ||
+ (RWNX_VIF_TYPE(rwnx_vif) == NL80211_IFTYPE_AP_VLAN) ||
+ (RWNX_VIF_TYPE(rwnx_vif) == NL80211_IFTYPE_P2P_GO)) &&
+ !(rwnx_vif->ap.flags & RWNX_AP_ISOLATE)) {
+ const struct ethhdr *eth;
+ rx_skb = skb_peek(&list);
+ skb_reset_mac_header(rx_skb);
+ eth = eth_hdr(rx_skb);
+
+ if (unlikely(is_multicast_ether_addr(eth->h_dest))) {
+ /* broadcast pkt need to be forwared to upper layer and resent
+ on wireless interface */
+ resend = true;
+ } else {
+ /* unicast pkt for STA inside the BSS, no need to forward to upper
+ layer simply resend on wireless interface */
+ if (rxhdr->flags_dst_idx != RWNX_INVALID_STA)
+ {
+ struct rwnx_sta *sta = &rwnx_hw->sta_table[rxhdr->flags_dst_idx];
+ if (sta->valid && (sta->vlan_idx == rwnx_vif->vif_index))
+ {
+ forward = false;
+ resend = true;
+ }
+ }
+ }
+ } else if (RWNX_VIF_TYPE(rwnx_vif) == NL80211_IFTYPE_MESH_POINT) {
+ const struct ethhdr *eth;
+ rx_skb = skb_peek(&list);
+ skb_reset_mac_header(rx_skb);
+ eth = eth_hdr(rx_skb);
+
+ if (!is_multicast_ether_addr(eth->h_dest)) {
+ /* unicast pkt for STA inside the BSS, no need to forward to upper
+ layer simply resend on wireless interface */
+ if (rxhdr->flags_dst_idx != RWNX_INVALID_STA)
+ {
+ forward = false;
+ resend = true;
+ }
+ }
+ }
+
+ while (!skb_queue_empty(&list)) {
+ rx_skb = __skb_dequeue(&list);
+
+ /* resend pkt on wireless interface */
+ if (resend) {
+ struct sk_buff *skb_copy;
+ /* always need to copy buffer when forward=0 to get enough headrom for tsdesc */
+ skb_copy = skb_copy_expand(rx_skb, sizeof(struct rwnx_txhdr) +
+ RWNX_SWTXHDR_ALIGN_SZ + 3 + 24 + 8, 0, GFP_ATOMIC);
+
+ if (skb_copy) {
+ int res;
+ skb_copy->protocol = htons(ETH_P_802_3);
+ skb_reset_network_header(skb_copy);
+ skb_reset_mac_header(skb_copy);
+
+ rwnx_vif->is_resending = true;
+ res = dev_queue_xmit(skb_copy);
+ rwnx_vif->is_resending = false;
+ /* note: buffer is always consummed by dev_queue_xmit */
+ if (res == NET_XMIT_DROP) {
+ rwnx_vif->net_stats.rx_dropped++;
+ rwnx_vif->net_stats.tx_dropped++;
+ } else if (res != NET_XMIT_SUCCESS) {
+ netdev_err(rwnx_vif->ndev,
+ "Failed to re-send buffer to driver (res=%d)",
+ res);
+ rwnx_vif->net_stats.tx_errors++;
+ }
+ } else {
+ netdev_err(rwnx_vif->ndev, "Failed to copy skb");
+ }
+ }
+
+ /* forward pkt to upper layer */
+ if (forward) {
+#ifdef CONFIG_BR_SUPPORT
+ void *br_port = NULL;
+
+
+ /* Update statistics */
+ rwnx_vif->net_stats.rx_packets++;
+ rwnx_vif->net_stats.rx_bytes += rx_skb->len;
+
+ if (1) {//(check_fwstate(pmlmepriv, WIFI_STATION_STATE | WIFI_ADHOC_STATE) == _TRUE) {
+ /* Insert NAT2.5 RX here! */
+#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 35))
+ br_port = rwnx_vif->ndev->br_port;
+#else
+ rcu_read_lock();
+ br_port = rcu_dereference(rwnx_vif->ndev->rx_handler_data);
+ rcu_read_unlock();
+#endif
+
+ if (br_port) {
+ int nat25_handle_frame(struct rwnx_vif *vif, struct sk_buff *skb);
+
+ if (nat25_handle_frame(rwnx_vif, rx_skb) == -1) {
+ /* priv->ext_stats.rx_data_drops++; */
+ /* DEBUG_ERR("RX DROP: nat25_handle_frame fail!\n"); */
+ /* return FAIL; */
+ }
+ }
+ }
+#endif /* CONFIG_BR_SUPPORT */
+
+ rwnx_skb_align_8bytes(rx_skb);
+
+ rx_skb->protocol = eth_type_trans(rx_skb, rwnx_vif->ndev);
+
+#ifdef AICWF_ARP_OFFLOAD
+ if(RWNX_VIF_TYPE(rwnx_vif) == NL80211_IFTYPE_STATION || RWNX_VIF_TYPE(rwnx_vif) == NL80211_IFTYPE_P2P_CLIENT)
+ arpoffload_proc(rx_skb, rwnx_vif);
+#endif
+ memset(rx_skb->cb, 0, sizeof(rx_skb->cb));
+ REG_SW_SET_PROFILING(rwnx_hw, SW_PROF_IEEE80211RX);
+ #ifdef CONFIG_RX_NETIF_RECV_SKB //modify by aic
+ local_bh_disable();
+ netif_receive_skb(rx_skb);
+ local_bh_enable();
+ #else
+ if (in_interrupt()) {
+ netif_rx(rx_skb);
+ } else {
+ /*
+ * If the receive is not processed inside an ISR, the softirqd must be woken explicitly to service the NET_RX_SOFTIRQ.
+ * * In 2.6 kernels, this is handledby netif_rx_ni(), but in earlier kernels, we need to do it manually.
+ */
+ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
+ netif_rx_ni(rx_skb);
+ #else
+ ulong flags;
+ netif_rx(rx_skb);
+ local_irq_save(flags);
+ RAISE_RX_SOFTIRQ();
+ local_irq_restore(flags);
+ #endif
+ }
+ #endif
+ REG_SW_CLEAR_PROFILING(rwnx_hw, SW_PROF_IEEE80211RX);
+
+ rwnx_hw->stats.last_rx = jiffies;
+ }
+ }
+
+ return forward;
+}
+
+#ifdef CONFIG_HE_FOR_OLD_KERNEL
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 9, 0))
+const u8 *cfg80211_find_ie_match(u8 eid, const u8 *ies, int len,
+ const u8 *match, int match_len,
+ int match_offset)
+{
+ const struct element *elem;
+
+ /* match_offset can't be smaller than 2, unless match_len is
+ * zero, in which case match_offset must be zero as well.
+ */
+ if (WARN_ON((match_len && match_offset < 2) ||
+ (!match_len && match_offset)))
+ return NULL;
+
+ for_each_element_id(elem, eid, ies, len) {
+ if (elem->datalen >= match_offset - 2 + match_len &&
+ !memcmp(elem->data + match_offset - 2, match, match_len))
+ return (void *)elem;
+ }
+
+ return NULL;
+}
+#endif
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0))
+
+static inline const u8 *cfg80211_find_ext_ie(u8 ext_eid, const u8* ies, int len)
+{
+ return cfg80211_find_ie_match(WLAN_EID_EXTENSION, ies, len,
+ &ext_eid, 1, 2);
+}
+#endif
+
+#endif
+
+
+/**
+ * rwnx_rx_mgmt - Process one 802.11 management frame
+ *
+ * @rwnx_hw: main driver data
+ * @rwnx_vif: vif to upload the buffer to
+ * @skb: skb received
+ * @rxhdr: HW rx descriptor
+ *
+ * Forward the management frame to a given interface.
+ */
+static void rwnx_rx_mgmt(struct rwnx_hw *rwnx_hw, struct rwnx_vif *rwnx_vif,
+ struct sk_buff *skb, struct hw_rxhdr *hw_rxhdr)
+{
+ struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
+ struct rx_vector_1 *rxvect = &hw_rxhdr->hwvect.rx_vect1;
+#ifdef CONFIG_HE_FOR_OLD_KERNEL
+ struct ieee80211_he_cap_elem *he;
+#endif
+
+ //printk("rwnx_rx_mgmt\n");
+#if (defined CONFIG_HE_FOR_OLD_KERNEL) || (defined CONFIG_VHT_FOR_OLD_KERNEL)
+ struct aic_sta *sta = &rwnx_hw->aic_table[rwnx_vif->ap.aic_index];
+ const u8* ie;
+ u32 len;
+
+ if (ieee80211_is_assoc_req(mgmt->frame_control) && rwnx_vif->wdev.iftype == NL80211_IFTYPE_AP) {
+ printk("ASSOC_REQ: sta_idx %d MAC %pM\n", rwnx_vif->ap.aic_index, mgmt->sa);
+ sta->sta_idx = rwnx_vif->ap.aic_index;
+ len = skb->len - (mgmt->u.assoc_req.variable - skb->data);
+
+ #ifdef CONFIG_HE_FOR_OLD_KERNEL
+ ie = cfg80211_find_ext_ie(WLAN_EID_EXT_HE_CAPABILITY, mgmt->u.assoc_req.variable, len);
+ if (ie && ie[1] >= sizeof(*he) + 1) {
+ printk("assoc_req: find he\n");
+ sta->he = true;
+ }
+ else {
+ printk("assoc_req: no find he\n");
+ sta->he = false;
+ }
+ #endif
+
+ #ifdef CONFIG_VHT_FOR_OLD_KERNEL
+ struct ieee80211_vht_cap *vht;
+ ie = cfg80211_find_ie(WLAN_EID_VHT_CAPABILITY, mgmt->u.assoc_req.variable, len);
+ if (ie && ie[1] >= sizeof(*vht)) {
+ printk("assoc_req: find vht\n");
+ sta->vht = true;
+ } else {
+ printk("assoc_req: no find vht\n");
+ sta->vht = false;
+ }
+ #endif
+ }
+#endif
+
+ if (ieee80211_is_beacon(mgmt->frame_control)) {
+ if ((RWNX_VIF_TYPE(rwnx_vif) == NL80211_IFTYPE_MESH_POINT) &&
+ hw_rxhdr->flags_new_peer) {
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 0, 0))
+ cfg80211_notify_new_peer_candidate(rwnx_vif->ndev, mgmt->sa,
+ mgmt->u.beacon.variable,
+ skb->len - offsetof(struct ieee80211_mgmt,
+ u.beacon.variable),
+ GFP_ATOMIC);
+#else
+ /* TODO: the value of parameter sig_dbm need to be confirmed */
+ cfg80211_notify_new_peer_candidate(rwnx_vif->ndev, mgmt->sa,
+ mgmt->u.beacon.variable,
+ skb->len - offsetof(struct ieee80211_mgmt,
+ u.beacon.variable),
+ rxvect->rssi1, GFP_ATOMIC);
+#endif
+ } else {
+
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 8, 0)
+ cfg80211_report_obss_beacon(rwnx_hw->wiphy, skb->data, skb->len,
+ hw_rxhdr->phy_info.phy_prim20_freq,
+ rxvect->rssi1, GFP_KERNEL);
+ #else
+ cfg80211_report_obss_beacon(rwnx_hw->wiphy, skb->data, skb->len,
+ hw_rxhdr->phy_info.phy_prim20_freq,
+ rxvect->rssi1);
+ #endif
+ }
+ } else if ((ieee80211_is_deauth(mgmt->frame_control) ||
+ ieee80211_is_disassoc(mgmt->frame_control)) &&
+ (mgmt->u.deauth.reason_code == WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA ||
+ mgmt->u.deauth.reason_code == WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA)) {
+ #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0) // TODO: process unprot mgmt
+ cfg80211_rx_unprot_mlme_mgmt(rwnx_vif->ndev, skb->data, skb->len);
+ #endif
+ } else if ((RWNX_VIF_TYPE(rwnx_vif) == NL80211_IFTYPE_STATION) &&
+ (ieee80211_is_action(mgmt->frame_control) &&
+ (mgmt->u.action.category == 6))) {
+ struct cfg80211_ft_event_params ft_event;
+ ft_event.target_ap = (uint8_t *)&mgmt->u.action + ETH_ALEN + 2;
+ ft_event.ies = (uint8_t *)&mgmt->u.action + ETH_ALEN * 2 + 2;
+ ft_event.ies_len = skb->len - (ft_event.ies - (uint8_t *)mgmt);
+ ft_event.ric_ies = NULL;
+ ft_event.ric_ies_len = 0;
+ cfg80211_ft_event(rwnx_vif->ndev, &ft_event);
+ } else {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)
+ cfg80211_rx_mgmt(&rwnx_vif->wdev, hw_rxhdr->phy_info.phy_prim20_freq,
+ rxvect->rssi1, skb->data, skb->len, 0);
+#else
+ cfg80211_rx_mgmt(rwnx_vif->ndev, hw_rxhdr->phy_info.phy_prim20_freq,
+ rxvect->rssi1, skb->data, skb->len, 0);
+#endif
+ }
+}
+
+/**
+ * rwnx_rx_mgmt_any - Process one 802.11 management frame
+ *
+ * @rwnx_hw: main driver data
+ * @skb: skb received
+ * @rxhdr: HW rx descriptor
+ *
+ * Process the management frame and free the corresponding skb.
+ * If vif is not specified in the rx descriptor, the the frame is uploaded
+ * on all active vifs.
+ */
+static void rwnx_rx_mgmt_any(struct rwnx_hw *rwnx_hw, struct sk_buff *skb,
+ struct hw_rxhdr *hw_rxhdr)
+{
+ struct rwnx_vif *rwnx_vif;
+ int vif_idx = hw_rxhdr->flags_vif_idx;
+#ifdef CREATE_TRACE_POINTS
+ trace_mgmt_rx(hw_rxhdr->phy_info.phy_prim20_freq, vif_idx,
+ hw_rxhdr->flags_sta_idx, (struct ieee80211_mgmt *)skb->data);
+#endif
+
+ if (vif_idx == RWNX_INVALID_VIF) {
+ list_for_each_entry(rwnx_vif, &rwnx_hw->vifs, list) {
+ if (! rwnx_vif->up)
+ continue;
+ rwnx_rx_mgmt(rwnx_hw, rwnx_vif, skb, hw_rxhdr);
+ }
+ } else {
+ rwnx_vif = rwnx_rx_get_vif(rwnx_hw, vif_idx);
+ if (rwnx_vif)
+ rwnx_rx_mgmt(rwnx_hw, rwnx_vif, skb, hw_rxhdr);
+ }
+
+ dev_kfree_skb(skb);
+}
+
+/**
+ * rwnx_rx_rtap_hdrlen - Return radiotap header length
+ *
+ * @rxvect: Rx vector used to fill the radiotap header
+ * @has_vend_rtap: boolean indicating if vendor specific data is present
+ *
+ * Compute the length of the radiotap header based on @rxvect and vendor
+ * specific data (if any).
+ */
+static u8 rwnx_rx_rtap_hdrlen(struct rx_vector_1 *rxvect,
+ bool has_vend_rtap)
+{
+ u8 rtap_len;
+
+ /* Compute radiotap header length */
+ rtap_len = sizeof(struct ieee80211_radiotap_header) + 8;
+
+ // Check for multiple antennas
+ if (hweight32(rxvect->antenna_set) > 1)
+ // antenna and antenna signal fields
+ rtap_len += 4 * hweight8(rxvect->antenna_set);
+
+ // TSFT
+ if (!has_vend_rtap) {
+ rtap_len = ALIGN(rtap_len, 8);
+ rtap_len += 8;
+ }
+
+ // IEEE80211_HW_SIGNAL_DBM
+ rtap_len++;
+
+ // Check if single antenna
+ if (hweight32(rxvect->antenna_set) == 1)
+ rtap_len++; //Single antenna
+
+ // padding for RX FLAGS
+ rtap_len = ALIGN(rtap_len, 2);
+
+ // Check for HT frames
+ if ((rxvect->format_mod == FORMATMOD_HT_MF) ||
+ (rxvect->format_mod == FORMATMOD_HT_GF))
+ rtap_len += 3;
+
+ // Check for AMPDU
+ if (!(has_vend_rtap) && ((rxvect->format_mod >= FORMATMOD_VHT) ||
+ ((rxvect->format_mod > FORMATMOD_NON_HT_DUP_OFDM) &&
+ (rxvect->ht.aggregation)))) {
+ rtap_len = ALIGN(rtap_len, 4);
+ rtap_len += 8;
+ }
+
+ // Check for VHT frames
+ if (rxvect->format_mod == FORMATMOD_VHT) {
+ rtap_len = ALIGN(rtap_len, 2);
+ rtap_len += 12;
+ }
+
+ // Check for HE frames
+ if (rxvect->format_mod == FORMATMOD_HE_SU) {
+ rtap_len = ALIGN(rtap_len, 2);
+ rtap_len += sizeof(struct ieee80211_radiotap_he);
+ }
+
+ // Check for multiple antennas
+ if (hweight32(rxvect->antenna_set) > 1) {
+ // antenna and antenna signal fields
+ rtap_len += 2 * hweight8(rxvect->antenna_set);
+ }
+
+ // Check for vendor specific data
+ if (has_vend_rtap) {
+ /* vendor presence bitmap */
+ rtap_len += 4;
+ /* alignment for fixed 6-byte vendor data header */
+ rtap_len = ALIGN(rtap_len, 2);
+ }
+
+ return rtap_len;
+}
+
+/**
+ * rwnx_rx_add_rtap_hdr - Add radiotap header to sk_buff
+ *
+ * @rwnx_hw: main driver data
+ * @skb: skb received (will include the radiotap header)
+ * @rxvect: Rx vector
+ * @phy_info: Information regarding the phy
+ * @hwvect: HW Info (NULL if vendor specific data is available)
+ * @rtap_len: Length of the radiotap header
+ * @vend_rtap_len: radiotap vendor length (0 if not present)
+ * @vend_it_present: radiotap vendor present
+ *
+ * Builds a radiotap header and add it to @skb.
+ */
+static void rwnx_rx_add_rtap_hdr(struct rwnx_hw* rwnx_hw,
+ struct sk_buff *skb,
+ struct rx_vector_1 *rxvect,
+ struct phy_channel_info_desc *phy_info,
+ struct hw_vect *hwvect,
+ int rtap_len,
+ u8 vend_rtap_len,
+ u32 vend_it_present)
+{
+ struct ieee80211_radiotap_header *rtap;
+ u8 *pos, rate_idx;
+ __le32 *it_present;
+ u32 it_present_val = 0;
+ bool fec_coding = false;
+ bool short_gi = false;
+ bool stbc = false;
+ bool aggregation = false;
+
+ rtap = (struct ieee80211_radiotap_header *)skb_push(skb, rtap_len);
+ memset((u8*) rtap, 0, rtap_len);
+
+ rtap->it_version = 0;
+ rtap->it_pad = 0;
+ rtap->it_len = cpu_to_le16(rtap_len + vend_rtap_len);
+
+ it_present = &rtap->it_present;
+
+ // Check for multiple antennas
+ if (hweight32(rxvect->antenna_set) > 1) {
+ int chain;
+ unsigned long chains = rxvect->antenna_set;
+
+ for_each_set_bit(chain, &chains, IEEE80211_MAX_CHAINS) {
+ it_present_val |=
+ BIT(IEEE80211_RADIOTAP_EXT) |
+ BIT(IEEE80211_RADIOTAP_RADIOTAP_NAMESPACE);
+ put_unaligned_le32(it_present_val, it_present);
+ it_present++;
+ it_present_val = BIT(IEEE80211_RADIOTAP_ANTENNA) |
+ BIT(IEEE80211_RADIOTAP_DBM_ANTSIGNAL);
+ }
+ }
+
+ // Check if vendor specific data is present
+ if (vend_rtap_len) {
+ it_present_val |= BIT(IEEE80211_RADIOTAP_VENDOR_NAMESPACE) |
+ BIT(IEEE80211_RADIOTAP_EXT);
+ put_unaligned_le32(it_present_val, it_present);
+ it_present++;
+ it_present_val = vend_it_present;
+ }
+
+ put_unaligned_le32(it_present_val, it_present);
+ pos = (void *)(it_present + 1);
+
+ // IEEE80211_RADIOTAP_TSFT
+ if (hwvect) {
+ rtap->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_TSFT);
+ // padding
+ while ((pos - (u8 *)rtap) & 7)
+ *pos++ = 0;
+ put_unaligned_le64((((u64)le32_to_cpu(hwvect->tsf_hi) << 32) +
+ (u64)le32_to_cpu(hwvect->tsf_lo)), pos);
+ pos += 8;
+ }
+
+ // IEEE80211_RADIOTAP_FLAGS
+ rtap->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_FLAGS);
+ if (hwvect && (!hwvect->frm_successful_rx))
+ *pos |= IEEE80211_RADIOTAP_F_BADFCS;
+ if (!rxvect->pre_type
+ && (rxvect->format_mod <= FORMATMOD_NON_HT_DUP_OFDM))
+ *pos |= IEEE80211_RADIOTAP_F_SHORTPRE;
+ pos++;
+
+ // IEEE80211_RADIOTAP_RATE
+ // check for HT, VHT or HE frames
+ if (rxvect->format_mod >= FORMATMOD_HE_SU) {
+ rate_idx = rxvect->he.mcs;
+ fec_coding = rxvect->he.fec;
+ stbc = rxvect->he.stbc;
+ aggregation = true;
+ *pos = 0;
+ } else if (rxvect->format_mod == FORMATMOD_VHT) {
+ rate_idx = rxvect->vht.mcs;
+ fec_coding = rxvect->vht.fec;
+ short_gi = rxvect->vht.short_gi;
+ stbc = rxvect->vht.stbc;
+ aggregation = true;
+ *pos = 0;
+ } else if (rxvect->format_mod > FORMATMOD_NON_HT_DUP_OFDM) {
+ rate_idx = rxvect->ht.mcs;
+ fec_coding = rxvect->ht.fec;
+ short_gi = rxvect->ht.short_gi;
+ stbc = rxvect->ht.stbc;
+ aggregation = rxvect->ht.aggregation;
+ *pos = 0;
+ } else {
+ struct ieee80211_supported_band* band =
+ rwnx_hw->wiphy->bands[phy_info->phy_band];
+ rtap->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_RATE);
+ BUG_ON((rate_idx = legrates_lut[rxvect->leg_rate]) == -1);
+ if (phy_info->phy_band == NL80211_BAND_5GHZ)
+ rate_idx -= 4; /* rwnx_ratetable_5ghz[0].hw_value == 4 */
+ *pos = DIV_ROUND_UP(band->bitrates[rate_idx].bitrate, 5);
+ }
+ pos++;
+
+ // IEEE80211_RADIOTAP_CHANNEL
+ rtap->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_CHANNEL);
+ put_unaligned_le16(phy_info->phy_prim20_freq, pos);
+ pos += 2;
+
+ if (phy_info->phy_band == NL80211_BAND_5GHZ)
+ put_unaligned_le16(IEEE80211_CHAN_OFDM | IEEE80211_CHAN_5GHZ, pos);
+ else if (rxvect->format_mod > FORMATMOD_NON_HT_DUP_OFDM)
+ put_unaligned_le16(IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ, pos);
+ else
+ put_unaligned_le16(IEEE80211_CHAN_CCK | IEEE80211_CHAN_2GHZ, pos);
+ pos += 2;
+
+ if (hweight32(rxvect->antenna_set) == 1) {
+ // IEEE80211_RADIOTAP_DBM_ANTSIGNAL
+ rtap->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL);
+ *pos++ = rxvect->rssi1;
+
+ // IEEE80211_RADIOTAP_ANTENNA
+ rtap->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_ANTENNA);
+ *pos++ = rxvect->antenna_set;
+ }
+
+ // IEEE80211_RADIOTAP_LOCK_QUALITY is missing
+ // IEEE80211_RADIOTAP_DB_ANTNOISE is missing
+
+ // IEEE80211_RADIOTAP_RX_FLAGS
+ rtap->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_RX_FLAGS);
+ // 2 byte alignment
+ if ((pos - (u8 *)rtap) & 1)
+ *pos++ = 0;
+ put_unaligned_le16(0, pos);
+ //Right now, we only support fcs error (no RX_FLAG_FAILED_PLCP_CRC)
+ pos += 2;
+
+ // Check if HT
+ if ((rxvect->format_mod == FORMATMOD_HT_MF)
+ || (rxvect->format_mod == FORMATMOD_HT_GF)) {
+ rtap->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_MCS);
+ *pos++ = IEEE80211_RADIOTAP_MCS_HAVE_MCS |
+ IEEE80211_RADIOTAP_MCS_HAVE_GI |
+ IEEE80211_RADIOTAP_MCS_HAVE_BW;
+ *pos = 0;
+ if (short_gi)
+ *pos |= IEEE80211_RADIOTAP_MCS_SGI;
+ if (rxvect->ch_bw == PHY_CHNL_BW_40)
+ *pos |= IEEE80211_RADIOTAP_MCS_BW_40;
+ if (rxvect->format_mod == FORMATMOD_HT_GF)
+ *pos |= IEEE80211_RADIOTAP_MCS_FMT_GF;
+ if (fec_coding)
+ *pos |= IEEE80211_RADIOTAP_MCS_FEC_LDPC;
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0)
+ *pos++ |= stbc << 5;
+ #else
+ *pos++ |= stbc << IEEE80211_RADIOTAP_MCS_STBC_SHIFT;
+ #endif
+ *pos++ = rate_idx;
+ }
+
+ // check for HT or VHT frames
+ if (aggregation && hwvect) {
+ // 4 byte alignment
+ while ((pos - (u8 *)rtap) & 3)
+ pos++;
+ rtap->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_AMPDU_STATUS);
+ put_unaligned_le32(hwvect->ampdu_cnt, pos);
+ pos += 4;
+ put_unaligned_le32(0, pos);
+ pos += 4;
+ }
+
+ // Check for VHT frames
+ if (rxvect->format_mod == FORMATMOD_VHT) {
+ u16 vht_details = IEEE80211_RADIOTAP_VHT_KNOWN_GI |
+ IEEE80211_RADIOTAP_VHT_KNOWN_BANDWIDTH;
+ u8 vht_nss = rxvect->vht.nss + 1;
+
+ rtap->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_VHT);
+
+ if ((rxvect->ch_bw == PHY_CHNL_BW_160)
+ && phy_info->phy_center2_freq)
+ vht_details &= ~IEEE80211_RADIOTAP_VHT_KNOWN_BANDWIDTH;
+ put_unaligned_le16(vht_details, pos);
+ pos += 2;
+
+ // flags
+ if (short_gi)
+ *pos |= IEEE80211_RADIOTAP_VHT_FLAG_SGI;
+ if (stbc)
+ *pos |= IEEE80211_RADIOTAP_VHT_FLAG_STBC;
+ pos++;
+
+ // bandwidth
+ if (rxvect->ch_bw == PHY_CHNL_BW_40)
+ *pos++ = 1;
+ if (rxvect->ch_bw == PHY_CHNL_BW_80)
+ *pos++ = 4;
+ else if ((rxvect->ch_bw == PHY_CHNL_BW_160)
+ && phy_info->phy_center2_freq)
+ *pos++ = 0; //80P80
+ else if (rxvect->ch_bw == PHY_CHNL_BW_160)
+ *pos++ = 11;
+ else // 20 MHz
+ *pos++ = 0;
+
+ // MCS/NSS
+ *pos = (rate_idx << 4) | vht_nss;
+ pos += 4;
+ if (fec_coding)
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 15, 0)
+ *pos |= 0x01;
+ #else
+ *pos |= IEEE80211_RADIOTAP_CODING_LDPC_USER0;
+ #endif
+ pos++;
+ // group ID
+ pos++;
+ // partial_aid
+ pos += 2;
+ }
+
+ // Check for HE frames
+ if (rxvect->format_mod == FORMATMOD_HE_SU) {
+ struct ieee80211_radiotap_he he;
+ #define HE_PREP(f, val) cpu_to_le16(FIELD_PREP(IEEE80211_RADIOTAP_HE_##f, val))
+ #define D1_KNOWN(f) cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_##f##_KNOWN)
+ #define D2_KNOWN(f) cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA2_##f##_KNOWN)
+
+ he.data1 = D1_KNOWN(DATA_MCS) | D1_KNOWN(BSS_COLOR) | D1_KNOWN(BEAM_CHANGE) |
+ D1_KNOWN(UL_DL) | D1_KNOWN(CODING) | D1_KNOWN(STBC) |
+ D1_KNOWN(BW_RU_ALLOC) | D1_KNOWN(DOPPLER) | D1_KNOWN(DATA_DCM);
+ he.data2 = D2_KNOWN(GI) | D2_KNOWN(TXBF);
+
+ if (stbc) {
+ he.data6 |= HE_PREP(DATA6_NSTS, 2);
+ he.data3 |= HE_PREP(DATA3_STBC, 1);
+ } else {
+ he.data6 |= HE_PREP(DATA6_NSTS, rxvect->he.nss);
+ }
+
+ he.data3 |= HE_PREP(DATA3_BSS_COLOR, rxvect->he.bss_color);
+ he.data3 |= HE_PREP(DATA3_BEAM_CHANGE, rxvect->he.beam_change);
+ he.data3 |= HE_PREP(DATA3_UL_DL, rxvect->he.uplink_flag);
+ he.data3 |= HE_PREP(DATA3_BSS_COLOR, rxvect->he.bss_color);
+ he.data3 |= HE_PREP(DATA3_DATA_MCS, rxvect->he.mcs);
+ he.data3 |= HE_PREP(DATA3_DATA_DCM, rxvect->he.dcm);
+ he.data3 |= HE_PREP(DATA3_CODING, rxvect->he.fec);
+
+ he.data5 |= HE_PREP(DATA5_GI, rxvect->he.gi_type);
+ he.data5 |= HE_PREP(DATA5_TXBF, rxvect->he.beamformed);
+ he.data5 |= HE_PREP(DATA5_LTF_SIZE, rxvect->he.he_ltf_type + 1);
+
+ switch (rxvect->ch_bw) {
+ case PHY_CHNL_BW_20:
+ he.data5 |= HE_PREP(DATA5_DATA_BW_RU_ALLOC,
+ IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_20MHZ);
+ break;
+ case PHY_CHNL_BW_40:
+ he.data5 |= HE_PREP(DATA5_DATA_BW_RU_ALLOC,
+ IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_40MHZ);
+ break;
+ case PHY_CHNL_BW_80:
+ he.data5 |= HE_PREP(DATA5_DATA_BW_RU_ALLOC,
+ IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_80MHZ);
+ break;
+ case PHY_CHNL_BW_160:
+ he.data5 |= HE_PREP(DATA5_DATA_BW_RU_ALLOC,
+ IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_160MHZ);
+ break;
+ default:
+ WARN_ONCE(1, "Invalid SU BW %d\n", rxvect->ch_bw);
+ }
+
+ he.data6 |= HE_PREP(DATA6_DOPPLER, rxvect->he.doppler);
+
+ /* ensure 2 byte alignment */
+ while ((pos - (u8 *)rtap) & 1)
+ pos++;
+ rtap->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_HE);
+ memcpy(pos, &he, sizeof(he));
+ pos += sizeof(he);
+ }
+
+ // Rx Chains
+ if (hweight32(rxvect->antenna_set) > 1) {
+ int chain;
+ unsigned long chains = rxvect->antenna_set;
+ u8 rssis[4] = {rxvect->rssi1, rxvect->rssi1, rxvect->rssi1, rxvect->rssi1};
+
+ for_each_set_bit(chain, &chains, IEEE80211_MAX_CHAINS) {
+ *pos++ = rssis[chain];
+ *pos++ = chain;
+ }
+ }
+}
+
+/**
+ * rwnx_rx_monitor - Build radiotap header for skb an send it to netdev
+ *
+ * @rwnx_hw: main driver data
+ * @rwnx_vif: vif that received the buffer
+ * @skb: sk_buff received
+ * @hw_rxhdr_ptr: Pointer to HW RX header
+ * @rtap_len: Radiotap Header length
+ *
+ * Add radiotap header to the receved skb and send it to netdev
+ */
+static int rwnx_rx_monitor(struct rwnx_hw *rwnx_hw, struct rwnx_vif *rwnx_vif,
+ struct sk_buff *skb, struct hw_rxhdr *hw_rxhdr_ptr,
+ u8 rtap_len)
+{
+ skb->dev = rwnx_vif->ndev;
+
+ if (rwnx_vif->wdev.iftype != NL80211_IFTYPE_MONITOR) {
+ netdev_err(rwnx_vif->ndev, "not a monitor vif\n");
+ return -1;
+ }
+
+ /* Add RadioTap Header */
+ rwnx_rx_add_rtap_hdr(rwnx_hw, skb, &hw_rxhdr_ptr->hwvect.rx_vect1,
+ &hw_rxhdr_ptr->phy_info, &hw_rxhdr_ptr->hwvect,
+ rtap_len, 0, 0);
+
+ skb_reset_mac_header(skb);
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ skb->pkt_type = PACKET_OTHERHOST;
+ skb->protocol = htons(ETH_P_802_2);
+
+ local_bh_disable();
+ netif_receive_skb(skb);
+ local_bh_enable();
+
+ return 0;
+}
+
+#ifdef AICWF_ARP_OFFLOAD
+void arpoffload_proc(struct sk_buff *skb, struct rwnx_vif *rwnx_vif)
+{
+ struct iphdr *iphead = (struct iphdr *)(skb->data);
+ struct udphdr *udph;
+ struct DHCPInfo *dhcph;
+
+ if(skb->protocol == htons(ETH_P_IP)) { // IP
+ if(iphead->protocol == IPPROTO_UDP) { // UDP
+ udph = (struct udphdr *)((u8 *)iphead + (iphead->ihl << 2));
+ if((udph->source == __constant_htons(SERVER_PORT))
+ && (udph->dest == __constant_htons(CLIENT_PORT))) { // DHCP offset/ack
+ AICWFDBG(LOGDEBUG, "dhcp: offer / ack\n");
+ dhcph = (struct DHCPInfo *)((u8 *)udph + sizeof(struct udphdr));
+ if(dhcph->cookie == htonl(DHCP_MAGIC) && dhcph->op == 2 &&
+ !memcmp(dhcph->chaddr, rwnx_vif->ndev->dev_addr, 6)) { // match magic word
+ u32 length = ntohs(udph->len) - sizeof(struct udphdr) - offsetof(struct DHCPInfo, options);
+ u16 offset = 0;
+ u8 *option = dhcph->options;
+ while (option[offset]!= DHCP_OPTION_END && offset<length) {
+ if (option[offset] == DHCP_OPTION_MESSAGE_TYPE) {
+ if (option[offset+2] == DHCP_ACK) {
+ AICWFDBG(LOGDEBUG, "dhcp: ack\n");
+ dhcped = 1;
+ if(rwnx_vif->sta.group_cipher_type == WLAN_CIPHER_SUITE_CCMP)
+ rwnx_send_arpoffload_en_req(rwnx_vif->rwnx_hw, rwnx_vif, dhcph->yiaddr, 1);
+ else
+ rwnx_send_arpoffload_en_req(rwnx_vif->rwnx_hw, rwnx_vif, dhcph->yiaddr, 0);
+ }
+ }
+ offset += 2 + option[offset+1];
+ }
+ }
+ }
+ }
+ }
+}
+#endif
+
+#ifdef AICWF_RX_REORDER
+void reord_rxframe_free(spinlock_t *lock, struct list_head *q, struct list_head *list)
+{
+ spin_lock_bh(lock);
+ list_add(list, q);
+ spin_unlock_bh(lock);
+}
+
+struct recv_msdu *reord_rxframe_alloc(spinlock_t *lock, struct list_head *q)
+{
+ struct recv_msdu *rxframe;
+
+ spin_lock_bh(lock);
+ if (list_empty(q)) {
+ spin_unlock_bh(lock);
+ return NULL;
+ }
+ rxframe = list_entry(q->next, struct recv_msdu, rxframe_list);
+ list_del_init(q->next);
+ spin_unlock_bh(lock);
+ return rxframe;
+}
+
+struct reord_ctrl_info *reord_init_sta(struct aicwf_rx_priv* rx_priv, const u8 *mac_addr)
+{
+ u8 i = 0;
+ struct reord_ctrl *preorder_ctrl = NULL;
+ struct reord_ctrl_info *reord_info;
+#ifdef AICWF_SDIO_SUPPORT
+ struct aicwf_bus *bus_if = rx_priv->sdiodev->bus_if;
+#else
+ struct aicwf_bus *bus_if = rx_priv->usbdev->bus_if;
+#endif
+
+ if (bus_if->state == BUS_DOWN_ST || rx_priv == NULL) {
+ printk("bad stat!\n");
+ return NULL;
+ }
+
+ reord_info = kmalloc(sizeof(struct reord_ctrl_info), GFP_ATOMIC);
+ if (!reord_info)
+ return NULL;
+
+ memcpy(reord_info->mac_addr, mac_addr, ETH_ALEN);
+ for (i=0; i < 8; i++) {
+ preorder_ctrl = &reord_info->preorder_ctrl[i];
+ preorder_ctrl->enable = true;
+ preorder_ctrl->ind_sn = 0xffff;
+ preorder_ctrl->wsize_b = AICWF_REORDER_WINSIZE;
+ preorder_ctrl->rx_priv= rx_priv;
+ INIT_LIST_HEAD(&preorder_ctrl->reord_list);
+ spin_lock_init(&preorder_ctrl->reord_list_lock);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4,14,0)
+ init_timer(&preorder_ctrl->reord_timer);
+ preorder_ctrl->reord_timer.data = (ulong) preorder_ctrl;
+ preorder_ctrl->reord_timer.function = reord_timeout_handler;
+#else
+ timer_setup(&preorder_ctrl->reord_timer, reord_timeout_handler, 0);
+#endif
+ INIT_WORK(&preorder_ctrl->reord_timer_work, reord_timeout_worker);
+ }
+
+ return reord_info;
+}
+
+int reord_flush_tid(struct aicwf_rx_priv *rx_priv, struct sk_buff *skb, u8 tid)
+{
+ struct reord_ctrl_info *reord_info;
+ struct reord_ctrl *preorder_ctrl;
+ struct rwnx_vif *rwnx_vif = (struct rwnx_vif *)rx_priv->rwnx_vif;
+ struct ethhdr *eh = (struct ethhdr *)(skb->data);
+ u8 *mac;
+ unsigned long flags;
+ u8 found = 0;
+ struct list_head *phead, *plist;
+ struct recv_msdu *prframe;
+ int ret;
+
+ if((rwnx_vif->wdev.iftype == NL80211_IFTYPE_STATION) || (rwnx_vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT))
+ mac = eh->h_dest;
+ else if((rwnx_vif->wdev.iftype == NL80211_IFTYPE_AP) || (rwnx_vif->wdev.iftype == NL80211_IFTYPE_P2P_GO))
+ mac = eh->h_source;
+ else {
+ printk("error mode:%d!\n", rwnx_vif->wdev.iftype);
+ dev_kfree_skb(skb);
+ return -1;
+ }
+
+ spin_lock_bh(&rx_priv->stas_reord_lock);
+ list_for_each_entry(reord_info, &rx_priv->stas_reord_list, list) {
+ if (!memcmp(mac, reord_info->mac_addr, ETH_ALEN)) {
+ found = 1;
+ preorder_ctrl = &reord_info->preorder_ctrl[tid];
+ break;
+ }
+ }
+ if (!found) {
+ spin_unlock_bh(&rx_priv->stas_reord_lock);
+ return 0;
+ }
+ spin_unlock_bh(&rx_priv->stas_reord_lock);
+
+ if(preorder_ctrl->enable == false)
+ return 0;
+ spin_lock_irqsave(&preorder_ctrl->reord_list_lock, flags);
+ phead = &preorder_ctrl->reord_list;
+ while (1) {
+ if (list_empty(phead)) {
+ break;
+ }
+ plist = phead->next;
+ prframe = list_entry(plist, struct recv_msdu, reord_pending_list);
+ reord_single_frame_ind(rx_priv, prframe);
+ list_del_init(&(prframe->reord_pending_list));
+ }
+
+ AICWFDBG(LOGINFO, "flush:tid=%d", tid);
+ preorder_ctrl->enable = false;
+ spin_unlock_irqrestore(&preorder_ctrl->reord_list_lock, flags);
+ if (timer_pending(&preorder_ctrl->reord_timer))
+ ret = del_timer_sync(&preorder_ctrl->reord_timer);
+ cancel_work_sync(&preorder_ctrl->reord_timer_work);
+
+ return 0;
+}
+
+void reord_deinit_sta(struct aicwf_rx_priv* rx_priv, struct reord_ctrl_info *reord_info)
+{
+ u8 i = 0;
+ //unsigned long flags;
+ struct reord_ctrl *preorder_ctrl = NULL;
+ int ret;
+
+ if (rx_priv == NULL) {
+ txrx_err("bad rx_priv!\n");
+ return;
+ }
+
+ AICWFDBG(LOGINFO, "%s\n", __func__);
+
+ for (i=0; i < 8; i++) {
+ struct recv_msdu *req, *next;
+ preorder_ctrl = &reord_info->preorder_ctrl[i];
+ if(preorder_ctrl->enable){
+ preorder_ctrl->enable = false;
+ if (timer_pending(&preorder_ctrl->reord_timer)) {
+ ret = del_timer_sync(&preorder_ctrl->reord_timer);
+ }
+ cancel_work_sync(&preorder_ctrl->reord_timer_work);
+ }
+
+ spin_lock_bh(&preorder_ctrl->reord_list_lock);
+ list_for_each_entry_safe(req, next, &preorder_ctrl->reord_list, reord_pending_list) {
+ list_del_init(&req->reord_pending_list);
+ if(req->pkt != NULL)
+ dev_kfree_skb(req->pkt);
+ req->pkt = NULL;
+ reord_rxframe_free(&rx_priv->freeq_lock, &rx_priv->rxframes_freequeue, &req->rxframe_list);
+ }
+
+ AICWFDBG(LOGINFO, "reord dinit in_irq():%d in_atomic:%d in_softirq:%d\r\n", (int)in_irq()
+ ,(int)in_atomic(), (int)in_softirq());
+ spin_unlock_bh(&preorder_ctrl->reord_list_lock);
+ }
+
+ spin_lock_bh(&rx_priv->stas_reord_lock);
+ list_del(&reord_info->list);
+ spin_unlock_bh(&rx_priv->stas_reord_lock);
+ kfree(reord_info);
+}
+
+int reord_single_frame_ind(struct aicwf_rx_priv *rx_priv, struct recv_msdu *prframe)
+{
+ struct list_head *rxframes_freequeue = NULL;
+ struct sk_buff *skb = NULL;
+ struct rwnx_vif *rwnx_vif = (struct rwnx_vif *)rx_priv->rwnx_vif;
+
+ rxframes_freequeue = &rx_priv->rxframes_freequeue;
+ skb = prframe->pkt;
+ if (skb == NULL) {
+ txrx_err("skb is NULL\n");
+ return -1;
+ }
+
+ if(!prframe->forward) {
+ //printk("single: %d not forward: drop\n", prframe->seq_num);
+ dev_kfree_skb(skb);
+ prframe->pkt = NULL;
+ reord_rxframe_free(&rx_priv->freeq_lock, rxframes_freequeue, &prframe->rxframe_list);
+ return 0;
+ }
+
+ skb->data = prframe->rx_data;
+ skb_set_tail_pointer(skb, prframe->len);
+ skb->len = prframe->len;
+
+ rwnx_vif->net_stats.rx_packets++;
+ rwnx_vif->net_stats.rx_bytes += skb->len;
+ //printk("netif sn=%d, len=%d\n", precv_frame->attrib.seq_num, skb->len);
+
+#ifdef CONFIG_BR_SUPPORT
+ void *br_port = NULL;
+
+ if (1) {//(check_fwstate(pmlmepriv, WIFI_STATION_STATE | WIFI_ADHOC_STATE) == _TRUE) {
+ /* Insert NAT2.5 RX here! */
+#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 35))
+ br_port = rwnx_vif->ndev->br_port;
+#else
+ rcu_read_lock();
+ br_port = rcu_dereference(rwnx_vif->ndev->rx_handler_data);
+ rcu_read_unlock();
+#endif
+
+ if (br_port) {
+ int nat25_handle_frame(struct rwnx_vif *vif, struct sk_buff *skb);
+
+ if (nat25_handle_frame(rwnx_vif, skb) == -1) {
+ /* priv->ext_stats.rx_data_drops++; */
+ /* DEBUG_ERR("RX DROP: nat25_handle_frame fail!\n"); */
+ /* return FAIL; */
+ }
+ }
+ }
+#endif /* CONFIG_BR_SUPPORT */
+
+ skb->dev = rwnx_vif->ndev;
+
+#if 0
+ if(test_log_flag){
+ if(skb->data[42] == 0x80){
+ printk("AIDEN : SN:%d R_SN:%d pid:%d\r\n",
+ prframe->seq_num, (skb->data[44] << 8| skb->data[45]), current->pid);
+ }
+ }
+#endif
+ rwnx_skb_align_8bytes(skb);
+
+ skb->protocol = eth_type_trans(skb, rwnx_vif->ndev);
+
+#ifdef AICWF_ARP_OFFLOAD
+ if(RWNX_VIF_TYPE(rwnx_vif) == NL80211_IFTYPE_STATION || RWNX_VIF_TYPE(rwnx_vif) == NL80211_IFTYPE_P2P_CLIENT) {
+ arpoffload_proc(skb, rwnx_vif);
+ }
+#endif
+ memset(skb->cb, 0, sizeof(skb->cb));
+
+#ifdef CONFIG_RX_NETIF_RECV_SKB//AIDEN test
+ local_bh_disable();
+ netif_receive_skb(skb);
+ local_bh_enable();
+#else
+ if (in_interrupt()) {
+ netif_rx(skb);
+ } else {
+ /*
+ * If the receive is not processed inside an ISR, the softirqd must be woken explicitly to service the NET_RX_SOFTIRQ.
+ * * In 2.6 kernels, this is handledby netif_rx_ni(), but in earlier kernels, we need to do it manually.
+ */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
+ netif_rx_ni(skb);
+#else
+ ulong flags;
+ netif_rx(skb);
+ local_irq_save(flags);
+ RAISE_RX_SOFTIRQ();
+ local_irq_restore(flags);
+#endif
+ }
+#endif//CONFIG_RX_NETIF_RECV_SKB
+ prframe->pkt = NULL;
+ reord_rxframe_free(&rx_priv->freeq_lock, rxframes_freequeue, &prframe->rxframe_list);
+
+ return 0;
+}
+
+bool reord_rxframes_process(struct aicwf_rx_priv *rx_priv, struct reord_ctrl *preorder_ctrl, int bforced)
+{
+ struct list_head *phead, *plist;
+ struct recv_msdu *prframe;
+ bool bPktInBuf = false;
+
+ if (bforced == true) {
+ phead = &preorder_ctrl->reord_list;
+ if (list_empty(phead)) {
+ return false;
+ }
+
+ plist = phead->next;
+ prframe = list_entry(plist, struct recv_msdu, reord_pending_list);
+ preorder_ctrl->ind_sn = prframe->seq_num;
+ }
+
+ phead = &preorder_ctrl->reord_list;
+ if (list_empty(phead)) {
+ return bPktInBuf;
+ }
+
+ list_for_each_entry(prframe, phead, reord_pending_list) {
+ if (!SN_LESS(preorder_ctrl->ind_sn, prframe->seq_num)) {
+ if (SN_EQUAL(preorder_ctrl->ind_sn, prframe->seq_num)) {
+ preorder_ctrl->ind_sn = (preorder_ctrl->ind_sn + 1) & 0xFFF;
+ }
+ } else {
+ bPktInBuf = true;
+ break;
+ }
+ }
+
+ return bPktInBuf;
+}
+
+void reord_rxframes_ind(struct aicwf_rx_priv *rx_priv,
+ struct reord_ctrl *preorder_ctrl)
+{
+ struct list_head *phead, *plist;
+ struct recv_msdu *prframe;
+
+ //spin_lock_bh(&preorder_ctrl->reord_list_lock);//AIDEN
+ phead = &preorder_ctrl->reord_list;
+ while (1) {
+ //spin_lock_bh(&preorder_ctrl->reord_list_lock);
+ if (list_empty(phead)) {
+ //spin_unlock_bh(&preorder_ctrl->reord_list_lock);
+ break;
+ }
+
+ plist = phead->next;
+ prframe = list_entry(plist, struct recv_msdu, reord_pending_list);
+
+ if (!SN_LESS(preorder_ctrl->ind_sn, prframe->seq_num)) {
+ list_del_init(&(prframe->reord_pending_list));
+ //spin_unlock_bh(&preorder_ctrl->reord_list_lock);
+ reord_single_frame_ind(rx_priv, prframe);
+ } else {
+ //spin_unlock_bh(&preorder_ctrl->reord_list_lock);
+ break;
+ }
+ }
+ //spin_unlock_bh(&preorder_ctrl->reord_list_lock);//AIDEN
+}
+
+int reorder_timeout = REORDER_UPDATE_TIME;
+module_param(reorder_timeout, int, 0660);
+
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4,14,0)
+void reord_timeout_handler (ulong data)
+#else
+void reord_timeout_handler (struct timer_list *t)
+#endif
+{
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4,14,0)
+ struct reord_ctrl *preorder_ctrl = (struct reord_ctrl *)data;
+#else
+ struct reord_ctrl *preorder_ctrl = from_timer(preorder_ctrl, t, reord_timer);
+#endif
+
+ AICWFDBG(LOGTRACE, "%s Enter \r\n", __func__);
+
+
+ if (g_rwnx_plat->usbdev->state == USB_DOWN_ST) {
+ usb_err("bus is down\n");
+ return;
+ }
+#if 0//AIDEN
+ struct aicwf_rx_priv *rx_priv = preorder_ctrl->rx_priv;
+
+ if (reord_rxframes_process(rx_priv, preorder_ctrl, true)==true) {
+ mod_timer(&preorder_ctrl->reord_timer, jiffies + msecs_to_jiffies(reorder_timeout/*REORDER_UPDATE_TIME*/));
+ }
+#endif
+
+ if(!work_pending(&preorder_ctrl->reord_timer_work))
+ schedule_work(&preorder_ctrl->reord_timer_work);
+
+}
+
+void reord_timeout_worker(struct work_struct *work)
+{
+ struct reord_ctrl *preorder_ctrl = container_of(work, struct reord_ctrl, reord_timer_work);
+ struct aicwf_rx_priv *rx_priv = preorder_ctrl->rx_priv;
+
+ spin_lock_bh(&preorder_ctrl->reord_list_lock);
+#if 1//AIDEN
+ if (reord_rxframes_process(rx_priv, preorder_ctrl, true)==true) {
+ mod_timer(&preorder_ctrl->reord_timer, jiffies + msecs_to_jiffies(reorder_timeout/*REORDER_UPDATE_TIME*/));
+ }
+#endif
+
+ reord_rxframes_ind(rx_priv, preorder_ctrl);
+ spin_unlock_bh(&preorder_ctrl->reord_list_lock);
+
+ return ;
+}
+
+int reord_process_unit(struct aicwf_rx_priv *rx_priv, struct sk_buff *skb, u16 seq_num, u8 tid, u8 forward)
+{
+ int ret=0;
+ u8 *mac;
+ struct recv_msdu *pframe;
+ struct reord_ctrl *preorder_ctrl;
+ struct reord_ctrl_info *reord_info;
+ struct rwnx_vif *rwnx_vif = (struct rwnx_vif *)rx_priv->rwnx_vif;
+ struct ethhdr *eh = (struct ethhdr *)(skb->data);
+ //u8 *da = eh->h_dest;
+ //u8 is_mcast = ((*da) & 0x01)? 1 : 0;
+
+ if (rwnx_vif == NULL || skb->len <= 14) {
+ dev_kfree_skb(skb);
+ return -1;
+ }
+
+ pframe = reord_rxframe_alloc(&rx_priv->freeq_lock, &rx_priv->rxframes_freequeue);
+ if (!pframe) {
+ dev_kfree_skb(skb);
+ return -1;
+ }
+
+ INIT_LIST_HEAD(&pframe->reord_pending_list);
+ pframe->seq_num = seq_num;
+ pframe->tid = tid;
+ pframe->rx_data = skb->data;
+ pframe->len = skb->len;
+ pframe->pkt = skb;
+ pframe->forward = forward;
+ preorder_ctrl = pframe->preorder_ctrl;
+
+#if 0
+ if ((ntohs(eh->h_proto) == ETH_P_PAE) || is_mcast){
+ printk("%s AIDEN pframe->seq_num:%d is bcast or mcast\r\n", __func__, pframe->seq_num);
+ ret = reord_single_frame_ind(rx_priv, pframe);
+ return ret;
+ }
+#endif
+
+ if((rwnx_vif->wdev.iftype == NL80211_IFTYPE_STATION) || (rwnx_vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT))
+ mac = eh->h_dest;
+ else if((rwnx_vif->wdev.iftype == NL80211_IFTYPE_AP) || (rwnx_vif->wdev.iftype == NL80211_IFTYPE_P2P_GO))
+ mac = eh->h_source;
+ else {
+ dev_kfree_skb(skb);
+ return -1;
+ }
+
+ spin_lock_bh(&rx_priv->stas_reord_lock);
+ list_for_each_entry(reord_info, &rx_priv->stas_reord_list, list) {
+ if (!memcmp(mac, reord_info->mac_addr, ETH_ALEN)) {
+ preorder_ctrl = &reord_info->preorder_ctrl[pframe->tid];
+ break;
+ }
+ }
+
+
+ if (&reord_info->list == &rx_priv->stas_reord_list) {//first time???
+ reord_info = reord_init_sta(rx_priv, mac);
+ //reord_info has 8 preorder_ctrl,
+ //There is a one-to-one matched preorder_ctrl and tid
+ if (!reord_info) {
+ spin_unlock_bh(&rx_priv->stas_reord_lock);
+ dev_kfree_skb(skb);
+ return -1;
+ }
+ list_add_tail(&reord_info->list, &rx_priv->stas_reord_list);
+ preorder_ctrl = &reord_info->preorder_ctrl[pframe->tid];
+ } else {
+ if(preorder_ctrl->enable == false) {
+ preorder_ctrl->enable = true;
+ preorder_ctrl->ind_sn = 0xffff;
+ preorder_ctrl->wsize_b = AICWF_REORDER_WINSIZE;
+ preorder_ctrl->rx_priv= rx_priv;
+ }
+ }
+ spin_unlock_bh(&rx_priv->stas_reord_lock);
+
+ if (preorder_ctrl->enable == false) {
+ preorder_ctrl->ind_sn = pframe->seq_num;
+ spin_lock_bh(&preorder_ctrl->reord_list_lock);//AIDEN
+ reord_single_frame_ind(rx_priv, pframe);
+ spin_unlock_bh(&preorder_ctrl->reord_list_lock);//AIDEN
+ preorder_ctrl->ind_sn = (preorder_ctrl->ind_sn + 1)%4096;
+ return 0;
+ }
+
+ spin_lock_bh(&preorder_ctrl->reord_list_lock);
+ if (reord_need_check(preorder_ctrl, pframe->seq_num)) {
+ if(pframe->rx_data[42] == 0x80){//this is rtp package
+ if(pframe->seq_num == preorder_ctrl->ind_sn){
+ printk("%s pframe->seq_num1:%d \r\n", __func__, pframe->seq_num);
+ reord_single_frame_ind(rx_priv, pframe);//not need to reorder
+ }else{
+ printk("%s free pframe->seq_num:%d \r\n", __func__, pframe->seq_num);
+ if (pframe->pkt){
+ dev_kfree_skb(pframe->pkt);
+ pframe->pkt = NULL;
+ }
+ reord_rxframe_free(&rx_priv->freeq_lock, &rx_priv->rxframes_freequeue, &pframe->rxframe_list);
+ }
+ }else{
+ //printk("%s pframe->seq_num2:%d \r\n", __func__, pframe->seq_num);
+ reord_single_frame_ind(rx_priv, pframe);//not need to reorder
+ }
+
+ spin_unlock_bh(&preorder_ctrl->reord_list_lock);
+ return 0;
+ }
+
+ if (reord_rxframe_enqueue(preorder_ctrl, pframe)) {
+ spin_unlock_bh(&preorder_ctrl->reord_list_lock);
+ goto fail;
+ }
+
+ if (reord_rxframes_process(rx_priv, preorder_ctrl, false) == true) {
+ if (!timer_pending(&preorder_ctrl->reord_timer)) {//if this timer not countdown, mod_timer timer to start count down
+ ret = mod_timer(&preorder_ctrl->reord_timer, jiffies + msecs_to_jiffies(reorder_timeout/*REORDER_UPDATE_TIME*/));
+ }
+ } else {
+ if(timer_pending(&preorder_ctrl->reord_timer)) {
+ ret = del_timer(&preorder_ctrl->reord_timer);
+ }
+ }
+
+ reord_rxframes_ind(rx_priv, preorder_ctrl);
+
+ spin_unlock_bh(&preorder_ctrl->reord_list_lock);
+
+
+
+ return 0;
+
+fail:
+ if (pframe->pkt){
+ dev_kfree_skb(pframe->pkt);
+ pframe->pkt = NULL;
+ }
+ reord_rxframe_free(&rx_priv->freeq_lock, &rx_priv->rxframes_freequeue, &pframe->rxframe_list);
+ return ret;
+}
+
+int reord_need_check(struct reord_ctrl *preorder_ctrl, u16 seq_num)
+{
+ u8 wsize = preorder_ctrl->wsize_b;
+ u16 wend = (preorder_ctrl->ind_sn + wsize -1) & 0xFFF;//0xFFF: 12 bits for seq num
+
+ //first time: wend = 0 + 64 - 1= 63
+
+ if (preorder_ctrl->ind_sn == 0xFFFF) {//first time
+ preorder_ctrl->ind_sn = seq_num;
+ }
+
+ if( SN_LESS(seq_num, preorder_ctrl->ind_sn)) {//first time seq_num = preorder_ctrl->ind_sn
+ return -1;//no need to reord
+ }
+
+ if (SN_EQUAL(seq_num, preorder_ctrl->ind_sn)) {
+ preorder_ctrl->ind_sn = (preorder_ctrl->ind_sn + 1) & 0xFFF;
+ } else if (SN_LESS(wend, seq_num)) {
+ if (seq_num >= (wsize-1))
+ preorder_ctrl->ind_sn = seq_num-(wsize-1);
+ else
+ preorder_ctrl->ind_sn = 0xFFF - (wsize - (seq_num + 1)) + 1;
+ }
+
+ return 0;
+}
+
+int reord_rxframe_enqueue(struct reord_ctrl *preorder_ctrl, struct recv_msdu *prframe)
+{
+ struct list_head *preord_list = &preorder_ctrl->reord_list;
+ struct list_head *phead, *plist;
+ struct recv_msdu *pnextrframe;
+
+//first time:not any prframe in preord_list, so phead = phead->next
+ phead = preord_list;
+ plist = phead->next;
+
+ while(phead != plist) {
+ pnextrframe = list_entry(plist, struct recv_msdu, reord_pending_list);
+ if(SN_LESS(pnextrframe->seq_num, prframe->seq_num)) {
+ plist = plist->next;
+ continue;
+ } else if(SN_EQUAL(pnextrframe->seq_num, prframe->seq_num)) {
+ return -1;
+ } else {
+ break;
+ }
+ }
+
+ //link prframe in plist
+ list_add_tail(&(prframe->reord_pending_list), plist);
+
+ return 0;
+}
+#endif /* AICWF_RX_REORDER */
+
+void remove_sec_hdr_mgmt_frame(struct hw_rxhdr *hw_rxhdr,struct sk_buff *skb)
+{
+ u8 hdr_len = 24;
+ u8 mgmt_header[24] = {0};
+
+ if(!hw_rxhdr->hwvect.ga_frame){
+ if(((skb->data[0] & 0x0C) == 0) && (skb->data[1] & 0x40) == 0x40){ //protect management frame
+ printk("frame type %x\n",skb->data[0]);
+ if(hw_rxhdr->hwvect.decr_status == RWNX_RX_HD_DECR_CCMP128){
+ memcpy(mgmt_header,skb->data,hdr_len);
+ skb_pull(skb,8);
+ memcpy(skb->data,mgmt_header,hdr_len);
+ hw_rxhdr->hwvect.len -= 8;
+ }
+ else {
+ printk("unsupport decr_status:%d\n",hw_rxhdr->hwvect.decr_status);
+ }
+ }
+ }
+}
+
+u8 rwnx_rxdataind_aicwf(struct rwnx_hw *rwnx_hw, void *hostid, void *rx_priv)
+{
+ struct hw_rxhdr *hw_rxhdr;
+ struct rxdesc_tag *rxdesc = NULL;
+ struct rwnx_vif *rwnx_vif;
+ struct sk_buff *skb = hostid;
+ int msdu_offset = sizeof(struct hw_rxhdr) + 2;
+ u16_l status = 0;
+ u8 hdr_len = 24;
+ u8 ra[MAC_ADDR_LEN] = {0};
+ u8 ta[MAC_ADDR_LEN] = {0};
+ u8 ether_type[2] = {0};
+ u8 pull_len = 0;
+ u8 tid = 0;
+ u8 is_qos = 0;
+#ifdef AICWF_RX_REORDER
+ struct aicwf_rx_priv *rx_priv_tmp;
+ u16 seq_num = 0;
+ bool resend = false;
+ bool forward = false;
+#endif
+
+ REG_SW_SET_PROFILING(rwnx_hw, SW_PROF_RWNXDATAIND);
+ hw_rxhdr = (struct hw_rxhdr *)skb->data;
+
+#ifdef AICWF_RX_REORDER
+ if(hw_rxhdr->is_monitor_vif) {
+ status = RX_STAT_MONITOR;
+ //printk("monitor rx\n");
+ }
+#endif
+
+ if(hw_rxhdr->flags_upload)
+ status |= RX_STAT_FORWARD;
+
+ /* Check if we need to delete the buffer */
+ if (status & RX_STAT_DELETE) {
+ /* Remove the SK buffer from the rxbuf_elems table */
+ #if 0
+ rwnx_ipc_rxbuf_elem_pull(rwnx_hw, skb);
+ #endif
+ /* Free the buffer */
+ dev_kfree_skb(skb);
+ goto end;
+ }
+
+ /* Check if we need to forward the buffer coming from a monitor interface */
+ if (status & RX_STAT_MONITOR) {
+ struct sk_buff *skb_monitor = NULL;
+ struct hw_rxhdr hw_rxhdr_copy;
+ u8 rtap_len;
+ u16 frm_len = 0;
+
+ //Check if monitor interface exists and is open
+ rwnx_vif = rwnx_rx_get_vif(rwnx_hw, rwnx_hw->monitor_vif);
+ if (!rwnx_vif) {
+ dev_err(rwnx_hw->dev, "Received monitor frame but there is no monitor interface open\n");
+ goto check_len_update;
+ }
+
+ rwnx_rx_vector_convert(rwnx_hw,
+ &hw_rxhdr->hwvect.rx_vect1,
+ &hw_rxhdr->hwvect.rx_vect2);
+ rtap_len = rwnx_rx_rtap_hdrlen(&hw_rxhdr->hwvect.rx_vect1, false);
+
+ if (status == RX_STAT_MONITOR)
+ {
+ /* Remove the SK buffer from the rxbuf_elems table. It will also
+ unmap the buffer and then sync the buffer for the cpu */
+ //rwnx_ipc_rxbuf_elem_pull(rwnx_hw, skb);
+ skb->data += (msdu_offset + 2); //sdio/usb word allign
+
+ //Save frame length
+ frm_len = le32_to_cpu(hw_rxhdr->hwvect.len);
+
+ // Reserve space for frame
+ skb->len = frm_len;
+
+ //Check if there is enough space to add the radiotap header
+ if (skb_headroom(skb) > rtap_len) {
+
+ skb_monitor = skb;
+
+ //Duplicate the HW Rx Header to override with the radiotap header
+ memcpy(&hw_rxhdr_copy, hw_rxhdr, sizeof(hw_rxhdr_copy));
+
+ hw_rxhdr = &hw_rxhdr_copy;
+ } else {
+ //Duplicate the skb and extend the headroom
+ skb_monitor = skb_copy_expand(skb, rtap_len, 0, GFP_ATOMIC);
+
+ //Reset original skb->data pointer
+ skb->data = (void*) hw_rxhdr;
+ }
+ } else {
+ #ifdef CONFIG_RWNX_MON_DATA
+ skb_monitor = skb_copy_expand(skb, rtap_len, 0, GFP_ATOMIC);
+ skb_monitor->data += (msdu_offset + 2); //sdio/usb word allign
+
+ //Save frame length
+ frm_len = le32_to_cpu(hw_rxhdr->hwvect.len);
+ #endif
+ }
+
+ skb_reset_tail_pointer(skb_monitor);
+ skb_monitor->len = 0;
+ skb_put(skb_monitor, frm_len);
+
+ if (rwnx_rx_monitor(rwnx_hw, rwnx_vif, skb_monitor, hw_rxhdr, rtap_len))
+ dev_kfree_skb(skb_monitor);
+
+ if (status == RX_STAT_MONITOR) {
+ if (skb_monitor != skb) {
+ dev_kfree_skb(skb);
+ }
+ }
+ }
+
+check_len_update:
+ /* Check if we need to update the length */
+ if (status & RX_STAT_LEN_UPDATE) {
+ if (rxdesc){
+ hw_rxhdr->hwvect.len = rxdesc->frame_len;
+ }
+
+ if (status & RX_STAT_ETH_LEN_UPDATE) {
+ /* Update Length Field inside the Ethernet Header */
+ struct ethhdr *hdr = (struct ethhdr *)((u8 *)hw_rxhdr + msdu_offset);
+
+ if (rxdesc){
+ hdr->h_proto = htons(rxdesc->frame_len - sizeof(struct ethhdr));
+ }
+ }
+
+ goto end;
+ }
+
+ /* Check if it must be discarded after informing upper layer */
+ if (status & RX_STAT_SPURIOUS) {
+ struct ieee80211_hdr *hdr;
+
+ hdr = (struct ieee80211_hdr *)(skb->data + msdu_offset);
+ rwnx_vif = rwnx_rx_get_vif(rwnx_hw, hw_rxhdr->flags_vif_idx);
+ if (rwnx_vif) {
+ cfg80211_rx_spurious_frame(rwnx_vif->ndev, hdr->addr2, GFP_ATOMIC);
+ }
+ goto end;
+ }
+
+ /* Check if we need to forward the buffer */
+ if (status & RX_STAT_FORWARD) {
+ rwnx_rx_vector_convert(rwnx_hw,
+ &hw_rxhdr->hwvect.rx_vect1,
+ &hw_rxhdr->hwvect.rx_vect2);
+ skb_pull(skb, msdu_offset + 2); //+2 since sdio allign 58->60
+
+ if(skb->data[1] & 0x80)//htc
+ hdr_len += 4;
+
+ if((skb->data[0] & 0x0f)==0x08) {
+ if((skb->data[0] & 0x80) == 0x80) {//qos data
+ hdr_len += 2;//802.11 mac header len
+ tid = skb->data[24] & 0x0F;
+ is_qos = 1;
+ }
+
+ if((skb->data[1] & 0x3) == 0x1) {// to ds
+ memcpy(ra, &skb->data[16], MAC_ADDR_LEN);//destination addr
+ memcpy(ta, &skb->data[10], MAC_ADDR_LEN);//source addr
+ } else if((skb->data[1] & 0x3) == 0x2) { //from ds
+ memcpy(ta, &skb->data[16], MAC_ADDR_LEN);//destination addr
+ memcpy(ra, &skb->data[4], MAC_ADDR_LEN);//BSSID
+ }
+
+ pull_len += (hdr_len + 8);
+#ifdef AICWF_RX_REORDER
+ seq_num = ((skb->data[22]&0xf0)>>4) | (skb->data[23]<<4);
+#endif
+ switch(hw_rxhdr->hwvect.decr_status)
+ {
+ case RWNX_RX_HD_DECR_CCMP128:
+ pull_len += 8;//ccmp_header
+ //skb_pull(&skb->data[skb->len-8], 8); //ccmp_mic_len
+ memcpy(ether_type, &skb->data[hdr_len + 6 + 8], 2);
+ break;
+ case RWNX_RX_HD_DECR_TKIP:
+ pull_len += 8;//tkip_header
+ memcpy(ether_type, &skb->data[hdr_len + 6 + 8], 2);
+ break;
+ case RWNX_RX_HD_DECR_WEP:
+ pull_len += 4;//wep_header
+ memcpy(ether_type, &skb->data[hdr_len + 6 + 4], 2);
+ break;
+ case RWNX_RX_HD_DECR_WAPI:
+ pull_len += 18;//wapi_header
+ memcpy(ether_type, &skb->data[hdr_len + 6 + 18], 2);
+ break;
+
+ default:
+ memcpy(ether_type, &skb->data[hdr_len + 6], 2);
+ break;
+ }
+
+ skb_pull(skb, pull_len);
+ skb_push(skb, 14);
+ //fill 802.3 header
+ memcpy(skb->data, ra, MAC_ADDR_LEN);//destination addr
+ memcpy(&skb->data[6], ta, MAC_ADDR_LEN);//source addr
+ memcpy(&skb->data[12], ether_type, 2);//802.3 type
+ }
+
+ if (hw_rxhdr->flags_is_80211_mpdu) {
+ remove_sec_hdr_mgmt_frame(hw_rxhdr,skb);
+ rwnx_rx_mgmt_any(rwnx_hw, skb, hw_rxhdr);
+ } else {
+ rwnx_vif = rwnx_rx_get_vif(rwnx_hw, hw_rxhdr->flags_vif_idx);
+
+ if (!rwnx_vif) {
+ dev_err(rwnx_hw->dev, "Frame received but no active vif (%d)",
+ hw_rxhdr->flags_vif_idx);
+ dev_kfree_skb(skb);
+ goto end;
+ }
+
+ if (hw_rxhdr->flags_sta_idx != RWNX_INVALID_STA) {
+ struct rwnx_sta *sta;
+
+ sta = &rwnx_hw->sta_table[hw_rxhdr->flags_sta_idx];
+ rwnx_rx_statistic(rwnx_hw, hw_rxhdr, sta);
+
+ if (sta->vlan_idx != rwnx_vif->vif_index) {
+ rwnx_vif = rwnx_hw->vif_table[sta->vlan_idx];
+ if (!rwnx_vif) {
+ dev_kfree_skb(skb);
+ goto end;
+ }
+ }
+
+ if (hw_rxhdr->flags_is_4addr && !rwnx_vif->use_4addr) {
+ cfg80211_rx_unexpected_4addr_frame(rwnx_vif->ndev,
+ sta->mac_addr, GFP_ATOMIC);
+ }
+ }
+
+ skb->priority = 256 + tid;//hw_rxhdr->flags_user_prio;
+
+#ifdef AICWF_RX_REORDER
+ rx_priv_tmp = rx_priv;
+ rx_priv_tmp->rwnx_vif = (void *)rwnx_vif;
+
+ if( (rwnx_vif->wdev.iftype == NL80211_IFTYPE_STATION) || (rwnx_vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT) ){
+ if(is_qos && hw_rxhdr->flags_need_reord){
+ reord_process_unit((struct aicwf_rx_priv *)rx_priv, skb, seq_num, tid, 1);
+ }else if(is_qos && !hw_rxhdr->flags_need_reord) {
+ reord_flush_tid((struct aicwf_rx_priv *)rx_priv, skb, tid);
+ if (!rwnx_rx_data_skb(rwnx_hw, rwnx_vif, skb, hw_rxhdr))
+ dev_kfree_skb(skb);
+ }
+ else {
+ if (!rwnx_rx_data_skb(rwnx_hw, rwnx_vif, skb, hw_rxhdr))
+ dev_kfree_skb(skb);
+ }
+ } else if( (rwnx_vif->wdev.iftype == NL80211_IFTYPE_AP) || (rwnx_vif->wdev.iftype == NL80211_IFTYPE_P2P_GO) ) {
+ #if 1
+ const struct ethhdr *eth;
+ resend = false;
+ forward = true;
+ skb_reset_mac_header(skb);
+ eth = eth_hdr(skb);
+ //printk("da:%pM, %x,%x, len=%d\n", eth->h_dest, skb->data[12], skb->data[13], skb->len);
+
+ if (unlikely(is_multicast_ether_addr(eth->h_dest))) {
+ /* broadcast pkt need to be forwared to upper layer and resent
+ on wireless interface */
+ resend = true;
+ } else {
+ /* unicast pkt for STA inside the BSS, no need to forward to upper
+ layer simply resend on wireless interface */
+ if (hw_rxhdr->flags_dst_idx != RWNX_INVALID_STA) {
+ struct rwnx_sta *sta = &rwnx_hw->sta_table[hw_rxhdr->flags_dst_idx];
+ if (sta->valid && (sta->vlan_idx == rwnx_vif->vif_index)) {
+ resend = true;
+ forward = false;
+ }
+ }
+ }
+
+ if(resend){
+ rwnx_rx_data_skb_resend(rwnx_hw, rwnx_vif, skb, hw_rxhdr);
+ }
+
+ if(forward) {
+ if (is_qos && hw_rxhdr->flags_need_reord)
+ reord_process_unit((struct aicwf_rx_priv *)rx_priv, skb, seq_num, tid, 1);
+ else if (is_qos && !hw_rxhdr->flags_need_reord) {
+ reord_flush_tid((struct aicwf_rx_priv *)rx_priv, skb, tid);
+ rwnx_rx_data_skb_forward(rwnx_hw, rwnx_vif, skb, hw_rxhdr);
+ } else
+ rwnx_rx_data_skb_forward(rwnx_hw, rwnx_vif, skb, hw_rxhdr);
+ } else if(resend) {
+ if (is_qos && hw_rxhdr->flags_need_reord)
+ reord_process_unit((struct aicwf_rx_priv *)rx_priv, skb, seq_num, tid, 0);
+ else if (is_qos && !hw_rxhdr->flags_need_reord) {
+ reord_flush_tid((struct aicwf_rx_priv *)rx_priv, skb, tid);
+ dev_kfree_skb(skb);
+ }
+ } else
+ dev_kfree_skb(skb);
+ #else
+ if (!rwnx_rx_data_skb(rwnx_hw, rwnx_vif, skb, hw_rxhdr))
+ dev_kfree_skb(skb);
+ #endif
+ }
+#else
+ if (!rwnx_rx_data_skb(rwnx_hw, rwnx_vif, skb, hw_rxhdr))
+ dev_kfree_skb(skb);
+#endif
+ }
+ }
+
+end:
+ REG_SW_CLEAR_PROFILING(rwnx_hw, SW_PROF_RWNXDATAIND);
+ return 0;
+}
+
diff --git a/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_rx.h b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_rx.h
new file mode 100644
index 000000000000..e5480cdc90d4
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_rx.h
@@ -0,0 +1,402 @@
+/**
+ ******************************************************************************
+ *
+ * @file rwnx_rx.h
+ *
+ * Copyright (C) RivieraWaves 2012-2019
+ *
+ ******************************************************************************
+ */
+#ifndef _RWNX_RX_H_
+#define _RWNX_RX_H_
+
+#include "aicwf_txrxif.h"
+
+#define SERVER_PORT 67
+#define CLIENT_PORT 68
+#define DHCP_MAGIC 0x63825363
+#define DHCP_ACK 5
+#define DHCP_OPTION_MESSAGE_TYPE 53 /* RFC 2132 9.6, important for DHCP */
+#define DHCP_OPTION_END 255
+
+enum rx_status_bits
+{
+ /// The buffer can be forwarded to the networking stack
+ RX_STAT_FORWARD = 1 << 0,
+ /// A new buffer has to be allocated
+ RX_STAT_ALLOC = 1 << 1,
+ /// The buffer has to be deleted
+ RX_STAT_DELETE = 1 << 2,
+ /// The length of the buffer has to be updated
+ RX_STAT_LEN_UPDATE = 1 << 3,
+ /// The length in the Ethernet header has to be updated
+ RX_STAT_ETH_LEN_UPDATE = 1 << 4,
+ /// Simple copy
+ RX_STAT_COPY = 1 << 5,
+ /// Spurious frame (inform upper layer and discard)
+ RX_STAT_SPURIOUS = 1 << 6,
+ /// packet for monitor interface
+ RX_STAT_MONITOR = 1 << 7,
+};
+
+
+/*
+ * Decryption status subfields.
+ * {
+ */
+#define RWNX_RX_HD_DECR_UNENC 0 // ENCRYPTION TYPE NONE
+#define RWNX_RX_HD_DECR_WEP 1 // ENCRYPTION TYPE WEP
+#define RWNX_RX_HD_DECR_TKIP 2 // ENCRYPTION TYPE TKIP
+#define RWNX_RX_HD_DECR_CCMP128 3 // ENCRYPTION TYPE CCMP128
+#define RWNX_RX_HD_DECR_CCMP256 4 // ENCRYPTION TYPE CCMP256
+#define RWNX_RX_HD_DECR_GCMP128 5 // ENCRYPTION TYPE GCMP128
+#define RWNX_RX_HD_DECR_GCMP256 6 // ENCRYPTION TYPE GCMP256
+#define RWNX_RX_HD_DECR_WAPI 7 // ENCRYPTION TYPE WAPI
+// @}
+
+//#ifdef CONFIG_RWNX_MON_DATA
+#if 0
+#define RX_MACHDR_BACKUP_LEN 64
+#endif
+
+struct rx_vector_1_old {
+ /** Receive Vector 1a */
+ u32 leg_length :12;
+ u32 leg_rate : 4;
+ u32 ht_length :16;
+
+ /** Receive Vector 1b */
+ u32 _ht_length : 4; // FIXME
+ u32 short_gi : 1;
+ u32 stbc : 2;
+ u32 smoothing : 1;
+ u32 mcs : 7;
+ u32 pre_type : 1;
+ u32 format_mod : 3;
+ u32 ch_bw : 2;
+ u32 n_sts : 3;
+ u32 lsig_valid : 1;
+ u32 sounding : 1;
+ u32 num_extn_ss : 2;
+ u32 aggregation : 1;
+ u32 fec_coding : 1;
+ u32 dyn_bw : 1;
+ u32 doze_not_allowed : 1;
+
+ /** Receive Vector 1c */
+ u32 antenna_set : 8;
+ u32 partial_aid : 9;
+ u32 group_id : 6;
+ u32 first_user : 1;
+ s32 rssi1 : 8;
+
+ /** Receive Vector 1d */
+ s32 rssi2 : 8;
+ s32 rssi3 : 8;
+ s32 rssi4 : 8;
+ u32 reserved_1d : 8;
+};
+
+struct rx_leg_vect
+{
+ u8 dyn_bw_in_non_ht : 1;
+ u8 chn_bw_in_non_ht : 2;
+ u8 rsvd_nht : 4;
+ u8 lsig_valid : 1;
+} __packed;
+
+struct rx_ht_vect
+{
+ u16 sounding : 1;
+ u16 smoothing : 1;
+ u16 short_gi : 1;
+ u16 aggregation : 1;
+ u16 stbc : 1;
+ u16 num_extn_ss : 2;
+ u16 lsig_valid : 1;
+ u16 mcs : 7;
+ u16 fec : 1;
+ u16 length :16;
+} __packed;
+
+struct rx_vht_vect
+{
+ u8 sounding : 1;
+ u8 beamformed : 1;
+ u8 short_gi : 1;
+ u8 rsvd_vht1 : 1;
+ u8 stbc : 1;
+ u8 doze_not_allowed : 1;
+ u8 first_user : 1;
+ u8 rsvd_vht2 : 1;
+ u16 partial_aid : 9;
+ u16 group_id : 6;
+ u16 rsvd_vht3 : 1;
+ u32 mcs : 4;
+ u32 nss : 3;
+ u32 fec : 1;
+ u32 length :20;
+ u32 rsvd_vht4 : 4;
+} __packed;
+
+struct rx_he_vect
+{
+ u8 sounding : 1;
+ u8 beamformed : 1;
+ u8 gi_type : 2;
+ u8 stbc : 1;
+ u8 rsvd_he1 : 3;
+
+ u8 uplink_flag : 1;
+ u8 beam_change : 1;
+ u8 dcm : 1;
+ u8 he_ltf_type : 2;
+ u8 doppler : 1;
+ u8 rsvd_he2 : 2;
+
+ u8 bss_color : 6;
+ u8 rsvd_he3 : 2;
+
+ u8 txop_duration : 7;
+ u8 rsvd_he4 : 1;
+
+ u8 pe_duration : 4;
+ u8 spatial_reuse : 4;
+
+ u8 sig_b_comp_mode : 1;
+ u8 dcm_sig_b : 1;
+ u8 mcs_sig_b : 3;
+ u8 ru_size : 3;
+
+ u32 mcs : 4;
+ u32 nss : 3;
+ u32 fec : 1;
+ u32 length :20;
+ u32 rsvd_he6 : 4;
+} __packed;
+
+struct rx_vector_1 {
+ u8 format_mod : 4;
+ u8 ch_bw : 3;
+ u8 pre_type : 1;
+ u8 antenna_set : 8;
+ s32 rssi_leg : 8;
+ u32 leg_length :12;
+ u32 leg_rate : 4;
+ s32 rssi1 : 8;
+
+ union
+ {
+ struct rx_leg_vect leg;
+ struct rx_ht_vect ht;
+ struct rx_vht_vect vht;
+ struct rx_he_vect he;
+ };
+} __packed;
+
+struct rx_vector_2_old {
+ /** Receive Vector 2a */
+ u32 rcpi : 8;
+ u32 evm1 : 8;
+ u32 evm2 : 8;
+ u32 evm3 : 8;
+
+ /** Receive Vector 2b */
+ u32 evm4 : 8;
+ u32 reserved2b_1 : 8;
+ u32 reserved2b_2 : 8;
+ u32 reserved2b_3 : 8;
+
+};
+
+struct rx_vector_2 {
+ /** Receive Vector 2a */
+ u32 rcpi1 : 8;
+ u32 rcpi2 : 8;
+ u32 rcpi3 : 8;
+ u32 rcpi4 : 8;
+
+ /** Receive Vector 2b */
+ u32 evm1 : 8;
+ u32 evm2 : 8;
+ u32 evm3 : 8;
+ u32 evm4 : 8;
+};
+
+struct phy_channel_info_desc {
+ /** PHY channel information 1 */
+ u32 phy_band : 8;
+ u32 phy_channel_type : 8;
+ u32 phy_prim20_freq : 16;
+ /** PHY channel information 2 */
+ u32 phy_center1_freq : 16;
+ u32 phy_center2_freq : 16;
+};
+
+struct hw_vect {
+ /** Total length for the MPDU transfer */
+ u32 len :16;
+
+ u32 reserved : 8;//data type is included
+ /** AMPDU Status Information */
+ u32 mpdu_cnt : 6;
+ u32 ampdu_cnt : 2;
+
+ /** TSF Low */
+ __le32 tsf_lo;
+ /** TSF High */
+ __le32 tsf_hi;
+
+ /** Receive Vector 1 */
+ struct rx_vector_1 rx_vect1;
+ /** Receive Vector 2 */
+ struct rx_vector_2 rx_vect2;
+
+ /** Status **/
+ u32 rx_vect2_valid : 1;
+ u32 resp_frame : 1;
+ /** Decryption Status */
+ u32 decr_status : 3;
+ u32 rx_fifo_oflow : 1;
+
+ /** Frame Unsuccessful */
+ u32 undef_err : 1;
+ u32 phy_err : 1;
+ u32 fcs_err : 1;
+ u32 addr_mismatch : 1;
+ u32 ga_frame : 1;
+ u32 current_ac : 2;
+
+ u32 frm_successful_rx : 1;
+ /** Descriptor Done */
+ u32 desc_done_rx : 1;
+ /** Key Storage RAM Index */
+ u32 key_sram_index : 10;
+ /** Key Storage RAM Index Valid */
+ u32 key_sram_v : 1;
+ u32 type : 2;
+ u32 subtype : 4;
+};
+
+//#ifdef CONFIG_RWNX_MON_DATA
+#if 0
+/// MAC header backup descriptor
+struct mon_machdrdesc
+{
+ /// Length of the buffer
+ u32 buf_len;
+ /// Buffer containing mac header, LLC and SNAP
+ u8 buffer[RX_MACHDR_BACKUP_LEN];
+};
+#endif
+
+struct hw_rxhdr {
+ /** RX vector */
+ struct hw_vect hwvect;
+
+ /** PHY channel information */
+ struct phy_channel_info_desc phy_info;
+
+ /** RX flags */
+ u32 flags_is_amsdu : 1;
+ u32 flags_is_80211_mpdu: 1;
+ u32 flags_is_4addr : 1;
+ u32 flags_new_peer : 1;
+#if defined(AICWF_SDIO_SUPPORT) || defined(AICWF_USB_SUPPORT)
+ u32 flags_user_prio : 1; // aic: fw not fill any more
+ u32 flags_need_reord : 1;
+ u32 flags_upload : 1;
+#else
+ u32 flags_user_prio : 3;
+#endif
+#ifndef AICWF_RX_REORDER
+ u32 flags_rsvd0 : 1;
+#else
+ u32 is_monitor_vif : 1;
+#endif
+ u32 flags_vif_idx : 8; // 0xFF if invalid VIF index
+ u32 flags_sta_idx : 8; // 0xFF if invalid STA index
+ u32 flags_dst_idx : 8; // 0xFF if unknown destination STA
+//#ifdef CONFIG_RWNX_MON_DATA
+#if 0
+ /// MAC header backup descriptor (used only for MSDU when there is a monitor and a data interface)
+ struct mon_machdrdesc mac_hdr_backup;
+#endif
+ /** Pattern indicating if the buffer is available for the driver */
+ u32 pattern;
+};
+
+extern const u8 legrates_lut[];
+extern u16 legrates_lut_rate[];
+extern u16 tx_legrates_lut_rate[];
+
+struct DHCPInfo {
+ u8 op;
+ u8 htype;
+ u8 hlen;
+ u8 hops;
+ u32 xid;
+ u16 secs;
+ u16 flags;
+ u32 ciaddr;
+ u32 yiaddr;
+ u32 siaddr;
+ u32 giaddr;
+ u8 chaddr[16];
+ u8 sname[64];
+ u8 file[128];
+ u32 cookie;
+ u8 options[308]; /* 312 - cookie */
+};
+
+u8 rwnx_unsup_rx_vec_ind(void *pthis, void *hostid);
+u8 rwnx_rxdataind(void *pthis, void *hostid);
+u8 rwnx_rxdataind_aicwf(struct rwnx_hw *rwnx_hw, void *hostid, void *rx_priv);
+int aicwf_process_rxframes(struct aicwf_rx_priv *rx_priv);
+#ifdef CONFIG_USB_MSG_IN_EP
+int aicwf_process_msg_rxframes(struct aicwf_rx_priv *rx_priv);
+#endif
+
+#ifdef AICWF_ARP_OFFLOAD
+void arpoffload_proc(struct sk_buff *skb, struct rwnx_vif *rwnx_vif);
+#endif
+#ifdef AICWF_RX_REORDER
+struct recv_msdu *reord_rxframe_alloc(spinlock_t *lock, struct list_head *q);
+void reord_rxframe_free(spinlock_t *lock, struct list_head *q, struct list_head *list);
+struct reord_ctrl_info *reord_init_sta( struct aicwf_rx_priv *rx_priv, const u8 *mac_addr);
+void reord_deinit_sta(struct aicwf_rx_priv *rx_priv, struct reord_ctrl_info *reord_info);
+int reord_need_check(struct reord_ctrl *preorder_ctrl, u16 seq_num);
+int reord_rxframe_enqueue(struct reord_ctrl *preorder_ctrl, struct recv_msdu *prframe);
+void reord_timeout_worker(struct work_struct *work);
+int reord_single_frame_ind(struct aicwf_rx_priv *rx_priv, struct recv_msdu *prframe);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4,14,0)
+void reord_timeout_handler (ulong data);
+#else
+void reord_timeout_handler (struct timer_list *t);
+#endif
+
+#endif
+
+#ifdef CONFIG_HE_FOR_OLD_KERNEL
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 4, 197)
+struct element {
+ u8 id;
+ u8 datalen;
+ u8 data[];
+};
+/* element iteration helpers */
+#define for_each_element(_elem, _data, _datalen) \
+ for (_elem = (const struct element *)(_data); \
+ (const u8 *)(_data) + (_datalen) - (const u8 *)_elem >= \
+ (int)sizeof(*_elem) && \
+ (const u8 *)(_data) + (_datalen) - (const u8 *)_elem >= \
+ (int)sizeof(*_elem) + _elem->datalen; \
+ _elem = (const struct element *)(_elem->data + _elem->datalen))
+
+#define for_each_element_id(element, _id, data, datalen) \
+ for_each_element(element, data, datalen) \
+ if (element->id == (_id))
+#endif
+#endif
+
+#endif /* _RWNX_RX_H_ */
diff --git a/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_strs.c b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_strs.c
new file mode 100644
index 000000000000..bafb440515de
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_strs.c
@@ -0,0 +1,261 @@
+/**
+ ******************************************************************************
+ *
+ * @file rwnx_strs.c
+ *
+ * @brief Miscellaneous debug strings
+ *
+ * Copyright (C) RivieraWaves 2014-2019
+ *
+ ******************************************************************************
+ */
+
+#include "lmac_msg.h"
+
+static const char *const rwnx_mmid2str[MSG_I(MM_MAX)] = {
+ [MSG_I(MM_RESET_REQ)] = "MM_RESET_REQ",
+ [MSG_I(MM_RESET_CFM)] = "MM_RESET_CFM",
+ [MSG_I(MM_START_REQ)] = "MM_START_REQ",
+ [MSG_I(MM_START_CFM)] = "MM_START_CFM",
+ [MSG_I(MM_VERSION_REQ)] = "MM_VERSION_REQ",
+ [MSG_I(MM_VERSION_CFM)] = "MM_VERSION_CFM",
+ [MSG_I(MM_ADD_IF_REQ)] = "MM_ADD_IF_REQ",
+ [MSG_I(MM_ADD_IF_CFM)] = "MM_ADD_IF_CFM",
+ [MSG_I(MM_REMOVE_IF_REQ)] = "MM_REMOVE_IF_REQ",
+ [MSG_I(MM_REMOVE_IF_CFM)] = "MM_REMOVE_IF_CFM",
+ [MSG_I(MM_STA_ADD_REQ)] = "MM_STA_ADD_REQ",
+ [MSG_I(MM_STA_ADD_CFM)] = "MM_STA_ADD_CFM",
+ [MSG_I(MM_STA_DEL_REQ)] = "MM_STA_DEL_REQ",
+ [MSG_I(MM_STA_DEL_CFM)] = "MM_STA_DEL_CFM",
+ [MSG_I(MM_SET_FILTER_REQ)] = "MM_SET_FILTER_REQ",
+ [MSG_I(MM_SET_FILTER_CFM)] = "MM_SET_FILTER_CFM",
+ [MSG_I(MM_SET_CHANNEL_REQ)] = "MM_SET_CHANNEL_REQ",
+ [MSG_I(MM_SET_CHANNEL_CFM)] = "MM_SET_CHANNEL_CFM",
+ [MSG_I(MM_SET_DTIM_REQ)] = "MM_SET_DTIM_REQ",
+ [MSG_I(MM_SET_DTIM_CFM)] = "MM_SET_DTIM_CFM",
+ [MSG_I(MM_SET_BEACON_INT_REQ)] = "MM_SET_BEACON_INT_REQ",
+ [MSG_I(MM_SET_BEACON_INT_CFM)] = "MM_SET_BEACON_INT_CFM",
+ [MSG_I(MM_SET_BASIC_RATES_REQ)] = "MM_SET_BASIC_RATES_REQ",
+ [MSG_I(MM_SET_BASIC_RATES_CFM)] = "MM_SET_BASIC_RATES_CFM",
+ [MSG_I(MM_SET_BSSID_REQ)] = "MM_SET_BSSID_REQ",
+ [MSG_I(MM_SET_BSSID_CFM)] = "MM_SET_BSSID_CFM",
+ [MSG_I(MM_SET_EDCA_REQ)] = "MM_SET_EDCA_REQ",
+ [MSG_I(MM_SET_EDCA_CFM)] = "MM_SET_EDCA_CFM",
+ [MSG_I(MM_SET_MODE_REQ)] = "MM_SET_MODE_REQ",
+ [MSG_I(MM_SET_MODE_CFM)] = "MM_SET_MODE_CFM",
+ [MSG_I(MM_SET_VIF_STATE_REQ)] = "MM_SET_VIF_STATE_REQ",
+ [MSG_I(MM_SET_VIF_STATE_CFM)] = "MM_SET_VIF_STATE_CFM",
+ [MSG_I(MM_SET_SLOTTIME_REQ)] = "MM_SET_SLOTTIME_REQ",
+ [MSG_I(MM_SET_SLOTTIME_CFM)] = "MM_SET_SLOTTIME_CFM",
+ [MSG_I(MM_SET_IDLE_REQ)] = "MM_SET_IDLE_REQ",
+ [MSG_I(MM_SET_IDLE_CFM)] = "MM_SET_IDLE_CFM",
+ [MSG_I(MM_KEY_ADD_REQ)] = "MM_KEY_ADD_REQ",
+ [MSG_I(MM_KEY_ADD_CFM)] = "MM_KEY_ADD_CFM",
+ [MSG_I(MM_KEY_DEL_REQ)] = "MM_KEY_DEL_REQ",
+ [MSG_I(MM_KEY_DEL_CFM)] = "MM_KEY_DEL_CFM",
+ [MSG_I(MM_BA_ADD_REQ)] = "MM_BA_ADD_REQ",
+ [MSG_I(MM_BA_ADD_CFM)] = "MM_BA_ADD_CFM",
+ [MSG_I(MM_BA_DEL_REQ)] = "MM_BA_DEL_REQ",
+ [MSG_I(MM_BA_DEL_CFM)] = "MM_BA_DEL_CFM",
+ [MSG_I(MM_PRIMARY_TBTT_IND)] = "MM_PRIMARY_TBTT_IND",
+ [MSG_I(MM_SECONDARY_TBTT_IND)] = "MM_SECONDARY_TBTT_IND",
+ [MSG_I(MM_SET_POWER_REQ)] = "MM_SET_POWER_REQ",
+ [MSG_I(MM_SET_POWER_CFM)] = "MM_SET_POWER_CFM",
+ [MSG_I(MM_DBG_TRIGGER_REQ)] = "MM_DBG_TRIGGER_REQ",
+ [MSG_I(MM_SET_PS_MODE_REQ)] = "MM_SET_PS_MODE_REQ",
+ [MSG_I(MM_SET_PS_MODE_CFM)] = "MM_SET_PS_MODE_CFM",
+ [MSG_I(MM_CHAN_CTXT_ADD_REQ)] = "MM_CHAN_CTXT_ADD_REQ",
+ [MSG_I(MM_CHAN_CTXT_ADD_CFM)] = "MM_CHAN_CTXT_ADD_CFM",
+ [MSG_I(MM_CHAN_CTXT_DEL_REQ)] = "MM_CHAN_CTXT_DEL_REQ",
+ [MSG_I(MM_CHAN_CTXT_DEL_CFM)] = "MM_CHAN_CTXT_DEL_CFM",
+ [MSG_I(MM_CHAN_CTXT_LINK_REQ)] = "MM_CHAN_CTXT_LINK_REQ",
+ [MSG_I(MM_CHAN_CTXT_LINK_CFM)] = "MM_CHAN_CTXT_LINK_CFM",
+ [MSG_I(MM_CHAN_CTXT_UNLINK_REQ)] = "MM_CHAN_CTXT_UNLINK_REQ",
+ [MSG_I(MM_CHAN_CTXT_UNLINK_CFM)] = "MM_CHAN_CTXT_UNLINK_CFM",
+ [MSG_I(MM_CHAN_CTXT_UPDATE_REQ)] = "MM_CHAN_CTXT_UPDATE_REQ",
+ [MSG_I(MM_CHAN_CTXT_UPDATE_CFM)] = "MM_CHAN_CTXT_UPDATE_CFM",
+ [MSG_I(MM_CHAN_CTXT_SCHED_REQ)] = "MM_CHAN_CTXT_SCHED_REQ",
+ [MSG_I(MM_CHAN_CTXT_SCHED_CFM)] = "MM_CHAN_CTXT_SCHED_CFM",
+ [MSG_I(MM_BCN_CHANGE_REQ)] = "MM_BCN_CHANGE_REQ",
+ [MSG_I(MM_BCN_CHANGE_CFM)] = "MM_BCN_CHANGE_CFM",
+ [MSG_I(MM_TIM_UPDATE_REQ)] = "MM_TIM_UPDATE_REQ",
+ [MSG_I(MM_TIM_UPDATE_CFM)] = "MM_TIM_UPDATE_CFM",
+ [MSG_I(MM_CONNECTION_LOSS_IND)] = "MM_CONNECTION_LOSS_IND",
+ [MSG_I(MM_CHANNEL_SWITCH_IND)] = "MM_CHANNEL_SWITCH_IND",
+ [MSG_I(MM_CHANNEL_PRE_SWITCH_IND)] = "MM_CHANNEL_PRE_SWITCH_IND",
+ [MSG_I(MM_REMAIN_ON_CHANNEL_REQ)] = "MM_REMAIN_ON_CHANNEL_REQ",
+ [MSG_I(MM_REMAIN_ON_CHANNEL_CFM)] = "MM_REMAIN_ON_CHANNEL_CFM",
+ [MSG_I(MM_REMAIN_ON_CHANNEL_EXP_IND)] = "MM_REMAIN_ON_CHANNEL_EXP_IND",
+ [MSG_I(MM_PS_CHANGE_IND)] = "MM_PS_CHANGE_IND",
+ [MSG_I(MM_TRAFFIC_REQ_IND)] = "MM_TRAFFIC_REQ_IND",
+ [MSG_I(MM_SET_PS_OPTIONS_REQ)] = "MM_SET_PS_OPTIONS_REQ",
+ [MSG_I(MM_SET_PS_OPTIONS_CFM)] = "MM_SET_PS_OPTIONS_CFM",
+ [MSG_I(MM_P2P_VIF_PS_CHANGE_IND)] = "MM_P2P_VIF_PS_CHANGE_IND",
+ [MSG_I(MM_CSA_COUNTER_IND)] = "MM_CSA_COUNTER_IND",
+ [MSG_I(MM_CHANNEL_SURVEY_IND)] = "MM_CHANNEL_SURVEY_IND",
+ [MSG_I(MM_SET_P2P_NOA_REQ)] = "MM_SET_P2P_NOA_REQ",
+ [MSG_I(MM_SET_P2P_OPPPS_REQ)] = "MM_SET_P2P_OPPPS_REQ",
+ [MSG_I(MM_SET_P2P_NOA_CFM)] = "MM_SET_P2P_NOA_CFM",
+ [MSG_I(MM_SET_P2P_OPPPS_CFM)] = "MM_SET_P2P_OPPPS_CFM",
+ [MSG_I(MM_CFG_RSSI_REQ)] = "MM_CFG_RSSI_REQ",
+ [MSG_I(MM_RSSI_STATUS_IND)] = "MM_RSSI_STATUS_IND",
+ [MSG_I(MM_CSA_FINISH_IND)] = "MM_CSA_FINISH_IND",
+ [MSG_I(MM_CSA_TRAFFIC_IND)] = "MM_CSA_TRAFFIC_IND",
+ [MSG_I(MM_MU_GROUP_UPDATE_REQ)] = "MM_MU_GROUP_UPDATE_REQ",
+ [MSG_I(MM_MU_GROUP_UPDATE_CFM)] = "MM_MU_GROUP_UPDATE_CFM",
+
+ [MSG_I(MM_SET_ARPOFFLOAD_REQ)] = "MM_SET_ARPOFFLOAD_REQ",
+ [MSG_I(MM_SET_ARPOFFLOAD_CFM)] = "MM_SET_ARPOFFLOAD_CFM",
+ [MSG_I(MM_SET_AGG_DISABLE_REQ)] = "MM_SET_AGG_DISABLE_REQ",
+ [MSG_I(MM_SET_AGG_DISABLE_CFM)] = "MM_SET_AGG_DISABLE_CFM",
+ [MSG_I(MM_SET_COEX_REQ)] = "MM_SET_COEX_REQ",
+ [MSG_I(MM_SET_COEX_CFM)] = "MM_SET_COEX_CFM",
+ [MSG_I(MM_SET_RF_CONFIG_REQ)] = "MM_SET_RF_CONFIG_REQ",
+ [MSG_I(MM_SET_RF_CONFIG_CFM)] = "MM_SET_RF_CONFIG_CFM",
+ [MSG_I(MM_SET_RF_CALIB_REQ)] = "MM_SET_RF_CALIB_REQ",
+ [MSG_I(MM_SET_RF_CALIB_CFM)] = "MM_SET_RF_CALIB_CFM",
+
+ [MSG_I(MM_GET_MAC_ADDR_REQ)] = "MM_GET_MAC_ADDR_REQ",
+ [MSG_I(MM_GET_MAC_ADDR_CFM)] = "MM_GET_MAC_ADDR_CFM",
+ [MSG_I(MM_GET_STA_INFO_REQ)] = "MM_GET_STA_INFO_REQ",
+ [MSG_I(MM_GET_STA_INFO_CFM)] = "MM_GET_STA_INFO_CFM",
+ [MSG_I(MM_SET_TXPWR_IDX_LVL_REQ)] = "MM_SET_TXPWR_IDX_LVL_REQ",
+ [MSG_I(MM_SET_TXPWR_IDX_LVL_CFM)] = "MM_SET_TXPWR_IDX_LVL_CFM",
+ [MSG_I(MM_SET_TXPWR_OFST_REQ)] = "MM_SET_TXPWR_OFST_REQ",
+ [MSG_I(MM_SET_TXPWR_OFST_CFM)] = "MM_SET_TXPWR_OFST_CFM",
+ [MSG_I(MM_SET_STACK_START_REQ)] = "MM_SET_STACK_START_REQ",
+ [MSG_I(MM_SET_STACK_START_CFM)] = "MM_SET_STACK_START_CFM",
+ [MSG_I(MM_APM_STALOSS_IND)] = "MM_APM_STALOSS_IND",
+ [MSG_I(MM_SET_VENDOR_HWCONFIG_REQ)] = "MM_SET_VENDOR_HWCONFIG_REQ",
+ [MSG_I(MM_SET_VENDOR_HWCONFIG_CFM)] = "MM_SET_VENDOR_HWCONFIG_CFM",
+ [MSG_I(MM_GET_FW_VERSION_REQ)] = "MM_GET_FW_VERSION_REQ",
+ [MSG_I(MM_GET_FW_VERSION_CFM)] = "MM_GET_FW_VERSION_CFM",
+};
+
+static const char *const rwnx_dbgid2str[MSG_I(DBG_MAX)] = {
+ [MSG_I(DBG_MEM_READ_REQ)] = "DBG_MEM_READ_REQ",
+ [MSG_I(DBG_MEM_READ_CFM)] = "DBG_MEM_READ_CFM",
+ [MSG_I(DBG_MEM_WRITE_REQ)] = "DBG_MEM_WRITE_REQ",
+ [MSG_I(DBG_MEM_WRITE_CFM)] = "DBG_MEM_WRITE_CFM",
+ [MSG_I(DBG_SET_MOD_FILTER_REQ)] = "DBG_SET_MOD_FILTER_REQ",
+ [MSG_I(DBG_SET_MOD_FILTER_CFM)] = "DBG_SET_MOD_FILTER_CFM",
+ [MSG_I(DBG_SET_SEV_FILTER_REQ)] = "DBG_SET_SEV_FILTER_REQ",
+ [MSG_I(DBG_SET_SEV_FILTER_CFM)] = "DBG_SET_SEV_FILTER_CFM",
+ [MSG_I(DBG_ERROR_IND)] = "DBG_ERROR_IND",
+ [MSG_I(DBG_GET_SYS_STAT_REQ)] = "DBG_GET_SYS_STAT_REQ",
+ [MSG_I(DBG_GET_SYS_STAT_CFM)] = "DBG_GET_SYS_STAT_CFM",
+};
+
+static const char *const rwnx_scanid2str[MSG_I(SCAN_MAX)] = {
+ [MSG_I(SCAN_START_REQ)] = "SCAN_START_REQ",
+ [MSG_I(SCAN_START_CFM)] = "SCAN_START_CFM",
+ [MSG_I(SCAN_DONE_IND)] = "SCAN_DONE_IND",
+};
+
+static const char *const rwnx_tdlsid2str[MSG_I(TDLS_MAX)] = {
+ [MSG_I(TDLS_CHAN_SWITCH_CFM)] = "TDLS_CHAN_SWITCH_CFM",
+ [MSG_I(TDLS_CHAN_SWITCH_REQ)] = "TDLS_CHAN_SWITCH_REQ",
+ [MSG_I(TDLS_CHAN_SWITCH_IND)] = "TDLS_CHAN_SWITCH_IND",
+ [MSG_I(TDLS_CHAN_SWITCH_BASE_IND)] = "TDLS_CHAN_SWITCH_BASE_IND",
+ [MSG_I(TDLS_CANCEL_CHAN_SWITCH_REQ)] = "TDLS_CANCEL_CHAN_SWITCH_REQ",
+ [MSG_I(TDLS_CANCEL_CHAN_SWITCH_CFM)] = "TDLS_CANCEL_CHAN_SWITCH_CFM",
+ [MSG_I(TDLS_PEER_PS_IND)] = "TDLS_PEER_PS_IND",
+ [MSG_I(TDLS_PEER_TRAFFIC_IND_REQ)] = "TDLS_PEER_TRAFFIC_IND_REQ",
+ [MSG_I(TDLS_PEER_TRAFFIC_IND_CFM)] = "TDLS_PEER_TRAFFIC_IND_CFM",
+};
+
+#ifdef CONFIG_RWNX_FULLMAC
+
+static const char *const rwnx_scanuid2str[MSG_I(SCANU_MAX)] = {
+ [MSG_I(SCANU_START_REQ)] = "SCANU_START_REQ",
+ [MSG_I(SCANU_START_CFM)] = "SCANU_START_CFM",
+ [MSG_I(SCANU_JOIN_REQ)] = "SCANU_JOIN_REQ",
+ [MSG_I(SCANU_JOIN_CFM)] = "SCANU_JOIN_CFM",
+ [MSG_I(SCANU_RESULT_IND)] = "SCANU_RESULT_IND",
+ [MSG_I(SCANU_FAST_REQ)] = "SCANU_FAST_REQ",
+ [MSG_I(SCANU_FAST_CFM)] = "SCANU_FAST_CFM",
+ [MSG_I(SCANU_VENDOR_IE_REQ)] = "SCANU_VENDOR_IE_REQ",
+ [MSG_I(SCANU_VENDOR_IE_CFM)] = "SCANU_VENDOR_IE_CFM",
+ [MSG_I(SCANU_START_CFM_ADDTIONAL)] = "SCANU_START_CFM_ADDTIONAL",
+ [MSG_I(SCANU_CANCEL_REQ)] = "SCANU_CANCEL_REQ",
+ [MSG_I(SCANU_CANCEL_CFM)] = "SCANU_CANCEL_CFM",
+};
+
+static const char *const rwnx_meid2str[MSG_I(ME_MAX)] = {
+ [MSG_I(ME_CONFIG_REQ)] = "ME_CONFIG_REQ",
+ [MSG_I(ME_CONFIG_CFM)] = "ME_CONFIG_CFM",
+ [MSG_I(ME_CHAN_CONFIG_REQ)] = "ME_CHAN_CONFIG_REQ",
+ [MSG_I(ME_CHAN_CONFIG_CFM)] = "ME_CHAN_CONFIG_CFM",
+ [MSG_I(ME_SET_CONTROL_PORT_REQ)] = "ME_SET_CONTROL_PORT_REQ",
+ [MSG_I(ME_SET_CONTROL_PORT_CFM)] = "ME_SET_CONTROL_PORT_CFM",
+ [MSG_I(ME_TKIP_MIC_FAILURE_IND)] = "ME_TKIP_MIC_FAILURE_IND",
+ [MSG_I(ME_STA_ADD_REQ)] = "ME_STA_ADD_REQ",
+ [MSG_I(ME_STA_ADD_CFM)] = "ME_STA_ADD_CFM",
+ [MSG_I(ME_STA_DEL_REQ)] = "ME_STA_DEL_REQ",
+ [MSG_I(ME_STA_DEL_CFM)] = "ME_STA_DEL_CFM",
+ [MSG_I(ME_TX_CREDITS_UPDATE_IND)]= "ME_TX_CREDITS_UPDATE_IND",
+ [MSG_I(ME_RC_STATS_REQ)] = "ME_RC_STATS_REQ",
+ [MSG_I(ME_RC_STATS_CFM)] = "ME_RC_STATS_CFM",
+ [MSG_I(ME_RC_SET_RATE_REQ)] = "ME_RC_SET_RATE_REQ",
+ [MSG_I(ME_TRAFFIC_IND_REQ)] = "ME_TRAFFIC_IND_REQ",
+ [MSG_I(ME_TRAFFIC_IND_CFM)] = "ME_TRAFFIC_IND_CFM",
+ [MSG_I(ME_SET_PS_MODE_REQ)] = "ME_SET_PS_MODE_REQ",
+ [MSG_I(ME_SET_PS_MODE_CFM)] = "ME_SET_PS_MODE_CFM",
+};
+
+static const char *const rwnx_smid2str[MSG_I(SM_MAX)] = {
+ [MSG_I(SM_CONNECT_REQ)] = "SM_CONNECT_REQ",
+ [MSG_I(SM_CONNECT_CFM)] = "SM_CONNECT_CFM",
+ [MSG_I(SM_CONNECT_IND)] = "SM_CONNECT_IND",
+ [MSG_I(SM_DISCONNECT_REQ)] = "SM_DISCONNECT_REQ",
+ [MSG_I(SM_DISCONNECT_CFM)] = "SM_DISCONNECT_CFM",
+ [MSG_I(SM_DISCONNECT_IND)] = "SM_DISCONNECT_IND",
+ [MSG_I(SM_EXTERNAL_AUTH_REQUIRED_IND)] = "SM_EXTERNAL_AUTH_REQUIRED_IND",
+ [MSG_I(SM_EXTERNAL_AUTH_REQUIRED_RSP)] = "SM_EXTERNAL_AUTH_REQUIRED_RSP",
+};
+
+static const char *const rwnx_apmid2str[MSG_I(APM_MAX)] = {
+ [MSG_I(APM_START_REQ)] = "APM_START_REQ",
+ [MSG_I(APM_START_CFM)] = "APM_START_CFM",
+ [MSG_I(APM_STOP_REQ)] = "APM_STOP_REQ",
+ [MSG_I(APM_STOP_CFM)] = "APM_STOP_CFM",
+ [MSG_I(APM_START_CAC_REQ)] = "APM_START_CAC_REQ",
+ [MSG_I(APM_START_CAC_CFM)] = "APM_START_CAC_CFM",
+ [MSG_I(APM_STOP_CAC_REQ)] = "APM_STOP_CAC_REQ",
+ [MSG_I(APM_STOP_CAC_CFM)] = "APM_STOP_CAC_CFM",
+ [MSG_I(APM_SET_BEACON_IE_REQ)] = "APM_SET_BEACON_IE_REQ",
+ [MSG_I(APM_SET_BEACON_IE_CFM)] = "APM_SET_BEACON_IE_CFM",
+};
+
+static const char *const rwnx_meshid2str[MSG_I(MESH_MAX)] = {
+ [MSG_I(MESH_START_REQ)] = "MESH_START_REQ",
+ [MSG_I(MESH_START_CFM)] = "MESH_START_CFM",
+ [MSG_I(MESH_STOP_REQ)] = "MESH_STOP_REQ",
+ [MSG_I(MESH_STOP_CFM)] = "MESH_STOP_CFM",
+ [MSG_I(MESH_UPDATE_REQ)] = "MESH_UPDATE_REQ",
+ [MSG_I(MESH_UPDATE_CFM)] = "MESH_UPDATE_CFM",
+ [MSG_I(MESH_PATH_CREATE_REQ)] = "MESH_PATH_CREATE_REQ",
+ [MSG_I(MESH_PATH_CREATE_CFM)] = "MESH_PATH_CREATE_CFM",
+ [MSG_I(MESH_PATH_UPDATE_REQ)] = "MESH_PATH_UPDATE_REQ",
+ [MSG_I(MESH_PATH_UPDATE_CFM)] = "MESH_PATH_UPDATE_CFM",
+ [MSG_I(MESH_PROXY_ADD_REQ)] = "MESH_PROXY_ADD_REQ",
+ [MSG_I(MESH_PEER_UPDATE_IND)] = "MESH_PEER_UPDATE_IND",
+ [MSG_I(MESH_PATH_UPDATE_IND)] = "MESH_PATH_UPDATE_IND",
+ [MSG_I(MESH_PROXY_UPDATE_IND)] = "MESH_PROXY_UPDATE_IND",
+};
+
+#endif /* CONFIG_RWNX_FULLMAC */
+
+const char *const *rwnx_id2str[TASK_LAST_EMB + 1] = {
+ [TASK_MM] = rwnx_mmid2str,
+ [TASK_DBG] = rwnx_dbgid2str,
+ [TASK_SCAN] = rwnx_scanid2str,
+ [TASK_TDLS] = rwnx_tdlsid2str,
+#ifdef CONFIG_RWNX_FULLMAC
+ [TASK_SCANU] = rwnx_scanuid2str,
+ [TASK_ME] = rwnx_meid2str,
+ [TASK_SM] = rwnx_smid2str,
+ [TASK_APM] = rwnx_apmid2str,
+ [TASK_MESH] = rwnx_meshid2str,
+#endif
+};
diff --git a/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_strs.h b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_strs.h
new file mode 100644
index 000000000000..5cf3f0dcc156
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_strs.h
@@ -0,0 +1,31 @@
+/**
+ ****************************************************************************************
+ *
+ * @file rwnx_strs.h
+ *
+ * @brief Miscellaneous debug strings
+ *
+ * Copyright (C) RivieraWaves 2014-2019
+ *
+ ****************************************************************************************
+ */
+
+#ifndef _RWNX_STRS_H_
+#define _RWNX_STRS_H_
+
+#ifdef CONFIG_RWNX_FHOST
+
+#define RWNX_ID2STR(tag) "Cmd"
+
+#else
+#include "lmac_msg.h"
+
+#define RWNX_ID2STR(tag) (((MSG_T(tag) < ARRAY_SIZE(rwnx_id2str)) && \
+ (rwnx_id2str[MSG_T(tag)]) && \
+ ((rwnx_id2str[MSG_T(tag)])[MSG_I(tag)])) ? \
+ (rwnx_id2str[MSG_T(tag)])[MSG_I(tag)] : "unknown")
+
+extern const char *const *rwnx_id2str[TASK_LAST_EMB + 1];
+#endif /* CONFIG_RWNX_FHOST */
+
+#endif /* _RWNX_STRS_H_ */
diff --git a/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_tdls.c b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_tdls.c
new file mode 100644
index 000000000000..ce400ce89bde
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_tdls.c
@@ -0,0 +1,796 @@
+/**
+ ******************************************************************************
+ *
+ * @file rwnx_tx.c
+ *
+ * Copyright (C) RivieraWaves 2012-2019
+ *
+ ******************************************************************************
+ */
+
+/**
+ * INCLUDE FILES
+ ******************************************************************************
+ */
+
+#include "rwnx_tdls.h"
+#include "rwnx_compat.h"
+
+/**
+ * FUNCTION DEFINITIONS
+ ******************************************************************************
+ */
+
+static u16
+rwnx_get_tdls_sta_capab(struct rwnx_vif *rwnx_vif, u16 status_code)
+{
+ u16 capab = 0;
+
+ /* The capability will be 0 when sending a failure code */
+ if (status_code != 0)
+ return capab;
+
+ if (rwnx_vif->sta.ap->band != NL80211_BAND_2GHZ)
+ return capab;
+
+ capab |= WLAN_CAPABILITY_SHORT_SLOT_TIME;
+ capab |= WLAN_CAPABILITY_SHORT_PREAMBLE;
+
+ return capab;
+}
+
+static int
+rwnx_tdls_prepare_encap_data(struct rwnx_hw *rwnx_hw, struct rwnx_vif *rwnx_vif,
+ const u8 *peer, u8 action_code, u8 dialog_token,
+ u16 status_code, struct sk_buff *skb)
+{
+ struct ieee80211_tdls_data *tf;
+ tf = (void *)skb_put(skb, sizeof(struct ieee80211_tdls_data) - sizeof(tf->u));
+
+ // set eth header
+ memcpy(tf->da, peer, ETH_ALEN);
+ memcpy(tf->sa, rwnx_hw->wiphy->perm_addr, ETH_ALEN);
+ tf->ether_type = cpu_to_be16(ETH_P_TDLS);
+
+ // set common TDLS info
+ tf->payload_type = WLAN_TDLS_SNAP_RFTYPE;
+ tf->category = WLAN_CATEGORY_TDLS;
+ tf->action_code = action_code;
+
+ // set action specific TDLS info
+ switch (action_code) {
+ case WLAN_TDLS_SETUP_REQUEST:
+ skb_put(skb, sizeof(tf->u.setup_req));
+ tf->u.setup_req.dialog_token = dialog_token;
+ tf->u.setup_req.capability =
+ cpu_to_le16(rwnx_get_tdls_sta_capab(rwnx_vif, status_code));
+ break;
+
+ case WLAN_TDLS_SETUP_RESPONSE:
+ skb_put(skb, sizeof(tf->u.setup_resp));
+ tf->u.setup_resp.status_code = cpu_to_le16(status_code);
+ tf->u.setup_resp.dialog_token = dialog_token;
+ tf->u.setup_resp.capability =
+ cpu_to_le16(rwnx_get_tdls_sta_capab(rwnx_vif, status_code));
+ break;
+
+ case WLAN_TDLS_SETUP_CONFIRM:
+ skb_put(skb, sizeof(tf->u.setup_cfm));
+ tf->u.setup_cfm.status_code = cpu_to_le16(status_code);
+ tf->u.setup_cfm.dialog_token = dialog_token;
+ break;
+
+ case WLAN_TDLS_TEARDOWN:
+ skb_put(skb, sizeof(tf->u.teardown));
+ tf->u.teardown.reason_code = cpu_to_le16(status_code);
+ break;
+
+ case WLAN_TDLS_DISCOVERY_REQUEST:
+ skb_put(skb, sizeof(tf->u.discover_req));
+ tf->u.discover_req.dialog_token = dialog_token;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int
+rwnx_prep_tdls_direct(struct rwnx_hw *rwnx_hw, struct rwnx_vif *rwnx_vif,
+ const u8 *peer, u8 action_code, u8 dialog_token,
+ u16 status_code, struct sk_buff *skb)
+{
+ struct ieee80211_mgmt *mgmt;
+
+ mgmt = (void *)skb_put(skb, 24);
+ memset(mgmt, 0, 24);
+ memcpy(mgmt->da, peer, ETH_ALEN);
+ memcpy(mgmt->sa, rwnx_hw->wiphy->perm_addr, ETH_ALEN);
+ memcpy(mgmt->bssid, rwnx_vif->sta.ap->mac_addr, ETH_ALEN);
+
+ mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
+ IEEE80211_STYPE_ACTION);
+
+ switch (action_code) {
+ case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
+ skb_put(skb, 1 + sizeof(mgmt->u.action.u.tdls_discover_resp));
+ mgmt->u.action.category = WLAN_CATEGORY_PUBLIC;
+ mgmt->u.action.u.tdls_discover_resp.action_code = WLAN_PUB_ACTION_TDLS_DISCOVER_RES;
+ mgmt->u.action.u.tdls_discover_resp.dialog_token = dialog_token;
+ mgmt->u.action.u.tdls_discover_resp.capability =
+ cpu_to_le16(rwnx_get_tdls_sta_capab(rwnx_vif, status_code));
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int
+rwnx_add_srates_ie(struct rwnx_hw *rwnx_hw, struct sk_buff *skb)
+{
+ u8 i, rates, *pos;
+ int rate;
+ struct ieee80211_supported_band *rwnx_band_2GHz = rwnx_hw->wiphy->bands[NL80211_BAND_2GHZ];
+
+ rates = 8;
+
+ if (skb_tailroom(skb) < rates + 2)
+ return -ENOMEM;
+
+ pos = skb_put(skb, rates + 2);
+ *pos++ = WLAN_EID_SUPP_RATES;
+ *pos++ = rates;
+ for (i = 0; i < rates; i++) {
+ rate = rwnx_band_2GHz->bitrates[i].bitrate;
+ rate = DIV_ROUND_UP(rate, 5);
+ *pos++ = (u8)rate;
+ }
+
+ return 0;
+}
+
+static int
+rwnx_add_ext_srates_ie(struct rwnx_hw *rwnx_hw, struct sk_buff *skb)
+{
+ u8 i, exrates, *pos;
+ int rate;
+ struct ieee80211_supported_band *rwnx_band_2GHz = rwnx_hw->wiphy->bands[NL80211_BAND_2GHZ];
+
+ exrates = rwnx_band_2GHz->n_bitrates - 8;
+
+ if (skb_tailroom(skb) < exrates + 2)
+ return -ENOMEM;
+
+ pos = skb_put(skb, exrates + 2);
+ *pos++ = WLAN_EID_EXT_SUPP_RATES;
+ *pos++ = exrates;
+ for (i = 8; i < (8+exrates); i++) {
+ rate = rwnx_band_2GHz->bitrates[i].bitrate;
+ rate = DIV_ROUND_UP(rate, 5);
+ *pos++ = (u8)rate;
+ }
+
+ return 0;
+}
+
+static void
+rwnx_tdls_add_supp_channels(struct rwnx_hw *rwnx_hw, struct sk_buff *skb)
+{
+ /*
+ * Add possible channels for TDLS. These are channels that are allowed
+ * to be active.
+ */
+ u8 subband_cnt = 0;
+ u8 *pos_subband;
+ u8 *pos = skb_put(skb, 2);
+ struct ieee80211_supported_band *rwnx_band_2GHz = rwnx_hw->wiphy->bands[NL80211_BAND_2GHZ];
+ //#ifdef USE_5G
+ struct ieee80211_supported_band *rwnx_band_5GHz = rwnx_hw->wiphy->bands[NL80211_BAND_5GHZ];
+ //#endif
+
+ *pos++ = WLAN_EID_SUPPORTED_CHANNELS;
+
+ /*
+ * 5GHz and 2GHz channels numbers can overlap. Ignore this for now, as
+ * this doesn't happen in real world scenarios.
+ */
+
+ /* 2GHz, with 5MHz spacing */
+ pos_subband = skb_put(skb, 2);
+ if (rwnx_band_2GHz->n_channels > 0)
+ {
+ *pos_subband++ = ieee80211_frequency_to_channel(rwnx_band_2GHz->channels[0].center_freq);
+ *pos_subband++ = rwnx_band_2GHz->n_channels;
+ subband_cnt++;
+ }
+
+ /* 5GHz, with 20MHz spacing */
+ pos_subband = skb_put(skb, 2);
+ //#ifdef USE_5G
+ if(rwnx_hw->band_5g_support){
+ if (rwnx_band_5GHz->n_channels > 0)
+ {
+ *pos_subband++ = ieee80211_frequency_to_channel(rwnx_band_5GHz->channels[0].center_freq);
+ *pos_subband++ = rwnx_band_5GHz->n_channels;
+ subband_cnt++;
+ }
+ }
+ //#endif
+ /* length */
+ *pos = 2 * subband_cnt;
+}
+
+static void
+rwnx_tdls_add_ext_capab(struct rwnx_hw *rwnx_hw, struct sk_buff *skb)
+{
+ u8 *pos = (void *)skb_put(skb, 7);
+ bool chan_switch = rwnx_hw->wiphy->features &
+ NL80211_FEATURE_TDLS_CHANNEL_SWITCH;
+
+ *pos++ = WLAN_EID_EXT_CAPABILITY;
+ *pos++ = 5; /* len */
+ *pos++ = 0x0;
+ *pos++ = 0x0;
+ *pos++ = 0x0;
+ *pos++ = WLAN_EXT_CAPA4_TDLS_BUFFER_STA |
+ (chan_switch ? WLAN_EXT_CAPA4_TDLS_CHAN_SWITCH : 0);
+ *pos++ = WLAN_EXT_CAPA5_TDLS_ENABLED;
+}
+
+static void
+rwnx_add_wmm_info_ie(struct sk_buff *skb, u8 qosinfo)
+{
+ u8 *pos = (void *)skb_put(skb, 9);
+
+ *pos++ = WLAN_EID_VENDOR_SPECIFIC;
+ *pos++ = 7; /* len */
+ *pos++ = 0x00; /* Microsoft OUI 00:50:F2 */
+ *pos++ = 0x50;
+ *pos++ = 0xf2;
+ *pos++ = 2; /* WME */
+ *pos++ = 0; /* WME info */
+ *pos++ = 1; /* WME ver */
+ *pos++ = qosinfo; /* U-APSD no in use */
+}
+
+/* translate numbering in the WMM parameter IE to the mac80211 notation */
+static u8 rwnx_ac_from_wmm(int ac)
+{
+ switch (ac) {
+ default:
+ WARN_ON_ONCE(1);
+ case 0:
+ return AC_BE;
+ case 1:
+ return AC_BK;
+ case 2:
+ return AC_VI;
+ case 3:
+ return AC_VO;
+ }
+}
+
+static void
+rwnx_add_wmm_param_ie(struct sk_buff *skb, u8 acm_bits, u32 *ac_params)
+{
+ struct ieee80211_wmm_param_ie *wmm;
+ int i, j;
+ u8 cw_min, cw_max;
+ bool acm;
+
+ wmm = (void *)skb_put(skb, sizeof(struct ieee80211_wmm_param_ie));
+ memset(wmm, 0, sizeof(*wmm));
+
+ wmm->element_id = WLAN_EID_VENDOR_SPECIFIC;
+ wmm->len = sizeof(*wmm) - 2;
+
+ wmm->oui[0] = 0x00; /* Microsoft OUI 00:50:F2 */
+ wmm->oui[1] = 0x50;
+ wmm->oui[2] = 0xf2;
+ wmm->oui_type = 2; /* WME */
+ wmm->oui_subtype = 1; /* WME param */
+ wmm->version = 1; /* WME ver */
+ wmm->qos_info = 0; /* U-APSD not in use */
+
+ /*
+ * Use the EDCA parameters defined for the BSS, or default if the AP
+ * doesn't support it, as mandated by 802.11-2012 section 10.22.4
+ */
+ for (i = 0; i < AC_MAX; i++) {
+ j = rwnx_ac_from_wmm(i);
+ cw_min = (ac_params[j] & 0xF0 ) >> 4;
+ cw_max = (ac_params[j] & 0xF00 ) >> 8;
+ acm = (acm_bits & (1 << j)) != 0;
+
+ wmm->ac[i].aci_aifsn = (i << 5) | (acm << 4) | (ac_params[j] & 0xF);
+ wmm->ac[i].cw = (cw_max << 4) | cw_min;
+ wmm->ac[i].txop_limit = (ac_params[j] & 0x0FFFF000 ) >> 12;
+ }
+}
+
+static void
+rwnx_tdls_add_oper_classes(struct rwnx_vif *rwnx_vif, struct sk_buff *skb)
+{
+ u8 *pos;
+ u8 op_class;
+ struct cfg80211_chan_def chan_def;
+ struct ieee80211_channel chan;
+
+ chan.band = rwnx_vif->sta.ap->band;
+ chan.center_freq = rwnx_vif->sta.ap->center_freq;
+ chan_def.chan = &chan;
+ chan_def.width = rwnx_vif->sta.ap->width;
+ chan_def.center_freq1 = rwnx_vif->sta.ap->center_freq1;
+ chan_def.center_freq2 = rwnx_vif->sta.ap->center_freq2;
+
+ if (!ieee80211_chandef_to_operating_class(&chan_def, &op_class))
+ return;
+
+ pos = skb_put(skb, 4);
+ *pos++ = WLAN_EID_SUPPORTED_REGULATORY_CLASSES;
+ *pos++ = 2; /* len */
+
+ // current op class
+ *pos++ = op_class;
+ *pos++ = op_class; /* give current operating class as alternate too */
+
+ // need to add 5GHz classes?
+}
+
+static void
+rwnx_ie_build_ht_cap(struct sk_buff *skb, struct ieee80211_sta_ht_cap *ht_cap,
+ u16 cap)
+{
+ u8 *pos;
+ __le16 tmp;
+
+ pos = skb_put(skb, sizeof(struct ieee80211_ht_cap) + 2);
+ *pos++ = WLAN_EID_HT_CAPABILITY;
+ *pos++ = sizeof(struct ieee80211_ht_cap);
+ memset(pos, 0, sizeof(struct ieee80211_ht_cap));
+
+ /* capability flags */
+ tmp = cpu_to_le16(cap);
+ memcpy(pos, &tmp, sizeof(u16));
+ pos += sizeof(u16);
+
+ /* AMPDU parameters */
+ *pos++ = ht_cap->ampdu_factor |
+ (ht_cap->ampdu_density <<
+ IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT);
+
+ /* MCS set */
+ memcpy(pos, &ht_cap->mcs, sizeof(ht_cap->mcs));
+ pos += sizeof(ht_cap->mcs);
+
+ /* extended capabilities */
+ pos += sizeof(__le16);
+
+ /* BF capabilities */
+ pos += sizeof(__le32);
+
+ /* antenna selection */
+ pos += sizeof(u8);
+}
+
+static void
+rwnx_ie_build_vht_cap(struct sk_buff *skb, struct ieee80211_sta_vht_cap *vht_cap,
+ u32 cap)
+{
+ u8 *pos;
+ __le32 tmp;
+
+ pos = skb_put(skb, 14);
+
+ *pos++ = WLAN_EID_VHT_CAPABILITY;
+ *pos++ = sizeof(struct ieee80211_vht_cap);
+ memset(pos, 0, sizeof(struct ieee80211_vht_cap));
+
+ /* capability flags */
+ tmp = cpu_to_le32(cap);
+ memcpy(pos, &tmp, sizeof(u32));
+ pos += sizeof(u32);
+
+ /* VHT MCS set */
+ memcpy(pos, &vht_cap->vht_mcs, sizeof(vht_cap->vht_mcs));
+ pos += sizeof(vht_cap->vht_mcs);
+}
+
+static void
+rwnx_tdls_add_bss_coex_ie(struct sk_buff *skb)
+{
+ u8 *pos = (void *)skb_put(skb, 3);
+
+ *pos++ = WLAN_EID_BSS_COEX_2040;
+ *pos++ = 1; /* len */
+
+ *pos++ = WLAN_BSS_COEX_INFORMATION_REQUEST;
+}
+
+static void
+rwnx_tdls_add_link_ie(struct rwnx_hw *rwnx_hw, struct rwnx_vif *rwnx_vif,
+ struct sk_buff *skb, const u8 *peer,
+ bool initiator)
+{
+ struct ieee80211_tdls_lnkie *lnkid;
+ const u8 *init_addr, *rsp_addr;
+
+ if (initiator) {
+ init_addr = rwnx_hw->wiphy->perm_addr;
+ rsp_addr = peer;
+ } else {
+ init_addr = peer;
+ rsp_addr = rwnx_hw->wiphy->perm_addr;
+ }
+
+ lnkid = (void *)skb_put(skb, sizeof(struct ieee80211_tdls_lnkie));
+
+ lnkid->ie_type = WLAN_EID_LINK_ID;
+ lnkid->ie_len = sizeof(struct ieee80211_tdls_lnkie) - 2;
+
+ memcpy(lnkid->bssid, rwnx_vif->sta.ap->mac_addr, ETH_ALEN);
+ memcpy(lnkid->init_sta, init_addr, ETH_ALEN);
+ memcpy(lnkid->resp_sta, rsp_addr, ETH_ALEN);
+}
+
+static void
+rwnx_tdls_add_aid_ie(struct rwnx_vif *rwnx_vif, struct sk_buff *skb)
+{
+ u8 *pos = (void *)skb_put(skb, 4);
+
+ #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)
+ *pos++ = WLAN_EID_AID;
+ #else
+ *pos++ = 197;
+ #endif
+ *pos++ = 2; /* len */
+ *pos++ = rwnx_vif->sta.ap->aid;
+}
+
+static u8 *
+rwnx_ie_build_ht_oper(struct rwnx_hw *rwnx_hw, struct rwnx_vif *rwnx_vif,
+ u8 *pos, struct ieee80211_sta_ht_cap *ht_cap,
+ u16 prot_mode)
+{
+ struct ieee80211_ht_operation *ht_oper;
+ /* Build HT Information */
+ *pos++ = WLAN_EID_HT_OPERATION;
+ *pos++ = sizeof(struct ieee80211_ht_operation);
+ ht_oper = (struct ieee80211_ht_operation *)pos;
+ ht_oper->primary_chan = ieee80211_frequency_to_channel(
+ rwnx_vif->sta.ap->center_freq);
+ switch (rwnx_vif->sta.ap->width) {
+ case NL80211_CHAN_WIDTH_160:
+ case NL80211_CHAN_WIDTH_80P80:
+ case NL80211_CHAN_WIDTH_80:
+ case NL80211_CHAN_WIDTH_40:
+ if (rwnx_vif->sta.ap->center_freq1 > rwnx_vif->sta.ap->center_freq)
+ ht_oper->ht_param = IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
+ else
+ ht_oper->ht_param = IEEE80211_HT_PARAM_CHA_SEC_BELOW;
+ break;
+ default:
+ ht_oper->ht_param = IEEE80211_HT_PARAM_CHA_SEC_NONE;
+ break;
+ }
+ if (ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 &&
+ rwnx_vif->sta.ap->width != NL80211_CHAN_WIDTH_20_NOHT &&
+ rwnx_vif->sta.ap->width != NL80211_CHAN_WIDTH_20)
+ ht_oper->ht_param |= IEEE80211_HT_PARAM_CHAN_WIDTH_ANY;
+
+ ht_oper->operation_mode = cpu_to_le16(prot_mode);
+ ht_oper->stbc_param = 0x0000;
+
+ /* It seems that Basic MCS set and Supported MCS set
+ are identical for the first 10 bytes */
+ memset(&ht_oper->basic_set, 0, 16);
+ memcpy(&ht_oper->basic_set, &ht_cap->mcs, 10);
+
+ return pos + sizeof(struct ieee80211_ht_operation);
+}
+
+static u8 *
+rwnx_ie_build_vht_oper(struct rwnx_hw *rwnx_hw, struct rwnx_vif *rwnx_vif,
+ u8 *pos, struct ieee80211_sta_ht_cap *ht_cap,
+ u16 prot_mode)
+{
+ struct ieee80211_vht_operation *vht_oper;
+ /* Build HT Information */
+ *pos++ = WLAN_EID_VHT_OPERATION;
+ *pos++ = sizeof(struct ieee80211_vht_operation);
+ vht_oper = (struct ieee80211_vht_operation *)pos;
+
+ switch (rwnx_vif->sta.ap->width) {
+ case NL80211_CHAN_WIDTH_80:
+ vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_80MHZ; // Channel Width
+ CCFS0(vht_oper) =
+ ieee80211_frequency_to_channel(rwnx_vif->sta.ap->center_freq); // Channel Center Frequency Segment 0
+ CCFS1(vht_oper) = 0; // Channel Center Frequency Segment 1 (N.A.)
+ break;
+ case NL80211_CHAN_WIDTH_160:
+ vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_160MHZ; // Channel Width
+ CCFS0(vht_oper) =
+ ieee80211_frequency_to_channel(rwnx_vif->sta.ap->center_freq); // Channel Center Frequency Segment 0
+ CCFS1(vht_oper) = 0; // Channel Center Frequency Segment 1 (N.A.)
+ break;
+ case NL80211_CHAN_WIDTH_80P80:
+ vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_80P80MHZ; // Channel Width
+ CCFS0(vht_oper) =
+ ieee80211_frequency_to_channel(rwnx_vif->sta.ap->center_freq1); // Channel Center Frequency Segment 0
+ CCFS1(vht_oper) =
+ ieee80211_frequency_to_channel(rwnx_vif->sta.ap->center_freq2); // Channel Center Frequency Segment 1
+ break;
+ default:
+ vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_USE_HT;
+ CCFS0(vht_oper) = 0;
+ CCFS1(vht_oper) = 0;
+ break;
+ }
+
+ vht_oper->basic_mcs_set = cpu_to_le16(rwnx_hw->mod_params->mcs_map);
+
+ return pos + sizeof(struct ieee80211_vht_operation);
+
+}
+
+static void
+rwnx_tdls_add_setup_start_ies(struct rwnx_hw *rwnx_hw, struct rwnx_vif *rwnx_vif,
+ struct sk_buff *skb, const u8 *peer,
+ u8 action_code, bool initiator,
+ const u8 *extra_ies, size_t extra_ies_len)
+{
+ enum nl80211_band band = rwnx_vif->sta.ap->band;
+ struct ieee80211_supported_band *sband;
+ struct ieee80211_sta_ht_cap ht_cap;
+ struct ieee80211_sta_vht_cap vht_cap;
+ size_t offset = 0, noffset;
+ u8 *pos;
+
+ rcu_read_lock();
+
+ rwnx_add_srates_ie(rwnx_hw, skb);
+ rwnx_add_ext_srates_ie(rwnx_hw, skb);
+ rwnx_tdls_add_supp_channels(rwnx_hw, skb);
+ rwnx_tdls_add_ext_capab(rwnx_hw, skb);
+
+ /* add the QoS element if we support it */
+ if (/*local->hw.queues >= IEEE80211_NUM_ACS &&*/
+ action_code != WLAN_PUB_ACTION_TDLS_DISCOVER_RES)
+ rwnx_add_wmm_info_ie(skb, 0); /* no U-APSD */
+
+ rwnx_tdls_add_oper_classes(rwnx_vif, skb);
+
+ /*
+ * with TDLS we can switch channels, and HT-caps are not necessarily
+ * the same on all bands. The specification limits the setup to a
+ * single HT-cap, so use the current band for now.
+ */
+ sband = rwnx_hw->wiphy->bands[band];
+ memcpy(&ht_cap, &sband->ht_cap, sizeof(ht_cap));
+ if (((action_code == WLAN_TDLS_SETUP_REQUEST) ||
+ (action_code == WLAN_TDLS_SETUP_RESPONSE) ||
+ (action_code == WLAN_PUB_ACTION_TDLS_DISCOVER_RES)) &&
+ ht_cap.ht_supported /* (!sta || sta->sta.ht_cap.ht_supported)*/) {
+ rwnx_ie_build_ht_cap(skb, &ht_cap, ht_cap.cap);
+ }
+
+ if (ht_cap.ht_supported &&
+ (ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40))
+ rwnx_tdls_add_bss_coex_ie(skb);
+
+ rwnx_tdls_add_link_ie(rwnx_hw, rwnx_vif, skb, peer, initiator);
+
+ memcpy(&vht_cap, &sband->vht_cap, sizeof(vht_cap));
+ if (vht_cap.vht_supported) {
+ rwnx_tdls_add_aid_ie(rwnx_vif, skb);
+ rwnx_ie_build_vht_cap(skb, &vht_cap, vht_cap.cap);
+ // Operating mode Notification (optional)
+ }
+
+ /* add any remaining IEs */
+ if (extra_ies_len) {
+ noffset = extra_ies_len;
+ pos = skb_put(skb, noffset - offset);
+ memcpy(pos, extra_ies + offset, noffset - offset);
+ }
+
+ rcu_read_unlock();
+}
+
+
+static void
+rwnx_tdls_add_setup_cfm_ies(struct rwnx_hw *rwnx_hw, struct rwnx_vif *rwnx_vif,
+ struct sk_buff *skb, const u8 *peer, bool initiator,
+ const u8 *extra_ies, size_t extra_ies_len)
+{
+ struct ieee80211_supported_band *sband;
+ enum nl80211_band band = rwnx_vif->sta.ap->band;
+ struct ieee80211_sta_ht_cap ht_cap;
+ struct ieee80211_sta_vht_cap vht_cap;
+
+ size_t offset = 0, noffset;
+ struct rwnx_sta *sta, *ap_sta;
+ u8 *pos;
+
+ rcu_read_lock();
+
+ sta = rwnx_get_sta(rwnx_hw, peer);
+ ap_sta = rwnx_vif->sta.ap;
+ if (WARN_ON_ONCE(!sta || !ap_sta)) {
+ rcu_read_unlock();
+ return;
+ }
+
+ /* add the QoS param IE if both the peer and we support it */
+ if (sta->qos)
+ rwnx_add_wmm_param_ie(skb, ap_sta->acm, ap_sta->ac_param);
+
+ /* if HT support is only added in TDLS, we need an HT-operation IE */
+ sband = rwnx_hw->wiphy->bands[band];
+ memcpy(&ht_cap, &sband->ht_cap, sizeof(ht_cap));
+ if (ht_cap.ht_supported && !ap_sta->ht && sta->ht) {
+ pos = skb_put(skb, 2 + sizeof(struct ieee80211_ht_operation));
+ /* send an empty HT operation IE */
+ rwnx_ie_build_ht_oper(rwnx_hw, rwnx_vif, pos, &ht_cap, 0);
+ }
+
+ rwnx_tdls_add_link_ie(rwnx_hw, rwnx_vif, skb, peer, initiator);
+
+ memcpy(&vht_cap, &sband->vht_cap, sizeof(vht_cap));
+ if (vht_cap.vht_supported && !ap_sta->vht && sta->vht) {
+ pos = skb_put(skb, 2 + sizeof(struct ieee80211_vht_operation));
+ rwnx_ie_build_vht_oper(rwnx_hw, rwnx_vif, pos, &ht_cap, 0);
+ // Operating mode Notification (optional)
+ }
+
+ /* add any remaining IEs */
+ if (extra_ies_len) {
+ noffset = extra_ies_len;
+ pos = skb_put(skb, noffset - offset);
+ memcpy(pos, extra_ies + offset, noffset - offset);
+ }
+
+ rcu_read_unlock();
+}
+
+static void
+rwnx_tdls_add_ies(struct rwnx_hw *rwnx_hw, struct rwnx_vif *rwnx_vif,
+ struct sk_buff *skb, const u8 *peer,
+ u8 action_code, u16 status_code,
+ bool initiator, const u8 *extra_ies,
+ size_t extra_ies_len, u8 oper_class,
+ struct cfg80211_chan_def *chandef)
+{
+ switch (action_code) {
+ case WLAN_TDLS_SETUP_REQUEST:
+ case WLAN_TDLS_SETUP_RESPONSE:
+ case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
+ if (status_code == 0)
+ rwnx_tdls_add_setup_start_ies(rwnx_hw, rwnx_vif, skb, peer, action_code,
+ initiator, extra_ies, extra_ies_len);
+ break;
+ case WLAN_TDLS_SETUP_CONFIRM:
+ if (status_code == 0)
+ rwnx_tdls_add_setup_cfm_ies(rwnx_hw, rwnx_vif, skb, peer, initiator,
+ extra_ies, extra_ies_len);
+ break;
+
+ case WLAN_TDLS_TEARDOWN:
+ case WLAN_TDLS_DISCOVERY_REQUEST:
+ if (extra_ies_len)
+ memcpy(skb_put(skb, extra_ies_len), extra_ies,
+ extra_ies_len);
+ if (status_code == 0 || action_code == WLAN_TDLS_TEARDOWN)
+ rwnx_tdls_add_link_ie(rwnx_hw, rwnx_vif, skb, peer, initiator);
+ break;
+ }
+}
+
+int
+rwnx_tdls_send_mgmt_packet_data(struct rwnx_hw *rwnx_hw, struct rwnx_vif *rwnx_vif,
+ const u8 *peer, u8 action_code, u8 dialog_token,
+ u16 status_code, u32 peer_capability, bool initiator,
+ const u8 *extra_ies, size_t extra_ies_len, u8 oper_class,
+ struct cfg80211_chan_def *chandef)
+{
+ struct sk_buff *skb;
+ int ret = 0;
+ struct ieee80211_supported_band *rwnx_band_2GHz = rwnx_hw->wiphy->bands[NL80211_BAND_2GHZ];
+ //#ifdef USE_5G
+ struct ieee80211_supported_band *rwnx_band_5GHz = rwnx_hw->wiphy->bands[NL80211_BAND_5GHZ];
+ //#endif
+ int channels = rwnx_band_2GHz->n_channels;
+
+ if (rwnx_hw->band_5g_support){
+ channels += rwnx_band_5GHz->n_channels;
+ }
+
+ skb = netdev_alloc_skb(rwnx_vif->ndev,
+ sizeof(struct ieee80211_tdls_data) + // ethhdr + TDLS info
+ 10 + /* supported rates */
+ 6 + /* extended supported rates */
+ (2 + channels) + /* supported channels */
+ //#ifdef USE_5G
+ //(2 + rwnx_band_2GHz->n_channels + rwnx_band_5GHz->n_channels) + /* supported channels */
+ //#else
+ //(2 + rwnx_band_2GHz->n_channels) + /* supported channels */
+ //#endif
+ sizeof(struct ieee_types_extcap) +
+ sizeof(struct ieee80211_wmm_param_ie) +
+ 4 + /* oper classes */
+ 28 + //sizeof(struct ieee80211_ht_cap) +
+ sizeof(struct ieee_types_bss_co_2040) +
+ sizeof(struct ieee80211_tdls_lnkie) +
+ (2 + sizeof(struct ieee80211_vht_cap)) +
+ 4 + /*AID*/
+ (2 + sizeof(struct ieee80211_ht_operation)) +
+ extra_ies_len);
+
+ if (!skb)
+ return 0;
+
+ switch (action_code) {
+ case WLAN_TDLS_SETUP_REQUEST:
+ case WLAN_TDLS_SETUP_RESPONSE:
+ case WLAN_TDLS_SETUP_CONFIRM:
+ case WLAN_TDLS_TEARDOWN:
+ case WLAN_TDLS_DISCOVERY_REQUEST:
+ ret = rwnx_tdls_prepare_encap_data(rwnx_hw, rwnx_vif, peer, action_code,
+ dialog_token, status_code, skb);
+ break;
+
+ case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
+ ret = rwnx_prep_tdls_direct(rwnx_hw, rwnx_vif, peer, action_code,
+ dialog_token, status_code, skb);
+ break;
+
+ default:
+ ret = -ENOTSUPP;
+ break;
+ }
+
+ if (ret < 0)
+ goto fail;
+
+ rwnx_tdls_add_ies(rwnx_hw, rwnx_vif, skb, peer, action_code, status_code,
+ initiator, extra_ies, extra_ies_len, oper_class, chandef);
+
+ if (action_code == WLAN_PUB_ACTION_TDLS_DISCOVER_RES) {
+ u64 cookie;
+ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0))
+ struct cfg80211_mgmt_tx_params params;
+
+ params.len = skb->len;
+ params.buf = skb->data;
+ ret = rwnx_start_mgmt_xmit(rwnx_vif, NULL, &params, false, &cookie);
+ #else
+ ret = rwnx_start_mgmt_xmit(rwnx_vif, NULL, NULL, false, 0, skb->data, skb->len, false, false, &cookie);
+ #endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) */
+
+ return ret;
+ }
+
+ switch (action_code) {
+ case WLAN_TDLS_SETUP_REQUEST:
+ case WLAN_TDLS_SETUP_RESPONSE:
+ case WLAN_TDLS_SETUP_CONFIRM:
+ skb->priority = 2;
+ break;
+ default:
+ skb->priority = 5;
+ break;
+ }
+
+ ret = rwnx_select_txq(rwnx_vif, skb);
+ ret = rwnx_start_xmit(skb, rwnx_vif->ndev);
+
+ return ret;
+
+fail:
+ dev_kfree_skb(skb);
+ return ret;
+}
diff --git a/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_tdls.h b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_tdls.h
new file mode 100644
index 000000000000..9e4aa90202c9
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_tdls.h
@@ -0,0 +1,54 @@
+/**
+ ******************************************************************************
+ *
+ * @file rwnx_tdls.h
+ *
+ * @brief TDLS function declarations
+ *
+ * Copyright (C) RivieraWaves 2012-2019
+ *
+ ******************************************************************************
+ */
+
+#ifndef RWNX_TDLS_H_
+#define RWNX_TDLS_H_
+
+#include "rwnx_defs.h"
+
+struct ieee_types_header {
+ u8 element_id;
+ u8 len;
+} __packed;
+
+struct ieee_types_bss_co_2040 {
+ struct ieee_types_header ieee_hdr;
+ u8 bss_2040co;
+} __packed;
+
+struct ieee_types_extcap {
+ struct ieee_types_header ieee_hdr;
+ u8 ext_capab[8];
+} __packed;
+
+struct ieee_types_vht_cap {
+ struct ieee_types_header ieee_hdr;
+ struct ieee80211_vht_cap vhtcap;
+} __packed;
+
+struct ieee_types_vht_oper {
+ struct ieee_types_header ieee_hdr;
+ struct ieee80211_vht_operation vhtoper;
+} __packed;
+
+struct ieee_types_aid {
+ struct ieee_types_header ieee_hdr;
+ u16 aid;
+} __packed;
+
+int rwnx_tdls_send_mgmt_packet_data(struct rwnx_hw *rwnx_hw, struct rwnx_vif *rwnx_vif,
+ const u8 *peer, u8 action_code, u8 dialog_token,
+ u16 status_code, u32 peer_capability, bool initiator,
+ const u8 *extra_ies, size_t extra_ies_len, u8 oper_class,
+ struct cfg80211_chan_def *chandef);
+
+#endif /* RWNX_TDLS_H_ */
diff --git a/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_testmode.c b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_testmode.c
new file mode 100644
index 000000000000..01d7fe09a3a1
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_testmode.c
@@ -0,0 +1,226 @@
+/**
+ ****************************************************************************************
+ *
+ * @file rwnx_testmode.c
+ *
+ * @brief Test mode function definitions
+ *
+ * Copyright (C) RivieraWaves 2012-2019
+ *
+ ****************************************************************************************
+ */
+
+#include <net/mac80211.h>
+#include <net/netlink.h>
+
+#include "rwnx_testmode.h"
+#include "rwnx_msg_tx.h"
+#include "rwnx_dini.h"
+#include "reg_access.h"
+
+/*
+ * This function handles the user application commands for register access.
+ *
+ * It retrieves command ID carried with RWNX_TM_ATTR_COMMAND and calls to the
+ * handlers respectively.
+ *
+ * If it's an unknown commdn ID, -ENOSYS is returned; or -ENOMSG if the
+ * mandatory fields(RWNX_TM_ATTR_REG_OFFSET,RWNX_TM_ATTR_REG_VALUE32)
+ * are missing; Otherwise 0 is replied indicating the success of the command execution.
+ *
+ * If RWNX_TM_ATTR_COMMAND is RWNX_TM_CMD_APP2DEV_REG_READ, the register read
+ * value is returned with RWNX_TM_ATTR_REG_VALUE32.
+ *
+ * @hw: ieee80211_hw object that represents the device
+ * @tb: general message fields from the user space
+ */
+int rwnx_testmode_reg(struct ieee80211_hw *hw, struct nlattr **tb)
+{
+ struct rwnx_hw *rwnx_hw = hw->priv;
+ u32 mem_addr, val32;
+ struct sk_buff *skb;
+ int status = 0;
+
+ /* First check if register address is there */
+ if (!tb[RWNX_TM_ATTR_REG_OFFSET]) {
+ printk("Error finding register offset\n");
+ return -ENOMSG;
+ }
+
+ mem_addr = nla_get_u32(tb[RWNX_TM_ATTR_REG_OFFSET]);
+
+ switch (nla_get_u32(tb[RWNX_TM_ATTR_COMMAND])) {
+ case RWNX_TM_CMD_APP2DEV_REG_READ:
+ {
+ struct dbg_mem_read_cfm mem_read_cfm;
+
+ /*** Send the command to the LMAC ***/
+ if ((status = rwnx_send_dbg_mem_read_req(rwnx_hw, mem_addr, &mem_read_cfm)))
+ return status;
+
+ /* Allocate the answer message */
+ skb = cfg80211_testmode_alloc_reply_skb(hw->wiphy, 20);
+ if (!skb) {
+ printk("Error allocating memory\n");
+ return -ENOMEM;
+ }
+
+ val32 = mem_read_cfm.memdata;
+ if (nla_put_u32(skb, RWNX_TM_ATTR_REG_VALUE32, val32))
+ goto nla_put_failure;
+
+ /* Send the answer to upper layer */
+ status = cfg80211_testmode_reply(skb);
+ if (status < 0)
+ printk("Error sending msg : %d\n", status);
+ }
+ break;
+
+ case RWNX_TM_CMD_APP2DEV_REG_WRITE:
+ {
+ if (!tb[RWNX_TM_ATTR_REG_VALUE32]) {
+ printk("Error finding value to write\n");
+ return -ENOMSG;
+ } else {
+ val32 = nla_get_u32(tb[RWNX_TM_ATTR_REG_VALUE32]);
+ /* Send the command to the LMAC */
+ if ((status = rwnx_send_dbg_mem_write_req(rwnx_hw, mem_addr, val32)))
+ return status;
+ }
+ }
+ break;
+
+ default:
+ printk("Unknown testmode register command ID\n");
+ return -ENOSYS;
+ }
+
+ return status;
+
+nla_put_failure:
+ kfree_skb(skb);
+ return -EMSGSIZE;
+}
+
+/*
+ * This function handles the user application commands for Debug filter settings.
+ *
+ * @hw: ieee80211_hw object that represents the device
+ * @tb: general message fields from the user space
+ */
+int rwnx_testmode_dbg_filter(struct ieee80211_hw *hw, struct nlattr **tb)
+{
+ struct rwnx_hw *rwnx_hw = hw->priv;
+ u32 filter;
+ int status = 0;
+
+ /* First check if the filter is there */
+ if (!tb[RWNX_TM_ATTR_REG_FILTER]) {
+ printk("Error finding filter value\n");
+ return -ENOMSG;
+ }
+
+ filter = nla_get_u32(tb[RWNX_TM_ATTR_REG_FILTER]);
+ RWNX_DBG("testmode debug filter, setting: 0x%x\n", filter);
+
+ switch (nla_get_u32(tb[RWNX_TM_ATTR_COMMAND])) {
+ case RWNX_TM_CMD_APP2DEV_SET_DBGMODFILTER:
+ {
+ /* Send the command to the LMAC */
+ if ((status = rwnx_send_dbg_set_mod_filter_req(rwnx_hw, filter)))
+ return status;
+ }
+ break;
+ case RWNX_TM_CMD_APP2DEV_SET_DBGSEVFILTER:
+ {
+ /* Send the command to the LMAC */
+ if ((status = rwnx_send_dbg_set_sev_filter_req(rwnx_hw, filter)))
+ return status;
+ }
+ break;
+
+ default:
+ printk("Unknown testmode register command ID\n");
+ return -ENOSYS;
+ }
+
+ return status;
+}
+
+/*
+ * This function handles the user application commands for register access without using
+ * the normal LMAC messaging way.
+ * This time register access will be done through direct PCI BAR windows. This can be used
+ * to access registers even when the :AMC FW is stuck.
+ *
+ * @hw: ieee80211_hw object that represents the device
+ * @tb: general message fields from the user space
+ */
+int rwnx_testmode_reg_dbg(struct ieee80211_hw *hw, struct nlattr **tb)
+{
+ struct rwnx_hw *rwnx_hw = hw->priv;
+ struct rwnx_plat *rwnx_plat = rwnx_hw->plat;
+ u32 mem_addr;
+ struct sk_buff *skb;
+ int status = 0;
+ volatile unsigned int reg_value = 0;
+ unsigned int offset;
+
+ /* First check if register address is there */
+ if (!tb[RWNX_TM_ATTR_REG_OFFSET]) {
+ printk("Error finding register offset\n");
+ return -ENOMSG;
+ }
+
+ mem_addr = nla_get_u32(tb[RWNX_TM_ATTR_REG_OFFSET]);
+ offset = mem_addr & 0x00FFFFFF;
+
+ switch (nla_get_u32(tb[RWNX_TM_ATTR_COMMAND])) {
+ case RWNX_TM_CMD_APP2DEV_REG_READ_DBG:
+ {
+ /*** Send the command to the LMAC ***/
+ reg_value = RWNX_REG_READ(rwnx_plat, RWNX_ADDR_SYSTEM, offset);
+
+ /* Allocate the answer message */
+ skb = cfg80211_testmode_alloc_reply_skb(hw->wiphy, 20);
+ if (!skb) {
+ printk("Error allocating memory\n");
+ return -ENOMEM;
+ }
+
+ if (nla_put_u32(skb, RWNX_TM_ATTR_REG_VALUE32, reg_value))
+ goto nla_put_failure;
+
+ /* Send the answer to upper layer */
+ status = cfg80211_testmode_reply(skb);
+ if (status < 0)
+ printk("Error sending msg : %d\n", status);
+ }
+ break;
+
+ case RWNX_TM_CMD_APP2DEV_REG_WRITE_DBG:
+ {
+ if (!tb[RWNX_TM_ATTR_REG_VALUE32]) {
+ printk("Error finding value to write\n");
+ return -ENOMSG;
+ } else {
+ reg_value = nla_get_u32(tb[RWNX_TM_ATTR_REG_VALUE32]);
+
+ /* Send the command to the LMAC */
+ RWNX_REG_WRITE(reg_value, rwnx_plat, RWNX_ADDR_SYSTEM,
+ offset);
+ }
+ }
+ break;
+
+ default:
+ printk("Unknown testmode register command ID\n");
+ return -ENOSYS;
+ }
+
+ return status;
+
+nla_put_failure:
+ kfree_skb(skb);
+ return -EMSGSIZE;
+}
diff --git a/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_testmode.h b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_testmode.h
new file mode 100644
index 000000000000..0115e0f4a460
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_testmode.h
@@ -0,0 +1,64 @@
+/**
+ ****************************************************************************************
+ *
+ * @file rwnx_testmode.h
+ *
+ * @brief Test mode function declarations
+ *
+ * Copyright (C) RivieraWaves 2012-2019
+ *
+ ****************************************************************************************
+ */
+
+#ifndef RWNX_TESTMODE_H_
+#define RWNX_TESTMODE_H_
+
+#include <net/mac80211.h>
+#include <net/netlink.h>
+
+/* Commands from user space to kernel space(RWNX_TM_CMD_APP2DEV_XX) and
+ * from and kernel space to user space(RWNX_TM_CMD_DEV2APP_XX).
+ * The command ID is carried with RWNX_TM_ATTR_COMMAND.
+ */
+enum rwnx_tm_cmd_t {
+ /* commands from user application to access register */
+ RWNX_TM_CMD_APP2DEV_REG_READ = 1,
+ RWNX_TM_CMD_APP2DEV_REG_WRITE,
+
+ /* commands from user application to select the Debug levels */
+ RWNX_TM_CMD_APP2DEV_SET_DBGMODFILTER,
+ RWNX_TM_CMD_APP2DEV_SET_DBGSEVFILTER,
+
+ /* commands to access registers without sending messages to LMAC layer,
+ * this must be used when LMAC FW is stuck. */
+ RWNX_TM_CMD_APP2DEV_REG_READ_DBG,
+ RWNX_TM_CMD_APP2DEV_REG_WRITE_DBG,
+
+ RWNX_TM_CMD_MAX,
+};
+
+enum rwnx_tm_attr_t {
+ RWNX_TM_ATTR_NOT_APPLICABLE = 0,
+
+ RWNX_TM_ATTR_COMMAND,
+
+ /* When RWNX_TM_ATTR_COMMAND is RWNX_TM_CMD_APP2DEV_REG_XXX,
+ * The mandatory fields are:
+ * RWNX_TM_ATTR_REG_OFFSET for the offset of the target register;
+ * RWNX_TM_ATTR_REG_VALUE32 for value */
+ RWNX_TM_ATTR_REG_OFFSET,
+ RWNX_TM_ATTR_REG_VALUE32,
+
+ /* When RWNX_TM_ATTR_COMMAND is RWNX_TM_CMD_APP2DEV_SET_DBGXXXFILTER,
+ * The mandatory field is RWNX_TM_ATTR_REG_FILTER. */
+ RWNX_TM_ATTR_REG_FILTER,
+
+ RWNX_TM_ATTR_MAX,
+};
+
+/***********************************************************************/
+int rwnx_testmode_reg(struct ieee80211_hw *hw, struct nlattr **tb);
+int rwnx_testmode_dbg_filter(struct ieee80211_hw *hw, struct nlattr **tb);
+int rwnx_testmode_reg_dbg(struct ieee80211_hw *hw, struct nlattr **tb);
+
+#endif /* RWNX_TESTMODE_H_ */
diff --git a/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_tx.c b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_tx.c
new file mode 100644
index 000000000000..5e3ac1e83184
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_tx.c
@@ -0,0 +1,2143 @@
+/**
+ ******************************************************************************
+ *
+ * @file rwnx_tx.c
+ *
+ * Copyright (C) RivieraWaves 2012-2019
+ *
+ ******************************************************************************
+ */
+#include <linux/dma-mapping.h>
+#include <linux/etherdevice.h>
+#include <net/sock.h>
+
+#include "rwnx_defs.h"
+#include "rwnx_tx.h"
+#include "rwnx_msg_tx.h"
+#include "rwnx_mesh.h"
+#include "rwnx_events.h"
+#include "rwnx_compat.h"
+#include "aicwf_txrxif.h"
+#ifdef CONFIG_RWNX_MON_XMIT
+#include <net/ieee80211_radiotap.h>
+#endif
+
+/******************************************************************************
+ * Power Save functions
+ *****************************************************************************/
+/**
+ * rwnx_set_traffic_status - Inform FW if traffic is available for STA in PS
+ *
+ * @rwnx_hw: Driver main data
+ * @sta: Sta in PS mode
+ * @available: whether traffic is buffered for the STA
+ * @ps_id: type of PS data requested (@LEGACY_PS_ID or @UAPSD_ID)
+ */
+void rwnx_set_traffic_status(struct rwnx_hw *rwnx_hw,
+ struct rwnx_sta *sta,
+ bool available,
+ u8 ps_id)
+{
+ if (sta->tdls.active) {
+ rwnx_send_tdls_peer_traffic_ind_req(rwnx_hw,
+ rwnx_hw->vif_table[sta->vif_idx]);
+ } else {
+ bool uapsd = (ps_id != LEGACY_PS_ID);
+ rwnx_send_me_traffic_ind(rwnx_hw, sta->sta_idx, uapsd, available);
+#ifdef CREATE_TRACE_POINTS
+ trace_ps_traffic_update(sta->sta_idx, available, uapsd);
+#endif
+ }
+}
+
+/**
+ * rwnx_ps_bh_enable - Enable/disable PS mode for one STA
+ *
+ * @rwnx_hw: Driver main data
+ * @sta: Sta which enters/leaves PS mode
+ * @enable: PS mode status
+ *
+ * This function will enable/disable PS mode for one STA.
+ * When enabling PS mode:
+ * - Stop all STA's txq for RWNX_TXQ_STOP_STA_PS reason
+ * - Count how many buffers are already ready for this STA
+ * - For BC/MC sta, update all queued SKB to use hw_queue BCMC
+ * - Update TIM if some packet are ready
+ *
+ * When disabling PS mode:
+ * - Start all STA's txq for RWNX_TXQ_STOP_STA_PS reason
+ * - For BC/MC sta, update all queued SKB to use hw_queue AC_BE
+ * - Update TIM if some packet are ready (otherwise fw will not update TIM
+ * in beacon for this STA)
+ *
+ * All counter/skb updates are protected from TX path by taking tx_lock
+ *
+ * NOTE: _bh_ in function name indicates that this function is called
+ * from a bottom_half tasklet.
+ */
+void rwnx_ps_bh_enable(struct rwnx_hw *rwnx_hw, struct rwnx_sta *sta,
+ bool enable)
+{
+ struct rwnx_txq *txq;
+
+ if (enable) {
+#ifdef CREATE_TRACE_POINTS
+ trace_ps_enable(sta);
+#endif
+ spin_lock_bh(&rwnx_hw->tx_lock);
+ sta->ps.active = true;
+ sta->ps.sp_cnt[LEGACY_PS_ID] = 0;
+ sta->ps.sp_cnt[UAPSD_ID] = 0;
+ rwnx_txq_sta_stop(sta, RWNX_TXQ_STOP_STA_PS, rwnx_hw);
+
+ if (is_multicast_sta(sta->sta_idx)) {
+ txq = rwnx_txq_sta_get(sta, 0, rwnx_hw);
+ sta->ps.pkt_ready[LEGACY_PS_ID] = skb_queue_len(&txq->sk_list);
+ sta->ps.pkt_ready[UAPSD_ID] = 0;
+ //txq->hwq = &rwnx_hw->hwq[RWNX_HWQ_BCMC];
+ } else {
+ int i;
+ sta->ps.pkt_ready[LEGACY_PS_ID] = 0;
+ sta->ps.pkt_ready[UAPSD_ID] = 0;
+ foreach_sta_txq(sta, txq, i, rwnx_hw) {
+ sta->ps.pkt_ready[txq->ps_id] += skb_queue_len(&txq->sk_list);
+ }
+ }
+
+ spin_unlock_bh(&rwnx_hw->tx_lock);
+
+ //if (sta->ps.pkt_ready[LEGACY_PS_ID])
+ // rwnx_set_traffic_status(rwnx_hw, sta, true, LEGACY_PS_ID);
+
+ //if (sta->ps.pkt_ready[UAPSD_ID])
+ // rwnx_set_traffic_status(rwnx_hw, sta, true, UAPSD_ID);
+ } else {
+#ifdef CREATE_TRACE_POINTS
+ trace_ps_disable(sta->sta_idx);
+#endif
+ spin_lock_bh(&rwnx_hw->tx_lock);
+ sta->ps.active = false;
+
+ if (is_multicast_sta(sta->sta_idx)) {
+ txq = rwnx_txq_sta_get(sta, 0, rwnx_hw);
+ txq->hwq = &rwnx_hw->hwq[RWNX_HWQ_BE];
+ txq->push_limit = 0;
+ } else {
+ int i;
+ foreach_sta_txq(sta, txq, i, rwnx_hw) {
+ txq->push_limit = 0;
+ }
+ }
+
+ rwnx_txq_sta_start(sta, RWNX_TXQ_STOP_STA_PS, rwnx_hw);
+ spin_unlock_bh(&rwnx_hw->tx_lock);
+
+ //if (sta->ps.pkt_ready[LEGACY_PS_ID])
+ // rwnx_set_traffic_status(rwnx_hw, sta, false, LEGACY_PS_ID);
+
+ //if (sta->ps.pkt_ready[UAPSD_ID])
+ // rwnx_set_traffic_status(rwnx_hw, sta, false, UAPSD_ID);
+
+ tasklet_schedule(&rwnx_hw->task);
+ }
+}
+
+/**
+ * rwnx_ps_bh_traffic_req - Handle traffic request for STA in PS mode
+ *
+ * @rwnx_hw: Driver main data
+ * @sta: Sta which enters/leaves PS mode
+ * @pkt_req: number of pkt to push
+ * @ps_id: type of PS data requested (@LEGACY_PS_ID or @UAPSD_ID)
+ *
+ * This function will make sure that @pkt_req are pushed to fw
+ * whereas the STA is in PS mode.
+ * If request is 0, send all traffic
+ * If request is greater than available pkt, reduce request
+ * Note: request will also be reduce if txq credits are not available
+ *
+ * All counter updates are protected from TX path by taking tx_lock
+ *
+ * NOTE: _bh_ in function name indicates that this function is called
+ * from the bottom_half tasklet.
+ */
+void rwnx_ps_bh_traffic_req(struct rwnx_hw *rwnx_hw, struct rwnx_sta *sta,
+ u16 pkt_req, u8 ps_id)
+{
+ int pkt_ready_all;
+ struct rwnx_txq *txq;
+ int schedule = 0;
+
+ //if (WARN(!sta->ps.active, "sta %pM is not in Power Save mode",
+ // sta->mac_addr))
+ // return;
+ if(!sta->ps.active) {
+ //AICWFDBG(LOGERROR, "sta(%d) %pM is not in Power Save mode", sta->sta_idx, sta->mac_addr);
+ return;
+ }
+#ifdef CREATE_TRACE_POINTS
+ trace_ps_traffic_req(sta, pkt_req, ps_id);
+#endif
+ spin_lock_bh(&rwnx_hw->tx_lock);
+
+ /* Fw may ask to stop a service period with PS_SP_INTERRUPTED. This only
+ happens for p2p-go interface if NOA starts during a service period */
+ if ((pkt_req == PS_SP_INTERRUPTED) && (ps_id == UAPSD_ID)) {
+ int tid;
+ sta->ps.sp_cnt[ps_id] = 0;
+ foreach_sta_txq(sta, txq, tid, rwnx_hw) {
+ txq->push_limit = 0;
+ }
+ goto done;
+ }
+
+ pkt_ready_all = (sta->ps.pkt_ready[ps_id] - sta->ps.sp_cnt[ps_id]);
+
+ /* Don't start SP until previous one is finished or we don't have
+ packet ready (which must not happen for U-APSD) */
+ if (sta->ps.sp_cnt[ps_id] || pkt_ready_all <= 0) {
+ goto done;
+ }
+
+ /* Adapt request to what is available. */
+ if (pkt_req == 0 || pkt_req > pkt_ready_all) {
+ pkt_req = pkt_ready_all;
+ }
+
+ /* Reset the SP counter */
+ sta->ps.sp_cnt[ps_id] = 0;
+ schedule = 1;
+
+ /* "dispatch" the request between txq */
+ if (is_multicast_sta(sta->sta_idx)) {
+ txq = rwnx_txq_sta_get(sta, 0, rwnx_hw);
+ //if (txq->credits <= 0)
+ // goto done;
+ if (pkt_req > txq->credits)
+ pkt_req = txq->credits;
+ txq->push_limit = pkt_req;
+ sta->ps.sp_cnt[ps_id] = pkt_req;
+ rwnx_txq_add_to_hw_list(txq);
+ } else {
+ int i, tid;
+
+ foreach_sta_txq_prio(sta, txq, tid, i, rwnx_hw) {
+ u16 txq_len = skb_queue_len(&txq->sk_list);
+
+ if (txq->ps_id != ps_id)
+ continue;
+
+ if (txq_len > txq->credits)
+ txq_len = txq->credits;
+
+ if (txq_len == 0)
+ continue;
+
+ if (txq_len < pkt_req) {
+ /* Not enough pkt queued in this txq, add this
+ txq to hwq list and process next txq */
+ pkt_req -= txq_len;
+ txq->push_limit = txq_len;
+ sta->ps.sp_cnt[ps_id] += txq_len;
+ rwnx_txq_add_to_hw_list(txq);
+ } else {
+ /* Enough pkt in this txq to comlete the request
+ add this txq to hwq list and stop processing txq */
+ txq->push_limit = pkt_req;
+ sta->ps.sp_cnt[ps_id] += pkt_req;
+ rwnx_txq_add_to_hw_list(txq);
+ break;
+ }
+ }
+ }
+
+ done:
+ spin_unlock_bh(&rwnx_hw->tx_lock);
+ if(schedule)
+ tasklet_schedule(&rwnx_hw->task);
+}
+
+/******************************************************************************
+ * TX functions
+ *****************************************************************************/
+#define PRIO_STA_NULL 0xAA
+
+static const int rwnx_down_hwq2tid[3] = {
+ [RWNX_HWQ_BK] = 2,
+ [RWNX_HWQ_BE] = 3,
+ [RWNX_HWQ_VI] = 5,
+};
+
+static void rwnx_downgrade_ac(struct rwnx_sta *sta, struct sk_buff *skb)
+{
+ int8_t ac = rwnx_tid2hwq[skb->priority];
+
+ if (WARN((ac > RWNX_HWQ_VO),
+ "Unexepcted ac %d for skb before downgrade", ac))
+ ac = RWNX_HWQ_VO;
+
+ while (sta->acm & BIT(ac)) {
+ if (ac == RWNX_HWQ_BK) {
+ skb->priority = 1;
+ return;
+ }
+ ac--;
+ skb->priority = rwnx_down_hwq2tid[ac];
+ }
+}
+
+u16 rwnx_select_txq(struct rwnx_vif *rwnx_vif, struct sk_buff *skb)
+{
+ struct rwnx_hw *rwnx_hw = rwnx_vif->rwnx_hw;
+ struct wireless_dev *wdev = &rwnx_vif->wdev;
+ struct rwnx_sta *sta = NULL;
+ struct rwnx_txq *txq;
+ u16 netdev_queue;
+ bool tdls_mgmgt_frame = false;
+ int nx_bcmc_txq_ndev_idx = NX_BCMC_TXQ_NDEV_IDX;
+
+ if((g_rwnx_plat->usbdev->chipid == PRODUCT_ID_AIC8801) ||
+ ((g_rwnx_plat->usbdev->chipid == PRODUCT_ID_AIC8800DC ||
+ g_rwnx_plat->usbdev->chipid == PRODUCT_ID_AIC8800DW) && chip_id < 3)){
+ nx_bcmc_txq_ndev_idx = NX_BCMC_TXQ_NDEV_IDX_FOR_OLD_IC;
+ }
+
+ switch (wdev->iftype) {
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_P2P_CLIENT:
+ {
+ struct ethhdr *eth;
+ eth = (struct ethhdr *)skb->data;
+ if (eth->h_proto == cpu_to_be16(ETH_P_TDLS)) {
+ tdls_mgmgt_frame = true;
+ }
+ if ((rwnx_vif->tdls_status == TDLS_LINK_ACTIVE) &&
+ (rwnx_vif->sta.tdls_sta != NULL) &&
+ (memcmp(eth->h_dest, rwnx_vif->sta.tdls_sta->mac_addr, ETH_ALEN) == 0))
+ sta = rwnx_vif->sta.tdls_sta;
+ else
+ sta = rwnx_vif->sta.ap;
+ break;
+ }
+ case NL80211_IFTYPE_AP_VLAN:
+ if (rwnx_vif->ap_vlan.sta_4a) {
+ sta = rwnx_vif->ap_vlan.sta_4a;
+ break;
+ }
+
+ /* AP_VLAN interface is not used for a 4A STA,
+ fallback searching sta amongs all AP's clients */
+ rwnx_vif = rwnx_vif->ap_vlan.master;
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_P2P_GO:
+ {
+ struct rwnx_sta *cur;
+ struct ethhdr *eth = (struct ethhdr *)skb->data;
+
+ if (is_multicast_ether_addr(eth->h_dest)) {
+ sta = &rwnx_hw->sta_table[rwnx_vif->ap.bcmc_index];
+ } else {
+ spin_lock_bh(&rwnx_vif->rwnx_hw->cb_lock);
+ list_for_each_entry(cur, &rwnx_vif->ap.sta_list, list) {
+ if (!memcmp(cur->mac_addr, eth->h_dest, ETH_ALEN)) {
+ sta = cur;
+ break;
+ }
+ }
+ spin_unlock_bh(&rwnx_vif->rwnx_hw->cb_lock);
+ }
+
+ break;
+ }
+ case NL80211_IFTYPE_MESH_POINT:
+ {
+ struct ethhdr *eth = (struct ethhdr *)skb->data;
+
+ if (!rwnx_vif->is_resending) {
+ /*
+ * If ethernet source address is not the address of a mesh wireless interface, we are proxy for
+ * this address and have to inform the HW
+ */
+ if (memcmp(&eth->h_source[0], &rwnx_vif->ndev->perm_addr[0], ETH_ALEN)) {
+ /* Check if LMAC is already informed */
+ if (!rwnx_get_mesh_proxy_info(rwnx_vif, (u8 *)&eth->h_source, true)) {
+ rwnx_send_mesh_proxy_add_req(rwnx_hw, rwnx_vif, (u8 *)&eth->h_source);
+ }
+ }
+ }
+
+ if (is_multicast_ether_addr(eth->h_dest)) {
+ sta = &rwnx_hw->sta_table[rwnx_vif->ap.bcmc_index];
+ } else {
+ /* Path to be used */
+ struct rwnx_mesh_path *p_mesh_path = NULL;
+ struct rwnx_mesh_path *p_cur_path;
+ /* Check if destination is proxied by a peer Mesh STA */
+ struct rwnx_mesh_proxy *p_mesh_proxy = rwnx_get_mesh_proxy_info(rwnx_vif, (u8 *)&eth->h_dest, false);
+ /* Mesh Target address */
+ struct mac_addr *p_tgt_mac_addr;
+
+ if (p_mesh_proxy) {
+ p_tgt_mac_addr = &p_mesh_proxy->proxy_addr;
+ } else {
+ p_tgt_mac_addr = (struct mac_addr *)&eth->h_dest;
+ }
+
+ /* Look for path with provided target address */
+ list_for_each_entry(p_cur_path, &rwnx_vif->ap.mpath_list, list) {
+ if (!memcmp(&p_cur_path->tgt_mac_addr, p_tgt_mac_addr, ETH_ALEN)) {
+ p_mesh_path = p_cur_path;
+ break;
+ }
+ }
+
+ if (p_mesh_path) {
+ sta = p_mesh_path->p_nhop_sta;
+ } else {
+ rwnx_send_mesh_path_create_req(rwnx_hw, rwnx_vif, (u8 *)p_tgt_mac_addr);
+ }
+ }
+
+ break;
+ }
+ default:
+ break;
+ }
+
+ if (sta && sta->qos)
+ {
+ if (tdls_mgmgt_frame) {
+ skb_set_queue_mapping(skb, NX_STA_NDEV_IDX(skb->priority, sta->sta_idx));
+ } else {
+ /* use the data classifier to determine what 802.1d tag the
+ * data frame has */
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 14, 0)
+ skb->priority = cfg80211_classify8021d(skb) & IEEE80211_QOS_CTL_TAG1D_MASK;
+ #else
+ skb->priority = cfg80211_classify8021d(skb, NULL) & IEEE80211_QOS_CTL_TAG1D_MASK;
+ #endif
+ }
+ if (sta->acm)
+ rwnx_downgrade_ac(sta, skb);
+
+ txq = rwnx_txq_sta_get(sta, skb->priority, rwnx_hw);
+ netdev_queue = txq->ndev_idx;
+ }
+ else if (sta)
+ {
+ skb->priority = 0xFF;
+ txq = rwnx_txq_sta_get(sta, 0, rwnx_hw);
+ netdev_queue = txq->ndev_idx;
+ }
+ else
+ {
+ /* This packet will be dropped in xmit function, still need to select
+ an active queue for xmit to be called. As it most likely to happen
+ for AP interface, select BCMC queue
+ (TODO: select another queue if BCMC queue is stopped) */
+ skb->priority = PRIO_STA_NULL;
+ netdev_queue = nx_bcmc_txq_ndev_idx;
+ }
+
+#ifndef CONFIG_ONE_TXQ
+ BUG_ON(netdev_queue >= NX_NB_NDEV_TXQ);
+#endif
+
+ return netdev_queue;
+}
+
+/**
+ * rwnx_set_more_data_flag - Update MORE_DATA flag in tx sw desc
+ *
+ * @rwnx_hw: Driver main data
+ * @sw_txhdr: Header for pkt to be pushed
+ *
+ * If STA is in PS mode
+ * - Set EOSP in case the packet is the last of the UAPSD service period
+ * - Set MORE_DATA flag if more pkt are ready for this sta
+ * - Update TIM if this is the last pkt buffered for this sta
+ *
+ * note: tx_lock already taken.
+ */
+static inline void rwnx_set_more_data_flag(struct rwnx_hw *rwnx_hw,
+ struct rwnx_sw_txhdr *sw_txhdr)
+{
+ struct rwnx_sta *sta = sw_txhdr->rwnx_sta;
+ struct rwnx_vif *vif = sw_txhdr->rwnx_vif;
+ struct rwnx_txq *txq = sw_txhdr->txq;
+
+ if (unlikely(sta->ps.active)) {
+ sta->ps.pkt_ready[txq->ps_id]--;
+ sta->ps.sp_cnt[txq->ps_id]--;
+#ifdef CREATE_TRACE_POINTS
+ trace_ps_push(sta);
+#endif
+ if (((txq->ps_id == UAPSD_ID) || (vif->wdev.iftype == NL80211_IFTYPE_MESH_POINT) || (sta->tdls.active))
+ && !sta->ps.sp_cnt[txq->ps_id]) {
+ sw_txhdr->desc.host.flags |= TXU_CNTRL_EOSP;
+ }
+
+ if (sta->ps.pkt_ready[txq->ps_id]) {
+ sw_txhdr->desc.host.flags |= TXU_CNTRL_MORE_DATA;
+ } else {
+ rwnx_set_traffic_status(rwnx_hw, sta, false, txq->ps_id);
+ }
+ }
+}
+
+/**
+ * rwnx_get_tx_priv - Get STA and tid for one skb
+ *
+ * @rwnx_vif: vif ptr
+ * @skb: skb
+ * @tid: pointer updated with the tid to use for this skb
+ *
+ * @return: pointer on the destination STA (may be NULL)
+ *
+ * skb has already been parsed in rwnx_select_queue function
+ * simply re-read information form skb.
+ */
+static struct rwnx_sta *rwnx_get_tx_priv(struct rwnx_vif *rwnx_vif,
+ struct sk_buff *skb,
+ u8 *tid)
+{
+ struct rwnx_hw *rwnx_hw = rwnx_vif->rwnx_hw;
+ struct rwnx_sta *sta;
+ int sta_idx;
+ int nx_remote_sta_max = NX_REMOTE_STA_MAX;
+ int nx_bcmc_txq_ndev_idx = NX_BCMC_TXQ_NDEV_IDX;
+
+ if((g_rwnx_plat->usbdev->chipid == PRODUCT_ID_AIC8801) ||
+ ((g_rwnx_plat->usbdev->chipid == PRODUCT_ID_AIC8800DC ||
+ g_rwnx_plat->usbdev->chipid == PRODUCT_ID_AIC8800DW) && chip_id < 3)){
+ nx_remote_sta_max = NX_REMOTE_STA_MAX_FOR_OLD_IC;
+ nx_bcmc_txq_ndev_idx = NX_BCMC_TXQ_NDEV_IDX_FOR_OLD_IC;
+ }
+
+
+ *tid = skb->priority;
+ if (unlikely(skb->priority == PRIO_STA_NULL)) {
+ return NULL;
+ } else {
+ int ndev_idx = skb_get_queue_mapping(skb);
+
+ if (ndev_idx == nx_bcmc_txq_ndev_idx)
+ sta_idx = nx_remote_sta_max + master_vif_idx(rwnx_vif);
+ else
+ sta_idx = ndev_idx / NX_NB_TID_PER_STA;
+
+ sta = &rwnx_hw->sta_table[sta_idx];
+ }
+
+ return sta;
+}
+
+/**
+ * rwnx_prep_tx - Prepare buffer for DMA transmission
+ *
+ * @rwnx_hw: Driver main data
+ * @txhdr: Tx descriptor
+ *
+ * Maps hw_txhdr and buffer data for transmission via DMA.
+ * - Data buffer with be downloaded by embebded side.
+ * - hw_txhdr will be uploaded by embedded side when buffer has been
+ * transmitted over the air.
+ */
+static int rwnx_prep_tx(struct rwnx_hw *rwnx_hw, struct rwnx_txhdr *txhdr)
+{
+#if 0
+ struct rwnx_sw_txhdr *sw_txhdr = txhdr->sw_hdr;
+ struct rwnx_hw_txhdr *hw_txhdr = &txhdr->hw_hdr;
+ dma_addr_t dma_addr;
+
+ /* MAP (and sync) memory for DMA */
+ dma_addr = dma_map_single(rwnx_hw->dev, hw_txhdr,
+ sw_txhdr->map_len, DMA_BIDIRECTIONAL);
+ if (WARN_ON(dma_mapping_error(rwnx_hw->dev, dma_addr)))
+ return -1;
+
+ sw_txhdr->dma_addr = dma_addr;
+#endif
+ return 0;
+}
+
+/**
+ * rwnx_tx_push - Push one packet to fw
+ *
+ * @rwnx_hw: Driver main data
+ * @txhdr: tx desc of the buffer to push
+ * @flags: push flags (see @rwnx_push_flags)
+ *
+ * Push one packet to fw. Sw desc of the packet has already been updated.
+ * Only MORE_DATA flag will be set if needed.
+ */
+void rwnx_tx_push(struct rwnx_hw *rwnx_hw, struct rwnx_txhdr *txhdr, int flags)
+{
+ struct rwnx_sw_txhdr *sw_txhdr = txhdr->sw_hdr;
+ struct sk_buff *skb = sw_txhdr->skb;
+ struct rwnx_txq *txq = sw_txhdr->txq;
+ u16 hw_queue = txq->hwq->id;
+ int user = 0;
+
+ lockdep_assert_held(&rwnx_hw->tx_lock);
+
+ //printk("rwnx_tx_push\n");
+ /* RETRY flag is not always set so retest here */
+ if (txq->nb_retry) {
+ flags |= RWNX_PUSH_RETRY;
+ txq->nb_retry--;
+ if (txq->nb_retry == 0) {
+ WARN(skb != txq->last_retry_skb,
+ "last retry buffer is not the expected one");
+ txq->last_retry_skb = NULL;
+ }
+ } else if (!(flags & RWNX_PUSH_RETRY)) {
+ txq->pkt_sent++;
+ }
+
+#ifdef CONFIG_RWNX_AMSDUS_TX
+ if (txq->amsdu == sw_txhdr) {
+ WARN((flags & RWNX_PUSH_RETRY), "End A-MSDU on a retry");
+ rwnx_hw->stats.amsdus[sw_txhdr->amsdu.nb - 1].done++;
+ txq->amsdu = NULL;
+ } else if (!(flags & RWNX_PUSH_RETRY) &&
+ !(sw_txhdr->desc.host.flags & TXU_CNTRL_AMSDU)) {
+ rwnx_hw->stats.amsdus[0].done++;
+ }
+#endif /* CONFIG_RWNX_AMSDUS_TX */
+
+ /* Wait here to update hw_queue, as for multicast STA hwq may change
+ between queue and push (because of PS) */
+ sw_txhdr->hw_queue = hw_queue;
+
+ //sw_txhdr->desc.host.packet_addr = hw_queue; //use packet_addr field for hw_txq
+ sw_txhdr->desc.host.ac = hw_queue; //use ac field for hw_txq
+#ifdef CONFIG_RWNX_MUMIMO_TX
+ /* MU group is only selected during hwq processing */
+ sw_txhdr->desc.host.mumimo_info = txq->mumimo_info;
+ user = RWNX_TXQ_POS_ID(txq);
+#endif /* CONFIG_RWNX_MUMIMO_TX */
+
+ if (sw_txhdr->rwnx_sta) {
+ /* only for AP mode */
+ rwnx_set_more_data_flag(rwnx_hw, sw_txhdr);
+ }
+#ifdef CREATE_TRACE_POINTS
+ trace_push_desc(skb, sw_txhdr, flags);
+#endif
+ #if 0
+ txq->credits--;
+ #endif
+ txq->pkt_pushed[user]++;
+ //printk("txq->credits=%d\n",txq->credits);
+ #if 0
+ if (txq->credits <= 0)
+ rwnx_txq_stop(txq, RWNX_TXQ_STOP_FULL);
+ #endif
+
+ if (txq->push_limit)
+ txq->push_limit--;
+#if 0
+ rwnx_ipc_txdesc_push(rwnx_hw, &sw_txhdr->desc, skb, hw_queue, user);
+#else
+#ifdef AICWF_SDIO_SUPPORT
+ if( ((sw_txhdr->desc.host.flags & TXU_CNTRL_MGMT) && \
+ ((*(skb->data+sw_txhdr->headroom)==0xd0) || (*(skb->data+sw_txhdr->headroom)==0x10) || (*(skb->data+sw_txhdr->headroom)==0x30))) || \
+ (sw_txhdr->desc.host.ethertype == 0x8e88) ) { // 0xd0:Action, 0x10:AssocRsp, 0x8e88:EAPOL
+ sw_txhdr->need_cfm = 1;
+ sw_txhdr->desc.host.status_desc_addr = ((1<<31) | rwnx_hw->sdio_env.txdesc_free_idx[0]);
+ aicwf_sdio_host_txdesc_push(&(rwnx_hw->sdio_env), 0, (long)skb);
+ AICWFDBG(LOGINFO, "need cfm ethertype:%8x,user_idx=%d, skb=%p sta_idx:%d\n",
+ sw_txhdr->desc.host.ethertype,
+ rwnx_hw->sdio_env.txdesc_free_idx[0],
+ skb,
+ sw_txhdr->desc.host.staid);
+ } else {
+ sw_txhdr->need_cfm = 0;
+ if (sw_txhdr->raw_frame) {
+ sw_txhdr->desc.host.flags |= TXU_CNTRL_MGMT;
+ }
+ if (sw_txhdr->fixed_rate) {
+ sw_txhdr->desc.host.status_desc_addr = (0x01UL << 30) | sw_txhdr->rate_config;
+ } else {
+ sw_txhdr->desc.host.status_desc_addr = 0;
+ }
+
+ sw_txhdr->rwnx_vif->net_stats.tx_packets++;
+ sw_txhdr->rwnx_vif->net_stats.tx_bytes += sw_txhdr->frame_len;
+ rwnx_hw->stats.last_tx = jiffies;
+ }
+ aicwf_frame_tx((void *)(rwnx_hw->sdiodev), skb);
+#endif
+#ifdef AICWF_USB_SUPPORT
+ if( ((sw_txhdr->desc.host.flags & TXU_CNTRL_MGMT) && \
+ ((*(skb->data+sw_txhdr->headroom)==0xd0) || (*(skb->data+sw_txhdr->headroom)==0x10) || (*(skb->data+sw_txhdr->headroom)==0x30))) || \
+ (sw_txhdr->desc.host.ethertype == 0x8e88) || (sw_txhdr->desc.host.ethertype == 0xb488)) {
+ // 0xd0:Action, 0x10:AssocRsp, 0x8e88:EAPOL, 0xb488: WAPI
+ sw_txhdr->need_cfm = 1;
+ sw_txhdr->desc.host.status_desc_addr = ((1<<31) | rwnx_hw->usb_env.txdesc_free_idx[0]);
+ aicwf_usb_host_txdesc_push(&(rwnx_hw->usb_env), 0, (long)(skb));
+ AICWFDBG(LOGINFO, "need cfm ethertype:%8x,user_idx=%d, skb=%p sta_idx:%d\n",
+ sw_txhdr->desc.host.ethertype,
+ rwnx_hw->usb_env.txdesc_free_idx[0],
+ skb,
+ sw_txhdr->desc.host.staid);
+ } else {
+ sw_txhdr->need_cfm = 0;
+ if (sw_txhdr->raw_frame) {
+ sw_txhdr->desc.host.flags |= TXU_CNTRL_MGMT;
+ }
+ if (sw_txhdr->fixed_rate) {
+ sw_txhdr->desc.host.status_desc_addr = (0x01UL << 30) | sw_txhdr->rate_config;
+ } else {
+ sw_txhdr->desc.host.status_desc_addr = 0;
+ }
+
+ sw_txhdr->rwnx_vif->net_stats.tx_packets++;
+ sw_txhdr->rwnx_vif->net_stats.tx_bytes += sw_txhdr->frame_len;
+ rwnx_hw->stats.last_tx = jiffies;
+ }
+ aicwf_frame_tx((void *)(rwnx_hw->usbdev), skb);
+#endif
+#endif
+ #if 0
+ txq->hwq->credits[user]--;
+ #endif
+ rwnx_hw->stats.cfm_balance[hw_queue]++;
+}
+
+
+
+/**
+ * rwnx_tx_retry - Push an AMPDU pkt that need to be retried
+ *
+ * @rwnx_hw: Driver main data
+ * @skb: pkt to re-push
+ * @txhdr: tx desc of the pkt to re-push
+ * @sw_retry: Indicates if fw decide to retry this buffer
+ * (i.e. it has never been transmitted over the air)
+ *
+ * Called when a packet needs to be repushed to the firmware.
+ * First update sw descriptor and then queue it in the retry list.
+ */
+static void rwnx_tx_retry(struct rwnx_hw *rwnx_hw, struct sk_buff *skb,
+ struct rwnx_txhdr *txhdr, bool sw_retry)
+{
+ struct rwnx_sw_txhdr *sw_txhdr = txhdr->sw_hdr;
+ struct tx_cfm_tag *cfm = &txhdr->hw_hdr.cfm;
+ struct rwnx_txq *txq = sw_txhdr->txq;
+ int peek_off = offsetof(struct rwnx_hw_txhdr, cfm);
+ int peek_len = sizeof(((struct rwnx_hw_txhdr *)0)->cfm);
+
+ if (!sw_retry) {
+ /* update sw desc */
+ #if 0
+ sw_txhdr->desc.host.sn = cfm->sn;
+ sw_txhdr->desc.host.pn[0] = cfm->pn[0];
+ sw_txhdr->desc.host.pn[1] = cfm->pn[1];
+ sw_txhdr->desc.host.pn[2] = cfm->pn[2];
+ sw_txhdr->desc.host.pn[3] = cfm->pn[3];
+ sw_txhdr->desc.host.timestamp = cfm->timestamp;
+ #endif
+ sw_txhdr->desc.host.flags |= TXU_CNTRL_RETRY;
+
+ #ifdef CONFIG_RWNX_AMSDUS_TX
+ if (sw_txhdr->desc.host.flags & TXU_CNTRL_AMSDU)
+ rwnx_hw->stats.amsdus[sw_txhdr->amsdu.nb - 1].failed++;
+ #endif
+ }
+
+ /* MORE_DATA will be re-set if needed when pkt will be repushed */
+ sw_txhdr->desc.host.flags &= ~TXU_CNTRL_MORE_DATA;
+
+ cfm->status.value = 0;
+ dma_sync_single_for_device(rwnx_hw->dev, sw_txhdr->dma_addr + peek_off,
+ peek_len, DMA_BIDIRECTIONAL);
+
+ txq->credits++;
+ if (txq->credits > 0)
+ rwnx_txq_start(txq, RWNX_TXQ_STOP_FULL);
+
+ /* Queue the buffer */
+ rwnx_txq_queue_skb(skb, txq, rwnx_hw, true);
+}
+
+
+#ifdef CONFIG_RWNX_AMSDUS_TX
+/* return size of subframe (including header) */
+static inline int rwnx_amsdu_subframe_length(struct ethhdr *eth, int eth_len)
+{
+ /* ethernet header is replaced with amdsu header that have the same size
+ Only need to check if LLC/SNAP header will be added */
+ int len = eth_len;
+
+ if (ntohs(eth->h_proto) >= ETH_P_802_3_MIN) {
+ len += sizeof(rfc1042_header) + 2;
+ }
+
+ return len;
+}
+
+static inline bool rwnx_amsdu_is_aggregable(struct sk_buff *skb)
+{
+ /* need to add some check on buffer to see if it can be aggregated ? */
+ return true;
+}
+
+
+/**
+ * rwnx_amsdu_del_subframe_header - remove AMSDU header
+ *
+ * amsdu_txhdr: amsdu tx descriptor
+ *
+ * Move back the ethernet header at the "beginning" of the data buffer.
+ * (which has been moved in @rwnx_amsdu_add_subframe_header)
+ */
+static void rwnx_amsdu_del_subframe_header(struct rwnx_amsdu_txhdr *amsdu_txhdr)
+{
+ struct sk_buff *skb = amsdu_txhdr->skb;
+ struct ethhdr *eth;
+ u8 *pos;
+
+ pos = skb->data;
+ pos += sizeof(struct rwnx_amsdu_txhdr);
+ eth = (struct ethhdr*)pos;
+ pos += amsdu_txhdr->pad + sizeof(struct ethhdr);
+
+ if (ntohs(eth->h_proto) >= ETH_P_802_3_MIN) {
+ pos += sizeof(rfc1042_header) + 2;
+ }
+
+ memmove(pos, eth, sizeof(*eth));
+ skb_pull(skb, (pos - skb->data));
+}
+
+/**
+ * rwnx_amsdu_add_subframe_header - Add AMSDU header and link subframe
+ *
+ * @rwnx_hw Driver main data
+ * @skb Buffer to aggregate
+ * @sw_txhdr Tx descriptor for the first A-MSDU subframe
+ *
+ * return 0 on sucess, -1 otherwise
+ *
+ * This functions Add A-MSDU header and LLC/SNAP header in the buffer
+ * and update sw_txhdr of the first subframe to link this buffer.
+ * If an error happens, the buffer will be queued as a normal buffer.
+ *
+ *
+ * Before After
+ * +-------------+ +-------------+
+ * | HEADROOM | | HEADROOM |
+ * | | +-------------+ <- data
+ * | | | amsdu_txhdr |
+ * | | | * pad size |
+ * | | +-------------+
+ * | | | ETH hdr | keep original eth hdr
+ * | | | | to restore it once transmitted
+ * | | +-------------+ <- packet_addr[x]
+ * | | | Pad |
+ * | | +-------------+
+ * data -> +-------------+ | AMSDU HDR |
+ * | ETH hdr | +-------------+
+ * | | | LLC/SNAP |
+ * +-------------+ +-------------+
+ * | DATA | | DATA |
+ * | | | |
+ * +-------------+ +-------------+
+ *
+ * Called with tx_lock hold
+ */
+static int rwnx_amsdu_add_subframe_header(struct rwnx_hw *rwnx_hw,
+ struct sk_buff *skb,
+ struct rwnx_sw_txhdr *sw_txhdr)
+{
+ struct rwnx_amsdu *amsdu = &sw_txhdr->amsdu;
+ struct rwnx_amsdu_txhdr *amsdu_txhdr;
+ struct ethhdr *amsdu_hdr, *eth = (struct ethhdr *)skb->data;
+ int headroom_need, map_len, msdu_len;
+ dma_addr_t dma_addr;
+ u8 *pos, *map_start;
+
+ msdu_len = skb->len - sizeof(*eth);
+ headroom_need = sizeof(*amsdu_txhdr) + amsdu->pad +
+ sizeof(*amsdu_hdr);
+ if (ntohs(eth->h_proto) >= ETH_P_802_3_MIN) {
+ headroom_need += sizeof(rfc1042_header) + 2;
+ msdu_len += sizeof(rfc1042_header) + 2;
+ }
+
+ /* we should have enough headroom (checked in xmit) */
+ if (WARN_ON(skb_headroom(skb) < headroom_need)) {
+ return -1;
+ }
+
+ /* allocate headroom */
+ pos = skb_push(skb, headroom_need);
+ amsdu_txhdr = (struct rwnx_amsdu_txhdr *)pos;
+ pos += sizeof(*amsdu_txhdr);
+
+ /* move eth header */
+ memmove(pos, eth, sizeof(*eth));
+ eth = (struct ethhdr *)pos;
+ pos += sizeof(*eth);
+
+ /* Add padding from previous subframe */
+ map_start = pos;
+ memset(pos, 0, amsdu->pad);
+ pos += amsdu->pad;
+
+ /* Add AMSDU hdr */
+ amsdu_hdr = (struct ethhdr *)pos;
+ memcpy(amsdu_hdr->h_dest, eth->h_dest, ETH_ALEN);
+ memcpy(amsdu_hdr->h_source, eth->h_source, ETH_ALEN);
+ amsdu_hdr->h_proto = htons(msdu_len);
+ pos += sizeof(*amsdu_hdr);
+
+ if (ntohs(eth->h_proto) >= ETH_P_802_3_MIN) {
+ memcpy(pos, rfc1042_header, sizeof(rfc1042_header));
+ pos += sizeof(rfc1042_header);
+ }
+
+ /* MAP (and sync) memory for DMA */
+ map_len = msdu_len + amsdu->pad + sizeof(*amsdu_hdr);
+ dma_addr = dma_map_single(rwnx_hw->dev, map_start, map_len,
+ DMA_BIDIRECTIONAL);
+ if (WARN_ON(dma_mapping_error(rwnx_hw->dev, dma_addr))) {
+ pos -= sizeof(*eth);
+ memmove(pos, eth, sizeof(*eth));
+ skb_pull(skb, headroom_need);
+ return -1;
+ }
+
+ /* update amdsu_txhdr */
+ amsdu_txhdr->map_len = map_len;
+ amsdu_txhdr->dma_addr = dma_addr;
+ amsdu_txhdr->skb = skb;
+ amsdu_txhdr->pad = amsdu->pad;
+ amsdu_txhdr->msdu_len = msdu_len;
+
+ /* update rwnx_sw_txhdr (of the first subframe) */
+ BUG_ON(amsdu->nb != sw_txhdr->desc.host.packet_cnt);
+ sw_txhdr->desc.host.packet_addr[amsdu->nb] = dma_addr;
+ sw_txhdr->desc.host.packet_len[amsdu->nb] = map_len;
+ sw_txhdr->desc.host.packet_cnt++;
+ amsdu->nb++;
+
+ amsdu->pad = AMSDU_PADDING(map_len - amsdu->pad);
+ list_add_tail(&amsdu_txhdr->list, &amsdu->hdrs);
+ amsdu->len += map_len;
+
+ rwnx_ipc_sta_buffer(rwnx_hw, sw_txhdr->txq->sta,
+ sw_txhdr->txq->tid, msdu_len);
+
+ trace_amsdu_subframe(sw_txhdr);
+ return 0;
+}
+
+/**
+ * rwnx_amsdu_add_subframe - Add this buffer as an A-MSDU subframe if possible
+ *
+ * @rwnx_hw Driver main data
+ * @skb Buffer to aggregate if possible
+ * @sta Destination STA
+ * @txq sta's txq used for this buffer
+ *
+ * Tyr to aggregate the buffer in an A-MSDU. If it succeed then the
+ * buffer is added as a new A-MSDU subframe with AMSDU and LLC/SNAP
+ * headers added (so FW won't have to modify this subframe).
+ *
+ * To be added as subframe :
+ * - sta must allow amsdu
+ * - buffer must be aggregable (to be defined)
+ * - at least one other aggregable buffer is pending in the queue
+ * or an a-msdu (with enough free space) is currently in progress
+ *
+ * returns true if buffer has been added as A-MDSP subframe, false otherwise
+ *
+ */
+static bool rwnx_amsdu_add_subframe(struct rwnx_hw *rwnx_hw, struct sk_buff *skb,
+ struct rwnx_sta *sta, struct rwnx_txq *txq)
+{
+ bool res = false;
+ struct ethhdr *eth;
+
+ /* immediately return if amsdu are not allowed for this sta */
+ if (!txq->amsdu_len || rwnx_hw->mod_params->amsdu_maxnb < 2 ||
+ !rwnx_amsdu_is_aggregable(skb)
+ )
+ return false;
+
+ spin_lock_bh(&rwnx_hw->tx_lock);
+ if (txq->amsdu) {
+ /* aggreagation already in progress, add this buffer if enough space
+ available, otherwise end the current amsdu */
+ struct rwnx_sw_txhdr *sw_txhdr = txq->amsdu;
+ eth = (struct ethhdr *)(skb->data);
+
+ if (((sw_txhdr->amsdu.len + sw_txhdr->amsdu.pad +
+ rwnx_amsdu_subframe_length(eth, skb->len)) > txq->amsdu_len) ||
+ rwnx_amsdu_add_subframe_header(rwnx_hw, skb, sw_txhdr)) {
+ txq->amsdu = NULL;
+ goto end;
+ }
+
+ if (sw_txhdr->amsdu.nb >= rwnx_hw->mod_params->amsdu_maxnb) {
+ rwnx_hw->stats.amsdus[sw_txhdr->amsdu.nb - 1].done++;
+ /* max number of subframes reached */
+ txq->amsdu = NULL;
+ }
+ } else {
+ /* Check if a new amsdu can be started with the previous buffer
+ (if any) and this one */
+ struct sk_buff *skb_prev = skb_peek_tail(&txq->sk_list);
+ struct rwnx_txhdr *txhdr;
+ struct rwnx_sw_txhdr *sw_txhdr;
+ int len1, len2;
+
+ if (!skb_prev || !rwnx_amsdu_is_aggregable(skb_prev))
+ goto end;
+
+ txhdr = (struct rwnx_txhdr *)skb_prev->data;
+ sw_txhdr = txhdr->sw_hdr;
+ if ((sw_txhdr->amsdu.len) ||
+ (sw_txhdr->desc.host.flags & TXU_CNTRL_RETRY))
+ /* previous buffer is already a complete amsdu or a retry */
+ goto end;
+
+ eth = (struct ethhdr *)(skb_prev->data + sw_txhdr->headroom);
+ len1 = rwnx_amsdu_subframe_length(eth, (sw_txhdr->frame_len +
+ sizeof(struct ethhdr)));
+
+ eth = (struct ethhdr *)(skb->data);
+ len2 = rwnx_amsdu_subframe_length(eth, skb->len);
+
+ if (len1 + AMSDU_PADDING(len1) + len2 > txq->amsdu_len)
+ /* not enough space to aggregate those two buffers */
+ goto end;
+
+ /* Add subframe header.
+ Note: Fw will take care of adding AMDSU header for the first
+ subframe while generating 802.11 MAC header */
+ INIT_LIST_HEAD(&sw_txhdr->amsdu.hdrs);
+ sw_txhdr->amsdu.len = len1;
+ sw_txhdr->amsdu.nb = 1;
+ sw_txhdr->amsdu.pad = AMSDU_PADDING(len1);
+ if (rwnx_amsdu_add_subframe_header(rwnx_hw, skb, sw_txhdr))
+ goto end;
+
+ sw_txhdr->desc.host.flags |= TXU_CNTRL_AMSDU;
+
+ if (sw_txhdr->amsdu.nb < rwnx_hw->mod_params->amsdu_maxnb)
+ txq->amsdu = sw_txhdr;
+ else
+ rwnx_hw->stats.amsdus[sw_txhdr->amsdu.nb - 1].done++;
+ }
+
+ res = true;
+
+ end:
+ spin_unlock_bh(&rwnx_hw->tx_lock);
+ return res;
+}
+#endif /* CONFIG_RWNX_AMSDUS_TX */
+
+#ifdef CONFIG_BR_SUPPORT
+int aic_br_client_tx(struct rwnx_vif *vif, struct sk_buff **pskb)
+{
+ struct sk_buff *skb = *pskb;
+
+ /* if(check_fwstate(pmlmepriv, WIFI_STATION_STATE|WIFI_ADHOC_STATE) == _TRUE) */
+ {
+ void dhcp_flag_bcast(struct rwnx_vif *vif, struct sk_buff *skb);
+ int res, is_vlan_tag = 0, i, do_nat25 = 1;
+ unsigned short vlan_hdr = 0;
+ void *br_port = NULL;
+
+ /* mac_clone_handle_frame(priv, skb); */
+
+#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 35))
+ br_port = vif->ndev->br_port;
+#else /* (LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 35)) */
+ rcu_read_lock();
+ br_port = rcu_dereference(vif->ndev->rx_handler_data);
+ rcu_read_unlock();
+#endif /* (LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 35)) */
+#ifdef BR_SUPPORT_DEBUG
+ printk("SA=%pM, br_mac=%pM, type=0x%x, da[0]=%x, scdb=%pM, vif_type=%d\n", skb->data + MACADDRLEN, vif->br_mac, *((unsigned short *)(skb->data + MACADDRLEN * 2)),
+ skb->data[0], vif->scdb_mac,RWNX_VIF_TYPE(vif));
+#endif
+ spin_lock_bh(&vif->br_ext_lock);
+ if (!(skb->data[0] & 1) &&
+ br_port &&
+ memcmp(skb->data + MACADDRLEN, vif->br_mac, MACADDRLEN) &&
+ *((unsigned short *)(skb->data + MACADDRLEN * 2)) != __constant_htons(ETH_P_8021Q) &&
+ *((unsigned short *)(skb->data + MACADDRLEN * 2)) == __constant_htons(ETH_P_IP) &&
+ !memcmp(vif->scdb_mac, skb->data + MACADDRLEN, MACADDRLEN) && vif->scdb_entry) {
+ memcpy(skb->data + MACADDRLEN, vif->ndev->dev_addr, MACADDRLEN);
+ vif->scdb_entry->ageing_timer = jiffies;
+ spin_unlock_bh(&vif->br_ext_lock);
+ } else
+ /* if (!priv->pmib->ethBrExtInfo.nat25_disable) */
+ {
+ /* if (priv->dev->br_port &&
+ * !memcmp(skb->data+MACADDRLEN, priv->br_mac, MACADDRLEN)) { */
+#if 1
+ if (*((unsigned short *)(skb->data + MACADDRLEN * 2)) == __constant_htons(ETH_P_8021Q)) {
+ is_vlan_tag = 1;
+ vlan_hdr = *((unsigned short *)(skb->data + MACADDRLEN * 2 + 2));
+ for (i = 0; i < 6; i++)
+ *((unsigned short *)(skb->data + MACADDRLEN * 2 + 2 - i * 2)) = *((unsigned short *)(skb->data + MACADDRLEN * 2 - 2 - i * 2));
+ skb_pull(skb, 4);
+ }
+ /* if SA == br_mac && skb== IP => copy SIP to br_ip ?? why */
+ if (!memcmp(skb->data + MACADDRLEN, vif->br_mac, MACADDRLEN) &&
+ (*((unsigned short *)(skb->data + MACADDRLEN * 2)) == __constant_htons(ETH_P_IP)))
+ memcpy(vif->br_ip, skb->data + WLAN_ETHHDR_LEN + 12, 4);
+
+ if (*((unsigned short *)(skb->data + MACADDRLEN * 2)) == __constant_htons(ETH_P_IP)) {
+ if (memcmp(vif->scdb_mac, skb->data + MACADDRLEN, MACADDRLEN)) {
+ #if 1
+ void *scdb_findEntry(struct rwnx_vif *vif, unsigned char *macAddr, unsigned char *ipAddr);
+
+ vif->scdb_entry = (struct nat25_network_db_entry *)scdb_findEntry(vif,
+ skb->data + MACADDRLEN, skb->data + WLAN_ETHHDR_LEN + 12);
+ if (vif->scdb_entry != NULL) {
+ memcpy(vif->scdb_mac, skb->data + MACADDRLEN, MACADDRLEN);
+ memcpy(vif->scdb_ip, skb->data + WLAN_ETHHDR_LEN + 12, 4);
+ vif->scdb_entry->ageing_timer = jiffies;
+ do_nat25 = 0;
+ }
+ #endif
+ } else {
+ if (vif->scdb_entry) {
+ vif->scdb_entry->ageing_timer = jiffies;
+ do_nat25 = 0;
+ } else {
+ memset(vif->scdb_mac, 0, MACADDRLEN);
+ memset(vif->scdb_ip, 0, 4);
+ }
+ }
+ }
+ spin_unlock_bh(&vif->br_ext_lock);
+#endif /* 1 */
+ if (do_nat25) {
+ #if 1
+ int nat25_db_handle(struct rwnx_vif *vif, struct sk_buff *skb, int method);
+ if (nat25_db_handle(vif, skb, NAT25_CHECK) == 0) {
+ struct sk_buff *newskb;
+
+ if (is_vlan_tag) {
+ skb_push(skb, 4);
+ for (i = 0; i < 6; i++)
+ *((unsigned short *)(skb->data + i * 2)) = *((unsigned short *)(skb->data + 4 + i * 2));
+ *((unsigned short *)(skb->data + MACADDRLEN * 2)) = __constant_htons(ETH_P_8021Q);
+ *((unsigned short *)(skb->data + MACADDRLEN * 2 + 2)) = vlan_hdr;
+ }
+
+ newskb = skb_copy(skb, in_interrupt() ? GFP_ATOMIC : GFP_KERNEL);
+ if (newskb == NULL) {
+ /* priv->ext_stats.tx_drops++; */
+ printk("TX DROP: skb_copy fail!\n");
+ /* goto stop_proc; */
+ return -1;
+ }
+ dev_kfree_skb_any(skb);
+
+ *pskb = skb = newskb;
+ if (is_vlan_tag) {
+ vlan_hdr = *((unsigned short *)(skb->data + MACADDRLEN * 2 + 2));
+ for (i = 0; i < 6; i++)
+ *((unsigned short *)(skb->data + MACADDRLEN * 2 + 2 - i * 2)) = *((unsigned short *)(skb->data + MACADDRLEN * 2 - 2 - i * 2));
+ skb_pull(skb, 4);
+ }
+ }
+
+ if (skb_is_nonlinear(skb))
+ printk("%s(): skb_is_nonlinear!!\n", __FUNCTION__);
+
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18))
+ res = skb_linearize(skb, GFP_ATOMIC);
+#else /* (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18)) */
+ res = skb_linearize(skb);
+#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18)) */
+ if (res < 0) {
+ printk("TX DROP: skb_linearize fail!\n");
+ /* goto free_and_stop; */
+ return -1;
+ }
+
+ res = nat25_db_handle(vif, skb, NAT25_INSERT);
+ if (res < 0) {
+ if (res == -2) {
+ /* priv->ext_stats.tx_drops++; */
+ printk("TX DROP: nat25_db_handle fail!\n");
+ /* goto free_and_stop; */
+ return -1;
+
+ }
+ /* we just print warning message and let it go */
+ /* DEBUG_WARN("%s()-%d: nat25_db_handle INSERT Warning!\n", __FUNCTION__, __LINE__); */
+ /* return -1; */ /* return -1 will cause system crash on 2011/08/30! */
+ return 0;
+ }
+ #endif
+ }
+
+ memcpy(skb->data + MACADDRLEN, vif->ndev->dev_addr, MACADDRLEN);
+
+ dhcp_flag_bcast(vif, skb);
+
+ if (is_vlan_tag) {
+ skb_push(skb, 4);
+ for (i = 0; i < 6; i++)
+ *((unsigned short *)(skb->data + i * 2)) = *((unsigned short *)(skb->data + 4 + i * 2));
+ *((unsigned short *)(skb->data + MACADDRLEN * 2)) = __constant_htons(ETH_P_8021Q);
+ *((unsigned short *)(skb->data + MACADDRLEN * 2 + 2)) = vlan_hdr;
+ }
+ }
+#if 0
+ else {
+ if (*((unsigned short *)(skb->data + MACADDRLEN * 2)) == __constant_htons(ETH_P_8021Q))
+ is_vlan_tag = 1;
+
+ if (is_vlan_tag) {
+ if (ICMPV6_MCAST_MAC(skb->data) && ICMPV6_PROTO1A_VALN(skb->data))
+ memcpy(skb->data + MACADDRLEN, GET_MY_HWADDR(padapter), MACADDRLEN);
+ } else {
+ if (ICMPV6_MCAST_MAC(skb->data) && ICMPV6_PROTO1A(skb->data))
+ memcpy(skb->data + MACADDRLEN, GET_MY_HWADDR(padapter), MACADDRLEN);
+ }
+ }
+#endif /* 0 */
+
+ /* check if SA is equal to our MAC */
+ if (memcmp(skb->data + MACADDRLEN, vif->ndev->dev_addr, MACADDRLEN)) {
+ /* priv->ext_stats.tx_drops++; */
+ printk("TX DROP: untransformed frame SA:%02X%02X%02X%02X%02X%02X!\n",
+ skb->data[6], skb->data[7], skb->data[8], skb->data[9], skb->data[10], skb->data[11]);
+ /* goto free_and_stop; */
+ return -1;
+ }
+ }
+ printk("%s:exit\n",__func__);
+ return 0;
+}
+#endif /* CONFIG_BR_SUPPORT */
+
+
+/**
+ * netdev_tx_t (*ndo_start_xmit)(struct sk_buff *skb,
+ * struct net_device *dev);
+ * Called when a packet needs to be transmitted.
+ * Must return NETDEV_TX_OK , NETDEV_TX_BUSY.
+ * (can also return NETDEV_TX_LOCKED if NETIF_F_LLTX)
+ *
+ * - Initialize the desciptor for this pkt (stored in skb before data)
+ * - Push the pkt in the corresponding Txq
+ * - If possible (i.e. credit available and not in PS) the pkt is pushed
+ * to fw
+ */
+netdev_tx_t rwnx_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct rwnx_vif *rwnx_vif = netdev_priv(dev);
+ struct rwnx_hw *rwnx_hw = rwnx_vif->rwnx_hw;
+ struct rwnx_txhdr *txhdr;
+ struct rwnx_sw_txhdr *sw_txhdr;
+ struct txdesc_api *desc;
+ struct rwnx_sta *sta;
+ struct rwnx_txq *txq;
+ int headroom;
+ int max_headroom;
+ int hdr_pads;
+
+ u16 frame_len;
+ u16 frame_oft;
+ u8 tid;
+
+ struct ethhdr eth_t;
+
+#ifdef CONFIG_ONE_TXQ
+ skb->queue_mapping = rwnx_select_txq(rwnx_vif, skb);
+#endif
+
+ memcpy(&eth_t, skb->data, sizeof(struct ethhdr));
+
+ sk_pacing_shift_update(skb->sk, rwnx_hw->tcp_pacing_shift);
+ max_headroom = sizeof(struct rwnx_txhdr);
+
+ /* check whether the current skb can be used */
+ if (skb_shared(skb) || (skb_headroom(skb) < max_headroom) ||
+ (skb_cloned(skb) && (dev->priv_flags & IFF_BRIDGE_PORT))) {
+ struct sk_buff *newskb = skb_copy_expand(skb, max_headroom, 0,
+ GFP_ATOMIC);
+ if (unlikely(newskb == NULL))
+ goto free;
+
+ dev_kfree_skb_any(skb);
+
+ skb = newskb;
+ }
+
+ /* Get the STA id and TID information */
+ sta = rwnx_get_tx_priv(rwnx_vif, skb, &tid);
+ if (!sta)
+ goto free;
+
+ txq = rwnx_txq_sta_get(sta, tid, rwnx_hw);
+ if (txq->idx == TXQ_INACTIVE)
+ goto free;
+
+#ifdef CONFIG_RWNX_AMSDUS_TX
+ if (rwnx_amsdu_add_subframe(rwnx_hw, skb, sta, txq))
+ return NETDEV_TX_OK;
+#endif
+
+#ifdef CONFIG_BR_SUPPORT
+ if (1) {//(check_fwstate(&padapter->mlmepriv, WIFI_STATION_STATE | WIFI_ADHOC_STATE) == _TRUE) {
+ void *br_port = NULL;
+
+ #if (LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 35))
+ br_port = rwnx_vif->ndev->br_port;
+ #else
+ rcu_read_lock();
+ br_port = rcu_dereference(rwnx_vif->ndev->rx_handler_data);
+ rcu_read_unlock();
+ #endif
+
+ if (br_port) {
+ s32 res = aic_br_client_tx(rwnx_vif, &skb);
+ if (res == -1) {
+ goto free;
+ }
+ }
+ }
+#endif /* CONFIG_BR_SUPPORT */
+
+ /* Retrieve the pointer to the Ethernet data */
+ // eth = (struct ethhdr *)skb->data;
+
+ skb_pull(skb, 14);
+ //hdr_pads = RWNX_SWTXHDR_ALIGN_PADS((long)eth);
+ hdr_pads = RWNX_SWTXHDR_ALIGN_PADS((long)skb->data);
+ headroom = sizeof(struct rwnx_txhdr) + hdr_pads;
+
+ skb_push(skb, headroom);
+
+ txhdr = (struct rwnx_txhdr *)skb->data;
+ sw_txhdr = kmem_cache_alloc(rwnx_hw->sw_txhdr_cache, GFP_ATOMIC);
+
+ if (unlikely(sw_txhdr == NULL))
+ goto free;
+ txhdr->sw_hdr = sw_txhdr;
+ desc = &sw_txhdr->desc;
+
+ frame_len = (u16)skb->len - headroom;// - sizeof(*eth);
+
+ sw_txhdr->txq = txq;
+ sw_txhdr->frame_len = frame_len;
+ sw_txhdr->rwnx_sta = sta;
+ sw_txhdr->rwnx_vif = rwnx_vif;
+ sw_txhdr->skb = skb;
+ sw_txhdr->headroom = headroom;
+ sw_txhdr->map_len = skb->len - offsetof(struct rwnx_txhdr, hw_hdr);
+
+#ifdef CONFIG_RWNX_AMSDUS_TX
+ sw_txhdr->amsdu.len = 0;
+ sw_txhdr->amsdu.nb = 0;
+#endif
+ sw_txhdr->raw_frame = 0;
+ sw_txhdr->fixed_rate = 0;
+ // Fill-in the descriptor
+ memcpy(&desc->host.eth_dest_addr, eth_t.h_dest, ETH_ALEN);
+ memcpy(&desc->host.eth_src_addr, eth_t.h_source, ETH_ALEN);
+ desc->host.ethertype = eth_t.h_proto;
+ desc->host.staid = sta->sta_idx;
+ desc->host.tid = tid;
+ if (unlikely(rwnx_vif->wdev.iftype == NL80211_IFTYPE_AP_VLAN))
+ desc->host.vif_idx = rwnx_vif->ap_vlan.master->vif_index;
+ else
+ desc->host.vif_idx = rwnx_vif->vif_index;
+
+ if (rwnx_vif->use_4addr && (sta->sta_idx < NX_REMOTE_STA_MAX))
+ desc->host.flags = TXU_CNTRL_USE_4ADDR;
+ else
+ desc->host.flags = 0;
+
+ if ((rwnx_vif->tdls_status == TDLS_LINK_ACTIVE) &&
+ rwnx_vif->sta.tdls_sta &&
+ (memcmp(desc->host.eth_dest_addr.array, rwnx_vif->sta.tdls_sta->mac_addr, ETH_ALEN) == 0)) {
+ desc->host.flags |= TXU_CNTRL_TDLS;
+ rwnx_vif->sta.tdls_sta->tdls.last_tid = desc->host.tid;
+ //rwnx_vif->sta.tdls_sta->tdls.last_sn = desc->host.sn;
+ }
+
+ if (rwnx_vif->wdev.iftype == NL80211_IFTYPE_MESH_POINT) {
+ if (rwnx_vif->is_resending) {
+ desc->host.flags |= TXU_CNTRL_MESH_FWD;
+ }
+ }
+
+#ifdef CONFIG_RWNX_SPLIT_TX_BUF
+ desc->host.packet_len[0] = frame_len;
+#else
+ desc->host.packet_len = frame_len;
+#endif
+
+ txhdr->hw_hdr.cfm.status.value = 0;
+
+ if (unlikely(rwnx_prep_tx(rwnx_hw, txhdr))) {
+ kmem_cache_free(rwnx_hw->sw_txhdr_cache, sw_txhdr);
+ skb_pull(skb, headroom);
+ dev_kfree_skb_any(skb);
+ return NETDEV_TX_BUSY;
+ }
+
+ /* Fill-in TX descriptor */
+ frame_oft = sizeof(struct rwnx_txhdr) - offsetof(struct rwnx_txhdr, hw_hdr)
+ + hdr_pads;// + sizeof(*eth);
+ #if 0
+#ifdef CONFIG_RWNX_SPLIT_TX_BUF
+ desc->host.packet_addr[0] = sw_txhdr->dma_addr + frame_oft;
+ desc->host.packet_cnt = 1;
+#else
+ desc->host.packet_addr = sw_txhdr->dma_addr + frame_oft;
+#endif
+#endif
+ desc->host.status_desc_addr = sw_txhdr->dma_addr;
+
+ spin_lock_bh(&rwnx_hw->tx_lock);
+ if (rwnx_txq_queue_skb(skb, txq, rwnx_hw, false))
+ rwnx_hwq_process(rwnx_hw, txq->hwq);
+ spin_unlock_bh(&rwnx_hw->tx_lock);
+
+ return NETDEV_TX_OK;
+
+free:
+ dev_kfree_skb_any(skb);
+
+ return NETDEV_TX_OK;
+}
+
+/**
+ * rwnx_start_mgmt_xmit - Transmit a management frame
+ *
+ * @vif: Vif that send the frame
+ * @sta: Destination of the frame. May be NULL if the destiantion is unknown
+ * to the AP.
+ * @params: Mgmt frame parameters
+ * @offchan: Indicate whether the frame must be send via the offchan TXQ.
+ * (is is redundant with params->offchan ?)
+ * @cookie: updated with a unique value to identify the frame with upper layer
+ *
+ */
+
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0))
+int rwnx_start_mgmt_xmit(struct rwnx_vif *vif, struct rwnx_sta *sta,
+ struct cfg80211_mgmt_tx_params *params, bool offchan,
+ u64 *cookie)
+#else
+int rwnx_start_mgmt_xmit(struct rwnx_vif *vif, struct rwnx_sta *sta,
+ struct ieee80211_channel *channel, bool offchan,
+ unsigned int wait, const u8* buf, size_t len,
+ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0))
+ bool no_cck,
+ #endif
+ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 3, 0))
+ bool dont_wait_for_ack,
+ #endif
+ u64 *cookie)
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) */
+{
+ struct rwnx_hw *rwnx_hw = vif->rwnx_hw;
+ struct rwnx_txhdr *txhdr;
+ struct rwnx_sw_txhdr *sw_txhdr;
+ struct txdesc_api *desc;
+ struct sk_buff *skb;
+ u16 frame_len, headroom, frame_oft;
+ u8 *data;
+ int nx_off_chan_txq_idx = NX_OFF_CHAN_TXQ_IDX;
+ struct rwnx_txq *txq;
+ bool robust;
+ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0))
+ const u8 *buf = params->buf;
+ size_t len = params->len;
+ bool no_cck = params->no_cck;
+ #endif
+ headroom = sizeof(struct rwnx_txhdr);
+ frame_len = len;
+
+ //----------------------------------------------------------------------
+
+ if((g_rwnx_plat->usbdev->chipid == PRODUCT_ID_AIC8801) ||
+ ((g_rwnx_plat->usbdev->chipid == PRODUCT_ID_AIC8800DC ||
+ g_rwnx_plat->usbdev->chipid == PRODUCT_ID_AIC8800DW) && chip_id < 3)){
+ nx_off_chan_txq_idx = NX_OFF_CHAN_TXQ_IDX_FOR_OLD_IC;
+ }
+
+ /* Set TID and Queues indexes */
+ if (sta) {
+ txq = rwnx_txq_sta_get(sta, 8, rwnx_hw);
+ } else {
+ if (offchan)
+ txq = &rwnx_hw->txq[nx_off_chan_txq_idx];
+ else
+ txq = rwnx_txq_vif_get(vif, NX_UNK_TXQ_TYPE);
+ }
+
+ /* Ensure that TXQ is active */
+ if (txq->idx == TXQ_INACTIVE) {
+ netdev_dbg(vif->ndev, "TXQ inactive\n");
+ return -EBUSY;
+ }
+
+ /*
+ * Create a SK Buff object that will contain the provided data
+ */
+ skb = dev_alloc_skb(headroom + frame_len);
+
+ if (!skb) {
+ return -ENOMEM;
+ }
+
+ *cookie = (unsigned long)skb;
+
+ /*
+ * Move skb->data pointer in order to reserve room for rwnx_txhdr
+ * headroom value will be equal to sizeof(struct rwnx_txhdr)
+ */
+ skb_reserve(skb, headroom);
+
+ /*
+ * Extend the buffer data area in order to contain the provided packet
+ * len value (for skb) will be equal to param->len
+ */
+ data = skb_put(skb, frame_len);
+ /* Copy the provided data */
+ memcpy(data, buf, frame_len);
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 15, 0))
+ robust = ieee80211_is_robust_mgmt_frame(skb);
+#else
+ if (skb->len < 25){
+ robust = false;
+ }
+ robust = ieee80211_is_robust_mgmt_frame((void *)skb->data);
+#endif
+
+ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0))
+ /* Update CSA counter if present */
+ if (unlikely(params->n_csa_offsets) &&
+ vif->wdev.iftype == NL80211_IFTYPE_AP &&
+ vif->ap.csa) {
+ int i;
+
+ data = skb->data;
+ for (i = 0; i < params->n_csa_offsets ; i++) {
+ data[params->csa_offsets[i]] = vif->ap.csa->count;
+ }
+ }
+ #endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) */
+
+ /*
+ * Go back to the beginning of the allocated data area
+ * skb->data pointer will move backward
+ */
+ skb_push(skb, headroom);
+
+ //----------------------------------------------------------------------
+
+ /* Fill the TX Header */
+ txhdr = (struct rwnx_txhdr *)skb->data;
+
+ txhdr->hw_hdr.cfm.status.value = 0;
+
+ //----------------------------------------------------------------------
+
+ /* Fill the SW TX Header */
+ sw_txhdr = kmem_cache_alloc(rwnx_hw->sw_txhdr_cache, GFP_ATOMIC);
+
+ if (unlikely(sw_txhdr == NULL)) {
+ dev_kfree_skb(skb);
+ return -ENOMEM;
+ }
+
+
+ txhdr->sw_hdr = sw_txhdr;
+
+ sw_txhdr->txq = txq;
+ sw_txhdr->frame_len = frame_len;
+ sw_txhdr->rwnx_sta = sta;
+ sw_txhdr->rwnx_vif = vif;
+ sw_txhdr->skb = skb;
+ sw_txhdr->headroom = headroom;
+ sw_txhdr->map_len = skb->len - offsetof(struct rwnx_txhdr, hw_hdr);
+#ifdef CONFIG_RWNX_AMSDUS_TX
+ sw_txhdr->amsdu.len = 0;
+ sw_txhdr->amsdu.nb = 0;
+#endif
+ sw_txhdr->raw_frame = 0;
+ sw_txhdr->fixed_rate = 0;
+ //----------------------------------------------------------------------
+
+ /* Fill the Descriptor to be provided to the MAC SW */
+ desc = &sw_txhdr->desc;
+
+ desc->host.ethertype = 0;
+ desc->host.staid = (sta) ? sta->sta_idx : 0xFF;
+ desc->host.vif_idx = vif->vif_index;
+ desc->host.tid = 0xFF;
+ desc->host.flags = TXU_CNTRL_MGMT;
+ if (robust)
+ desc->host.flags |= TXU_CNTRL_MGMT_ROBUST;
+
+#ifdef CONFIG_RWNX_SPLIT_TX_BUF
+ desc->host.packet_len[0] = frame_len;
+#else
+ desc->host.packet_len = frame_len;
+#endif
+
+ if (no_cck) {
+ desc->host.flags |= TXU_CNTRL_MGMT_NO_CCK;
+ }
+
+ /* Get DMA Address */
+ if (unlikely(rwnx_prep_tx(rwnx_hw, txhdr))) {
+ kmem_cache_free(rwnx_hw->sw_txhdr_cache, sw_txhdr);
+ dev_kfree_skb(skb);
+ return -EBUSY;
+ }
+
+ frame_oft = sizeof(struct rwnx_txhdr) - offsetof(struct rwnx_txhdr, hw_hdr);
+ #if 0
+#ifdef CONFIG_RWNX_SPLIT_TX_BUF
+ desc->host.packet_addr[0] = sw_txhdr->dma_addr + frame_oft;
+ desc->host.packet_cnt = 1;
+#else
+ desc->host.packet_addr = sw_txhdr->dma_addr + frame_oft;
+#endif
+ #endif
+ desc->host.status_desc_addr = sw_txhdr->dma_addr;
+
+ //----------------------------------------------------------------------
+
+ spin_lock_bh(&rwnx_hw->tx_lock);
+ AICWFDBG(LOGDEBUG, "%s sta:%p skb:%p desc->host.staid:%d \r\n", __func__, sta, skb, desc->host.staid);
+ if (rwnx_txq_queue_skb(skb, txq, rwnx_hw, false))
+ rwnx_hwq_process(rwnx_hw, txq->hwq);
+ spin_unlock_bh(&rwnx_hw->tx_lock);
+
+ return 0;
+}
+
+#ifdef CONFIG_RWNX_MON_XMIT
+/**
+ * netdev_tx_t (*ndo_start_xmit)(struct sk_buff *skb,
+ * struct net_device *dev);
+ * Called when a packet needs to be transmitted.
+ * Must return NETDEV_TX_OK , NETDEV_TX_BUSY.
+ * (can also return NETDEV_TX_LOCKED if NETIF_F_LLTX)
+ *
+ * - Initialize the desciptor for this pkt (stored in skb before data)
+ * - Push the pkt in the corresponding Txq
+ * - If possible (i.e. credit available and not in PS) the pkt is pushed
+ * to fw
+ */
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 11, 0))
+#define IEEE80211_RADIOTAP_MCS_HAVE_STBC 0x20
+#define IEEE80211_RADIOTAP_MCS_STBC_MASK 0x60
+#define IEEE80211_RADIOTAP_MCS_STBC_SHIFT 5
+#endif
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 15, 0))
+#define IEEE80211_RADIOTAP_CODING_LDPC_USER0 0x01
+#endif
+
+netdev_tx_t rwnx_start_monitor_if_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ int rtap_len, ret, idx, tmp_len;
+ struct ieee80211_radiotap_header *rtap_hdr; // net/ieee80211_radiotap.h
+ struct ieee80211_radiotap_iterator iterator; // net/cfg80211.h
+ u8_l *rtap_buf = (u8_l *)skb->data;
+ u8_l rate;
+
+ struct rwnx_vif *vif = netdev_priv(dev);
+ struct rwnx_hw *rwnx_hw = vif->rwnx_hw;
+ struct rwnx_txhdr *txhdr;
+ struct rwnx_sw_txhdr *sw_txhdr;
+ struct txdesc_api *desc;
+ struct rwnx_sta *sta;
+ struct rwnx_txq *txq;
+ u16_l frame_len, headroom, frame_oft;
+ u8_l tid, rate_fmt = FORMATMOD_NON_HT, rate_idx = 0, txsig_bw = PHY_CHNL_BW_20;
+ u8_l *pframe, *data;
+ bool robust;
+ struct sk_buff *skb_mgmt;
+ bool offchan = false;
+ int nx_off_chan_txq_idx = NX_OFF_CHAN_TXQ_IDX;
+
+ rtap_hdr = (struct ieee80211_radiotap_header*)(rtap_buf);
+ rtap_len = ieee80211_get_radiotap_len(rtap_buf);
+ frame_len = skb->len;
+
+ printk("rwnx_start_monitor_if_xmit, skb_len=%d, rtap_len=%d\n", skb->len, rtap_len);
+
+ if((g_rwnx_plat->usbdev->chipid == PRODUCT_ID_AIC8801) ||
+ ((g_rwnx_plat->usbdev->chipid == PRODUCT_ID_AIC8800DC ||
+ g_rwnx_plat->usbdev->chipid == PRODUCT_ID_AIC8800DW) && chip_id < 3)){
+ nx_off_chan_txq_idx = NX_OFF_CHAN_TXQ_IDX_FOR_OLD_IC;
+ }
+
+
+ if (unlikely(rtap_hdr->it_version))
+ goto free_tag;
+
+ if (unlikely(skb->len < rtap_len))
+ goto free_tag;
+
+ if (unlikely(rtap_len < sizeof(struct ieee80211_radiotap_header)))
+ goto free_tag;
+
+ frame_len -= rtap_len;
+ pframe = rtap_buf + rtap_len;
+
+ // Parse radiotap for injection items and overwrite attribs as needed
+ ret = ieee80211_radiotap_iterator_init(&iterator, rtap_hdr, rtap_len, NULL);
+ while (!ret) {
+ ret = ieee80211_radiotap_iterator_next(&iterator);
+ if (ret) {
+ continue;
+ }
+ switch (iterator.this_arg_index) {
+ case IEEE80211_RADIOTAP_RATE:
+ // This is basic 802.11b/g rate; use MCS/VHT for higher rates
+ rate = *iterator.this_arg;
+ printk("rate=0x%x\n", rate);
+ for (idx = 0; idx < HW_RATE_MAX; idx++) {
+ if ((rate * 5) == tx_legrates_lut_rate[idx]) {
+ break;
+ }
+ }
+ if (idx < HW_RATE_MAX) {
+ rate_idx = idx;
+ } else {
+ printk("invalid radiotap rate: %d\n", rate);
+ }
+ break;
+
+ case IEEE80211_RADIOTAP_TX_FLAGS: {
+ u16_l txflags = get_unaligned_le16(iterator.this_arg);
+ printk("txflags=0x%x\n", txflags);
+ if ((txflags & IEEE80211_RADIOTAP_F_TX_NOACK) == 0) {
+ printk(" TX_NOACK\n");
+ }
+ if (txflags & 0x0010) { // Use preconfigured seq num
+ // NOTE: this is currently ignored due to qos_en=_FALSE and HW seq num override
+ printk(" GetSequence\n");
+ }
+ }
+ break;
+
+ case IEEE80211_RADIOTAP_MCS: {
+ u8_l mcs_have = iterator.this_arg[0];
+ printk("mcs_have=0x%x\n", mcs_have);
+ rate_fmt = FORMATMOD_HT_MF;
+ if (mcs_have & IEEE80211_RADIOTAP_MCS_HAVE_BW) {
+ u8_l bw = (iterator.this_arg[1] & IEEE80211_RADIOTAP_MCS_BW_MASK);
+ u8_l ch_offset = 0;
+ if (bw == IEEE80211_RADIOTAP_MCS_BW_40) {
+ txsig_bw = PHY_CHNL_BW_40;
+ } else if (bw == IEEE80211_RADIOTAP_MCS_BW_20L) {
+ bw = IEEE80211_RADIOTAP_MCS_BW_20;
+ ch_offset = 1; // CHNL_OFFSET_LOWER;
+ } else if (bw == IEEE80211_RADIOTAP_MCS_BW_20U) {
+ bw = IEEE80211_RADIOTAP_MCS_BW_20;
+ ch_offset = 2; // CHNL_OFFSET_UPPER;
+ }
+ printk(" bw=%d, ch_offset=%d\n", bw, ch_offset);
+ }
+ if (mcs_have & IEEE80211_RADIOTAP_MCS_HAVE_MCS) {
+ u8_l fixed_rate = iterator.this_arg[2] & 0x7f;
+ if (fixed_rate > 31) {
+ fixed_rate = 0;
+ }
+ rate_idx = fixed_rate;
+ printk(" fixed_rate=0x%x\n", fixed_rate);
+ }
+ if ((mcs_have & IEEE80211_RADIOTAP_MCS_HAVE_GI) && (iterator.this_arg[1] & IEEE80211_RADIOTAP_MCS_SGI)) {
+ printk(" sgi\n");
+ }
+ if ((mcs_have & IEEE80211_RADIOTAP_MCS_HAVE_FEC) && (iterator.this_arg[1] & IEEE80211_RADIOTAP_MCS_FEC_LDPC)) {
+ printk(" ldpc\n");
+ }
+ if (mcs_have & IEEE80211_RADIOTAP_MCS_HAVE_STBC) {
+ u8 stbc = (iterator.this_arg[1] & IEEE80211_RADIOTAP_MCS_STBC_MASK) >> IEEE80211_RADIOTAP_MCS_STBC_SHIFT;
+ printk(" stbc=0x%x\n", stbc);
+ }
+ }
+ break;
+
+ case IEEE80211_RADIOTAP_VHT: {
+ unsigned int mcs, nss;
+ u8 known = iterator.this_arg[0];
+ u8 flags = iterator.this_arg[2];
+ rate_fmt = FORMATMOD_VHT;
+ printk("known=0x%x, flags=0x%x\n", known, flags);
+ // NOTE: this code currently only supports 1SS for radiotap defined rates
+ if ((known & IEEE80211_RADIOTAP_VHT_KNOWN_STBC) && (flags & IEEE80211_RADIOTAP_VHT_FLAG_STBC)) {
+ printk(" stbc\n");
+ }
+ if ((known & IEEE80211_RADIOTAP_VHT_KNOWN_GI) && (flags & IEEE80211_RADIOTAP_VHT_FLAG_SGI)) {
+ printk(" sgi\n");
+ }
+ if (known & IEEE80211_RADIOTAP_VHT_KNOWN_BANDWIDTH) {
+ u8_l bw = iterator.this_arg[3] & 0x1F;
+ printk(" bw=0x%x\n",bw);
+ // NOTE: there are various L and U, but we just use straight 20/40/80
+ // since it's not clear how to set CHNL_OFFSET_LOWER/_UPPER with different
+ // sideband sizes/configurations. TODO.
+ // Also, any 160 is treated as 80 due to lack of WIDTH_160.
+ txsig_bw = PHY_CHNL_BW_40;
+ if (bw == 0) {
+ txsig_bw = PHY_CHNL_BW_20;
+ printk(" 20M\n");
+ } else if (bw >=1 && bw <= 3) {
+ printk(" 40M\n");
+ } else if (bw >=4 && bw <= 10) {
+ printk(" 80M\n");
+ } else if (bw >= 11 && bw <= 25) {
+ printk(" 160M\n");
+ }
+ }
+ // User 0
+ nss = iterator.this_arg[4] & 0x0F; // Number of spatial streams
+ printk(" nss=0x%x\n", nss);
+ if (nss > 0) {
+ if (nss > 4) nss = 4;
+ mcs = (iterator.this_arg[4]>>4) & 0x0F; // MCS rate index
+ if (mcs > 8) mcs = 9;
+ rate_idx = mcs;
+ printk(" mcs=0x%x\n", mcs);
+ if (iterator.this_arg[8] & IEEE80211_RADIOTAP_CODING_LDPC_USER0) {
+ printk(" ldpc\n");
+ }
+ }
+ }
+ break;
+
+ case IEEE80211_RADIOTAP_HE: {
+ u16 data1 = ((u16)iterator.this_arg[1] << 8) | iterator.this_arg[0];
+ u16 data2 = ((u16)iterator.this_arg[3] << 8) | iterator.this_arg[2];
+ u16 data3 = ((u16)iterator.this_arg[5] << 8) | iterator.this_arg[4];
+ u16 data5 = ((u16)iterator.this_arg[9] << 8) | iterator.this_arg[8];
+ u8 fmt_he = data1 & IEEE80211_RADIOTAP_HE_DATA1_FORMAT_MASK;
+ if (fmt_he == IEEE80211_RADIOTAP_HE_DATA1_FORMAT_MU) {
+ rate_fmt = FORMATMOD_HE_MU;
+ } else if (fmt_he == IEEE80211_RADIOTAP_HE_DATA1_FORMAT_EXT_SU) {
+ rate_fmt = FORMATMOD_HE_ER;
+ } else {
+ rate_fmt = FORMATMOD_HE_SU;
+ }
+ if (data1 & IEEE80211_RADIOTAP_HE_DATA1_DATA_MCS_KNOWN) {
+ u8 mcs = (data3 & IEEE80211_RADIOTAP_HE_DATA3_DATA_MCS) >> 8;
+ if (mcs > 11) mcs = 11;
+ rate_idx = mcs;
+ }
+ if (data1 & IEEE80211_RADIOTAP_HE_DATA1_BW_RU_ALLOC_KNOWN) {
+ u8 bw = data5 & IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC;
+ txsig_bw = (bw == IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_20MHZ) ? PHY_CHNL_BW_20 : PHY_CHNL_BW_40;
+ }
+ if (data2 & IEEE80211_RADIOTAP_HE_DATA2_GI_KNOWN) {
+ u8 gi = (data5 & IEEE80211_RADIOTAP_HE_DATA5_GI) >> 4;
+ printk(" gi: %d\n", gi);
+ }
+ }
+ break;
+
+ default:
+ printk("unparsed arg: 0x%x\n",iterator.this_arg_index);
+ break;
+ }
+ }
+
+ #if 0
+ // dump buffer
+ tmp_len = 128;
+ if (skb->len < 128) {
+ tmp_len = skb->len;
+ }
+ for (idx = 0; idx < tmp_len; idx+=16) {
+ printk("[%04X] %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\n", idx,
+ rtap_buf[idx+0],rtap_buf[idx+1],rtap_buf[idx+2],rtap_buf[idx+3],
+ rtap_buf[idx+4],rtap_buf[idx+5],rtap_buf[idx+6],rtap_buf[idx+7],
+ rtap_buf[idx+8],rtap_buf[idx+9],rtap_buf[idx+10],rtap_buf[idx+11],
+ rtap_buf[idx+12],rtap_buf[idx+13],rtap_buf[idx+14],rtap_buf[idx+15]);
+ }
+ #endif
+
+ /* Get the STA id and TID information */
+ sta = rwnx_get_tx_priv(vif, skb, &tid);
+ //if (!sta) {
+ // printk("sta=null, tid=0x%x\n", tid);
+ //}
+ /* Set TID and Queues indexes */
+ if (sta) {
+ txq = rwnx_txq_sta_get(sta, 8, rwnx_hw);
+ } else {
+ if (offchan)
+ txq = &rwnx_hw->txq[nx_off_chan_txq_idx];
+ else
+ txq = rwnx_txq_vif_get(vif, NX_UNK_TXQ_TYPE);
+ }
+ if (txq->idx == TXQ_INACTIVE) {
+ printk("TXQ_INACTIVE\n");
+ goto free_tag;
+ }
+ // prepare to xmit
+ headroom = sizeof(struct rwnx_txhdr);
+ skb_mgmt = dev_alloc_skb(headroom + frame_len);
+ if (!skb_mgmt) {
+ printk("skb_mgmt alloc fail\n");
+ goto free_tag;
+ }
+ skb_reserve(skb_mgmt, headroom);
+ data = skb_put(skb_mgmt, frame_len);
+ /* Copy the provided data */
+ memcpy(data, pframe, frame_len);
+ robust = ieee80211_is_robust_mgmt_frame(
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 15, 0))
+ (void*)skb_mgmt
+#else
+ skb_mgmt
+#endif
+ );
+ skb_push(skb_mgmt, headroom);
+ /* Fill the TX Header */
+ txhdr = (struct rwnx_txhdr *)skb_mgmt->data;
+ txhdr->hw_hdr.cfm.status.value = 0;
+ /* Fill the SW TX Header */
+ sw_txhdr = kmem_cache_alloc(rwnx_hw->sw_txhdr_cache, GFP_ATOMIC);
+ if (unlikely(sw_txhdr == NULL)) {
+ dev_kfree_skb(skb_mgmt);
+ printk("sw_txhdr alloc fail\n");
+ goto free_tag;
+ }
+ txhdr->sw_hdr = sw_txhdr;
+ sw_txhdr->txq = txq;
+ sw_txhdr->frame_len = frame_len;
+ sw_txhdr->rwnx_sta = sta;
+ sw_txhdr->rwnx_vif = vif;
+ sw_txhdr->skb = skb_mgmt;
+ sw_txhdr->headroom = headroom;
+ sw_txhdr->map_len = skb_mgmt->len - offsetof(struct rwnx_txhdr, hw_hdr);
+ sw_txhdr->raw_frame = 1;
+ sw_txhdr->fixed_rate = 1;
+ sw_txhdr->rate_config = ((rate_fmt << FORMAT_MOD_TX_RCX_OFT) & FORMAT_MOD_TX_RCX_MASK) |
+ ((txsig_bw << BW_TX_RCX_OFT) & BW_TX_RCX_MASK) |
+ ((rate_idx << MCS_INDEX_TX_RCX_OFT) & MCS_INDEX_TX_RCX_MASK); // from radiotap
+ /* Fill the Descriptor to be provided to the MAC SW */
+ desc = &sw_txhdr->desc;
+ desc->host.staid = (sta) ? sta->sta_idx : 0xFF;
+ desc->host.vif_idx = vif->vif_index;
+ desc->host.tid = 0xFF;
+ desc->host.flags = TXU_CNTRL_MGMT;
+ if (robust) {
+ desc->host.flags |= TXU_CNTRL_MGMT_ROBUST;
+ }
+ frame_oft = sizeof(struct rwnx_txhdr) - offsetof(struct rwnx_txhdr, hw_hdr);
+ #if 0
+ #ifdef CONFIG_RWNX_SPLIT_TX_BUF
+ desc->host.packet_addr[0] = sw_txhdr->dma_addr + frame_oft;
+ desc->host.packet_len[0] = frame_len;
+ desc->host.packet_cnt = 1;
+ #else
+ desc->host.packet_addr = sw_txhdr->dma_addr + frame_oft;
+ desc->host.packet_len = frame_len;
+ #endif
+ #else
+ desc->host.packet_len = frame_len;
+ #endif
+
+ desc->host.status_desc_addr = sw_txhdr->dma_addr;
+
+ spin_lock_bh(&rwnx_hw->tx_lock);
+ if (rwnx_txq_queue_skb(skb_mgmt, txq, rwnx_hw, false))
+ rwnx_hwq_process(rwnx_hw, txq->hwq);
+ spin_unlock_bh(&rwnx_hw->tx_lock);
+
+free_tag:
+ dev_kfree_skb_any(skb);
+ return NETDEV_TX_OK;
+}
+#endif
+
+/**
+ * rwnx_txdatacfm - FW callback for TX confirmation
+ *
+ * called with tx_lock hold
+ */
+int rwnx_txdatacfm(void *pthis, void *host_id)
+{
+ struct rwnx_hw *rwnx_hw = (struct rwnx_hw *)pthis;
+ struct sk_buff *skb = host_id;
+ struct rwnx_txhdr *txhdr;
+ union rwnx_hw_txstatus rwnx_txst;
+ struct rwnx_sw_txhdr *sw_txhdr;
+ struct rwnx_hwq *hwq;
+ struct rwnx_txq *txq;
+ u16 headroom;
+ //int peek_off = offsetof(struct rwnx_hw_txhdr, cfm);
+ //int peek_len = sizeof(((struct rwnx_hw_txhdr *)0)->cfm);
+
+ txhdr = (struct rwnx_txhdr *)skb->data;
+ sw_txhdr = txhdr->sw_hdr;
+
+ /* Read status in the TX control header */
+ rwnx_txst = txhdr->hw_hdr.cfm.status;
+
+ /* Check status in the header. If status is null, it means that the buffer
+ * was not transmitted and we have to return immediately */
+ if (rwnx_txst.value == 0) {
+ return -1;
+ }
+
+#ifdef AICWF_USB_SUPPORT
+ if (rwnx_hw->usbdev->state == USB_DOWN_ST) {
+ headroom = sw_txhdr->headroom;
+ kmem_cache_free(rwnx_hw->sw_txhdr_cache, sw_txhdr);
+ skb_pull(skb, headroom);
+ consume_skb(skb);
+ return 0;
+ }
+#endif
+#ifdef AICWF_SDIO_SUPPORT
+ if(rwnx_hw->sdiodev->bus_if->state == BUS_DOWN_ST) {
+ headroom = sw_txhdr->headroom;
+ kmem_cache_free(rwnx_hw->sw_txhdr_cache, sw_txhdr);
+ skb_pull(skb, headroom);
+ consume_skb(skb);
+ return 0;
+ }
+#endif
+
+ txq = sw_txhdr->txq;
+ /* don't use txq->hwq as it may have changed between push and confirm */
+ hwq = &rwnx_hw->hwq[sw_txhdr->hw_queue];
+ rwnx_txq_confirm_any(rwnx_hw, txq, hwq, sw_txhdr);
+
+ /* Update txq and HW queue credits */
+ if (sw_txhdr->desc.host.flags & TXU_CNTRL_MGMT) {
+ trace_printk("done=%d retry_required=%d sw_retry_required=%d acknowledged=%d\n",
+ rwnx_txst.tx_done, rwnx_txst.retry_required,
+ rwnx_txst.sw_retry_required, rwnx_txst.acknowledged);
+#ifdef CREATE_TRACE_POINTS
+ trace_mgmt_cfm(sw_txhdr->rwnx_vif->vif_index,
+ (sw_txhdr->rwnx_sta) ? sw_txhdr->rwnx_sta->sta_idx : 0xFF,
+ rwnx_txst.acknowledged);
+#endif
+ /* Confirm transmission to CFG80211 */
+ cfg80211_mgmt_tx_status(&sw_txhdr->rwnx_vif->wdev,
+ (unsigned long)skb,
+ (skb->data + sw_txhdr->headroom),
+ sw_txhdr->frame_len,
+ rwnx_txst.acknowledged,
+ GFP_ATOMIC);
+ } else if ((txq->idx != TXQ_INACTIVE) &&
+ (rwnx_txst.retry_required || rwnx_txst.sw_retry_required)) {
+ bool sw_retry = (rwnx_txst.sw_retry_required) ? true : false;
+
+ /* Reset the status */
+ txhdr->hw_hdr.cfm.status.value = 0;
+
+ /* The confirmed packet was part of an AMPDU and not acked
+ * correctly, so reinject it in the TX path to be retried */
+ rwnx_tx_retry(rwnx_hw, skb, txhdr, sw_retry);
+ return 0;
+ }
+#ifdef CREATE_TRACE_POINTS
+ trace_skb_confirm(skb, txq, hwq, &txhdr->hw_hdr.cfm);
+#endif
+ /* STA may have disconnect (and txq stopped) when buffers were stored
+ in fw. In this case do nothing when they're returned */
+ if (txq->idx != TXQ_INACTIVE) {
+ #if 0
+ if (txhdr->hw_hdr.cfm.credits) {
+ txq->credits += txhdr->hw_hdr.cfm.credits;
+ if (txq->credits <= 0)
+ rwnx_txq_stop(txq, RWNX_TXQ_STOP_FULL);
+ else if (txq->credits > 0)
+ rwnx_txq_start(txq, RWNX_TXQ_STOP_FULL);
+ }
+ #endif
+
+ /* continue service period */
+ if (unlikely(txq->push_limit && !rwnx_txq_is_full(txq))) {
+ rwnx_txq_add_to_hw_list(txq);
+ }
+ }
+
+ if (txhdr->hw_hdr.cfm.ampdu_size &&
+ txhdr->hw_hdr.cfm.ampdu_size < IEEE80211_MAX_AMPDU_BUF)
+ rwnx_hw->stats.ampdus_tx[txhdr->hw_hdr.cfm.ampdu_size - 1]++;
+
+#ifdef CONFIG_RWNX_AMSDUS_TX
+ txq->amsdu_len = txhdr->hw_hdr.cfm.amsdu_size;
+#endif
+
+ /* Update statistics */
+ sw_txhdr->rwnx_vif->net_stats.tx_packets++;
+ sw_txhdr->rwnx_vif->net_stats.tx_bytes += sw_txhdr->frame_len;
+
+ /* Release SKBs */
+#ifdef CONFIG_RWNX_AMSDUS_TX
+ if (sw_txhdr->desc.host.flags & TXU_CNTRL_AMSDU) {
+ struct rwnx_amsdu_txhdr *amsdu_txhdr;
+ list_for_each_entry(amsdu_txhdr, &sw_txhdr->amsdu.hdrs, list) {
+ rwnx_amsdu_del_subframe_header(amsdu_txhdr);
+ consume_skb(amsdu_txhdr->skb);
+ }
+ }
+#endif /* CONFIG_RWNX_AMSDUS_TX */
+
+ kmem_cache_free(rwnx_hw->sw_txhdr_cache, sw_txhdr);
+ skb_pull(skb, sw_txhdr->headroom);
+ consume_skb(skb);
+
+ return 0;
+}
+
+/**
+ * rwnx_txq_credit_update - Update credit for one txq
+ *
+ * @rwnx_hw: Driver main data
+ * @sta_idx: STA idx
+ * @tid: TID
+ * @update: offset to apply in txq credits
+ *
+ * Called when fw send ME_TX_CREDITS_UPDATE_IND message.
+ * Apply @update to txq credits, and stop/start the txq if needed
+ */
+void rwnx_txq_credit_update(struct rwnx_hw *rwnx_hw, int sta_idx, u8 tid,
+ s8 update)
+{
+ struct rwnx_sta *sta = &rwnx_hw->sta_table[sta_idx];
+ struct rwnx_txq *txq;
+
+ txq = rwnx_txq_sta_get(sta, tid, rwnx_hw);
+
+ spin_lock_bh(&rwnx_hw->tx_lock);
+
+ if (txq->idx != TXQ_INACTIVE) {
+ //txq->credits += update;
+#ifdef CREATE_TRACE_POINTS
+ trace_credit_update(txq, update);
+#endif
+ if (txq->credits <= 0)
+ rwnx_txq_stop(txq, RWNX_TXQ_STOP_FULL);
+ else
+ rwnx_txq_start(txq, RWNX_TXQ_STOP_FULL);
+ }
+
+ spin_unlock_bh(&rwnx_hw->tx_lock);
+}
diff --git a/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_tx.h b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_tx.h
new file mode 100644
index 000000000000..da4511d6ab94
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_tx.h
@@ -0,0 +1,198 @@
+/**
+ ******************************************************************************
+ *
+ * @file rwnx_tx.h
+ *
+ * Copyright (C) RivieraWaves 2012-2019
+ *
+ ******************************************************************************
+ */
+#ifndef _RWNX_TX_H_
+#define _RWNX_TX_H_
+
+#include <linux/ieee80211.h>
+#include <net/cfg80211.h>
+#include <linux/netdevice.h>
+#include "lmac_types.h"
+#include "ipc_shared.h"
+#include "rwnx_txq.h"
+#include "hal_desc.h"
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 8, 0)
+#define IEEE80211_NUM_TIDS 16
+#endif
+
+#define RWNX_HWQ_BK 0
+#define RWNX_HWQ_BE 1
+#define RWNX_HWQ_VI 2
+#define RWNX_HWQ_VO 3
+#define RWNX_HWQ_BCMC 4
+#define RWNX_HWQ_NB NX_TXQ_CNT
+#define RWNX_HWQ_ALL_ACS (RWNX_HWQ_BK | RWNX_HWQ_BE | RWNX_HWQ_VI | RWNX_HWQ_VO)
+#define RWNX_HWQ_ALL_ACS_BIT ( BIT(RWNX_HWQ_BK) | BIT(RWNX_HWQ_BE) | \
+ BIT(RWNX_HWQ_VI) | BIT(RWNX_HWQ_VO) )
+
+#define RWNX_TX_LIFETIME_MS 1000
+#define RWNX_TX_MAX_RATES NX_TX_MAX_RATES
+
+#define RWNX_SWTXHDR_ALIGN_SZ 4
+#define RWNX_SWTXHDR_ALIGN_MSK (RWNX_SWTXHDR_ALIGN_SZ - 1)
+#define RWNX_SWTXHDR_ALIGN_PADS(x) \
+ ((RWNX_SWTXHDR_ALIGN_SZ - ((x) & RWNX_SWTXHDR_ALIGN_MSK)) \
+ & RWNX_SWTXHDR_ALIGN_MSK)
+#if RWNX_SWTXHDR_ALIGN_SZ & RWNX_SWTXHDR_ALIGN_MSK
+#error bad RWNX_SWTXHDR_ALIGN_SZ
+#endif
+
+#define AMSDU_PADDING(x) ((4 - ((x) & 0x3)) & 0x3)
+
+#define TXU_CNTRL_RETRY BIT(0)
+#define TXU_CNTRL_MORE_DATA BIT(2)
+#define TXU_CNTRL_MGMT BIT(3)
+#define TXU_CNTRL_MGMT_NO_CCK BIT(4)
+#define TXU_CNTRL_AMSDU BIT(6)
+#define TXU_CNTRL_MGMT_ROBUST BIT(7)
+#define TXU_CNTRL_USE_4ADDR BIT(8)
+#define TXU_CNTRL_EOSP BIT(9)
+#define TXU_CNTRL_MESH_FWD BIT(10)
+#define TXU_CNTRL_TDLS BIT(11)
+
+extern const int rwnx_tid2hwq[IEEE80211_NUM_TIDS];
+
+/**
+ * struct rwnx_amsdu_txhdr - Structure added in skb headroom (instead of
+ * rwnx_txhdr) for amsdu subframe buffer (except for the first subframe
+ * that has a normal rwnx_txhdr)
+ *
+ * @list List of other amsdu subframe (rwnx_sw_txhdr.amsdu.hdrs)
+ * @map_len Length to be downloaded for this subframe
+ * @dma_addr Buffer address form embedded point of view
+ * @skb skb
+ * @pad padding added before this subframe
+ * (only use when amsdu must be dismantled)
+ * @msdu_len Size, in bytes, of the MSDU (without padding nor amsdu header)
+ */
+struct rwnx_amsdu_txhdr {
+ struct list_head list;
+ size_t map_len;
+ dma_addr_t dma_addr;
+ struct sk_buff *skb;
+ u16 pad;
+ u16 msdu_len;
+};
+
+/**
+ * struct rwnx_amsdu - Structure to manage creation of an A-MSDU, updated
+ * only In the first subframe of an A-MSDU
+ *
+ * @hdrs List of subframe of rwnx_amsdu_txhdr
+ * @len Current size for this A-MDSU (doesn't take padding into account)
+ * 0 means that no amsdu is in progress
+ * @nb Number of subframe in the amsdu
+ * @pad Padding to add before adding a new subframe
+ */
+struct rwnx_amsdu {
+ struct list_head hdrs;
+ u16 len;
+ u8 nb;
+ u8 pad;
+};
+
+/**
+ * struct rwnx_sw_txhdr - Software part of tx header
+ *
+ * @rwnx_sta sta to which this buffer is addressed
+ * @rwnx_vif vif that send the buffer
+ * @txq pointer to TXQ used to send the buffer
+ * @hw_queue Index of the HWQ used to push the buffer.
+ * May be different than txq->hwq->id on confirmation.
+ * @frame_len Size of the frame (doesn't not include mac header)
+ * (Only used to update stat, can't we use skb->len instead ?)
+ * @headroom Headroom added in skb to add rwnx_txhdr
+ * (Only used to remove it before freeing skb, is it needed ?)
+ * @amsdu Description of amsdu whose first subframe is this buffer
+ * (amsdu.nb = 0 means this buffer is not part of amsdu)
+ * @skb skb received from transmission
+ * @map_len Length mapped for DMA (only rwnx_hw_txhdr and data are mapped)
+ * @dma_addr DMA address after mapping
+ * @desc Buffer description that will be copied in shared mem for FW
+ */
+struct rwnx_sw_txhdr {
+ struct rwnx_sta *rwnx_sta;
+ struct rwnx_vif *rwnx_vif;
+ struct rwnx_txq *txq;
+ u8 hw_queue;
+ u16 frame_len;
+ u16 headroom;
+#ifdef CONFIG_RWNX_AMSDUS_TX
+ struct rwnx_amsdu amsdu;
+#endif
+ u32 need_cfm;
+ struct sk_buff *skb;
+
+ size_t map_len;
+ dma_addr_t dma_addr;
+ struct txdesc_api desc;
+ u8 raw_frame;
+ u8 fixed_rate;
+ u16 rate_config;
+};
+
+/**
+ * struct rwnx_txhdr - Stucture to control transimission of packet
+ * (Added in skb headroom)
+ *
+ * @sw_hdr: Information from driver
+ * @cache_guard:
+ * @hw_hdr: Information for/from hardware
+ */
+struct rwnx_txhdr {
+ struct rwnx_sw_txhdr *sw_hdr;
+ char cache_guard[L1_CACHE_BYTES];
+ struct rwnx_hw_txhdr hw_hdr;
+};
+
+u16 rwnx_select_txq(struct rwnx_vif *rwnx_vif, struct sk_buff *skb);
+netdev_tx_t rwnx_start_xmit(struct sk_buff *skb, struct net_device *dev);
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0))
+int rwnx_start_mgmt_xmit(struct rwnx_vif *vif, struct rwnx_sta *sta,
+ struct cfg80211_mgmt_tx_params *params, bool offchan,
+ u64 *cookie);
+#else
+int rwnx_start_mgmt_xmit(struct rwnx_vif *vif, struct rwnx_sta *sta,
+ struct ieee80211_channel *channel, bool offchan,
+ unsigned int wait, const u8* buf, size_t len,
+ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0))
+ bool no_cck,
+ #endif
+ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 3, 0))
+ bool dont_wait_for_ack,
+ #endif
+ u64 *cookie);
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) */
+#ifdef CONFIG_RWNX_MON_XMIT
+int rwnx_start_monitor_if_xmit(struct sk_buff *skb, struct net_device *dev);
+#endif
+int rwnx_txdatacfm(void *pthis, void *host_id);
+
+struct rwnx_hw;
+struct rwnx_sta;
+void rwnx_set_traffic_status(struct rwnx_hw *rwnx_hw,
+ struct rwnx_sta *sta,
+ bool available,
+ u8 ps_id);
+void rwnx_ps_bh_enable(struct rwnx_hw *rwnx_hw, struct rwnx_sta *sta,
+ bool enable);
+void rwnx_ps_bh_traffic_req(struct rwnx_hw *rwnx_hw, struct rwnx_sta *sta,
+ u16 pkt_req, u8 ps_id);
+
+void rwnx_switch_vif_sta_txq(struct rwnx_sta *sta, struct rwnx_vif *old_vif,
+ struct rwnx_vif *new_vif);
+
+int rwnx_dbgfs_print_sta(char *buf, size_t size, struct rwnx_sta *sta,
+ struct rwnx_hw *rwnx_hw);
+void rwnx_txq_credit_update(struct rwnx_hw *rwnx_hw, int sta_idx, u8 tid,
+ s8 update);
+void rwnx_tx_push(struct rwnx_hw *rwnx_hw, struct rwnx_txhdr *txhdr, int flags);
+
+#endif /* _RWNX_TX_H_ */
diff --git a/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_txq.c b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_txq.c
new file mode 100644
index 000000000000..7ffb99e845e4
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_txq.c
@@ -0,0 +1,1364 @@
+/**
+ ******************************************************************************
+ *
+ * @file rwnx_txq.c
+ *
+ * Copyright (C) RivieraWaves 2016-2019
+ *
+ ******************************************************************************
+ */
+
+#include "rwnx_defs.h"
+#include "rwnx_tx.h"
+#include "ipc_host.h"
+#include "rwnx_events.h"
+#include "rwnx_gki.h"
+
+/******************************************************************************
+ * Utils functions
+ *****************************************************************************/
+#ifdef CONFIG_RWNX_FULLMAC
+const int nx_tid_prio[NX_NB_TID_PER_STA] = {7, 6, 5, 4, 3, 0, 2, 1};
+
+static inline int rwnx_txq_sta_idx(struct rwnx_sta *sta, u8 tid)
+{
+ if (is_multicast_sta(sta->sta_idx)){
+ if((g_rwnx_plat->usbdev->chipid == PRODUCT_ID_AIC8801) ||
+ ((g_rwnx_plat->usbdev->chipid == PRODUCT_ID_AIC8800DC ||
+ g_rwnx_plat->usbdev->chipid == PRODUCT_ID_AIC8800DW) && chip_id < 3)){
+ return NX_FIRST_VIF_TXQ_IDX_FOR_OLD_IC + sta->vif_idx;
+ }else{
+ return NX_FIRST_VIF_TXQ_IDX + sta->vif_idx;
+ }
+ }else{
+ return (sta->sta_idx * NX_NB_TXQ_PER_STA) + tid;
+ }
+}
+
+static inline int rwnx_txq_vif_idx(struct rwnx_vif *vif, u8 type)
+{
+ if((g_rwnx_plat->usbdev->chipid == PRODUCT_ID_AIC8801) ||
+ ((g_rwnx_plat->usbdev->chipid == PRODUCT_ID_AIC8800DC ||
+ g_rwnx_plat->usbdev->chipid == PRODUCT_ID_AIC8800DW) && chip_id < 3)){
+ return NX_FIRST_VIF_TXQ_IDX_FOR_OLD_IC + master_vif_idx(vif) + (type * NX_VIRT_DEV_MAX);
+ }else{
+ return NX_FIRST_VIF_TXQ_IDX + master_vif_idx(vif) + (type * NX_VIRT_DEV_MAX);
+ }
+}
+
+struct rwnx_txq *rwnx_txq_sta_get(struct rwnx_sta *sta, u8 tid,
+ struct rwnx_hw * rwnx_hw)
+{
+ if (tid >= NX_NB_TXQ_PER_STA)
+ tid = 0;
+
+ return &rwnx_hw->txq[rwnx_txq_sta_idx(sta, tid)];
+}
+
+struct rwnx_txq *rwnx_txq_vif_get(struct rwnx_vif *vif, u8 type)
+{
+ if (type > NX_UNK_TXQ_TYPE)
+ type = NX_BCMC_TXQ_TYPE;
+
+ return &vif->rwnx_hw->txq[rwnx_txq_vif_idx(vif, type)];
+}
+
+static inline struct rwnx_sta *rwnx_txq_2_sta(struct rwnx_txq *txq)
+{
+ return txq->sta;
+}
+
+#endif /* CONFIG_RWNX_FULLMAC */
+
+
+/******************************************************************************
+ * Init/Deinit functions
+ *****************************************************************************/
+/**
+ * rwnx_txq_init - Initialize a TX queue
+ *
+ * @txq: TX queue to be initialized
+ * @idx: TX queue index
+ * @status: TX queue initial status
+ * @hwq: Associated HW queue
+ * @ndev: Net device this queue belongs to
+ * (may be null for non netdev txq)
+ *
+ * Each queue is initialized with the credit of @NX_TXQ_INITIAL_CREDITS.
+ */
+static void rwnx_txq_init(struct rwnx_txq *txq, int idx, u8 status,
+ struct rwnx_hwq *hwq, int tid,
+#ifdef CONFIG_RWNX_FULLMAC
+ struct rwnx_sta *sta, struct net_device *ndev
+#endif
+ )
+{
+ int i;
+ int nx_first_unk_txq_idx = NX_FIRST_UNK_TXQ_IDX;
+ int nx_bcmc_txq_ndev_idx = NX_BCMC_TXQ_NDEV_IDX;
+ int nx_first_vif_txq_idx = NX_FIRST_VIF_TXQ_IDX;
+
+ if((g_rwnx_plat->usbdev->chipid == PRODUCT_ID_AIC8801) ||
+ ((g_rwnx_plat->usbdev->chipid == PRODUCT_ID_AIC8800DC ||
+ g_rwnx_plat->usbdev->chipid == PRODUCT_ID_AIC8800DW) && chip_id < 3)){
+ nx_first_unk_txq_idx = NX_FIRST_UNK_TXQ_IDX_FOR_OLD_IC;
+ nx_bcmc_txq_ndev_idx = NX_BCMC_TXQ_NDEV_IDX_FOR_OLD_IC;
+ nx_first_vif_txq_idx = NX_FIRST_VIF_TXQ_IDX_FOR_OLD_IC;
+ }
+
+
+ txq->idx = idx;
+ txq->status = status;
+ txq->credits = NX_TXQ_INITIAL_CREDITS;
+ txq->pkt_sent = 0;
+ skb_queue_head_init(&txq->sk_list);
+ txq->last_retry_skb = NULL;
+ txq->nb_retry = 0;
+ txq->hwq = hwq;
+ txq->sta = sta;
+ for (i = 0; i < CONFIG_USER_MAX ; i++)
+ txq->pkt_pushed[i] = 0;
+ txq->push_limit = 0;
+ txq->tid = tid;
+#ifdef CONFIG_MAC80211_TXQ
+ txq->nb_ready_mac80211 = 0;
+#endif
+#ifdef CONFIG_RWNX_FULLMAC
+ txq->ps_id = LEGACY_PS_ID;
+ if (idx < nx_first_vif_txq_idx) {
+ int sta_idx = sta->sta_idx;
+ int tid = idx - (sta_idx * NX_NB_TXQ_PER_STA);
+ if (tid < NX_NB_TID_PER_STA)
+ txq->ndev_idx = NX_STA_NDEV_IDX(tid, sta_idx);
+ else
+ txq->ndev_idx = NDEV_NO_TXQ;
+ } else if (idx < nx_first_unk_txq_idx) {
+ txq->ndev_idx = nx_bcmc_txq_ndev_idx;
+ } else {
+ txq->ndev_idx = NDEV_NO_TXQ;
+ }
+ txq->ndev = ndev;
+#ifdef CONFIG_RWNX_AMSDUS_TX
+ txq->amsdu = NULL;
+ txq->amsdu_len = 0;
+#endif /* CONFIG_RWNX_AMSDUS_TX */
+#endif /* CONFIG_RWNX_FULLMAC */
+}
+
+/**
+ * rwnx_txq_flush - Flush all buffers queued for a TXQ
+ *
+ * @rwnx_hw: main driver data
+ * @txq: txq to flush
+ */
+void rwnx_txq_flush(struct rwnx_hw *rwnx_hw, struct rwnx_txq *txq)
+{
+ struct sk_buff *skb;
+
+
+ while((skb = skb_dequeue(&txq->sk_list)) != NULL) {
+ struct rwnx_sw_txhdr *sw_txhdr = ((struct rwnx_txhdr *)skb->data)->sw_hdr;
+
+#ifdef CONFIG_RWNX_AMSDUS_TX
+ if (sw_txhdr->desc.host.packet_cnt > 1) {
+ struct rwnx_amsdu_txhdr *amsdu_txhdr;
+ list_for_each_entry(amsdu_txhdr, &sw_txhdr->amsdu.hdrs, list) {
+ //dma_unmap_single(rwnx_hw->dev, amsdu_txhdr->dma_addr,
+ // amsdu_txhdr->map_len, DMA_TO_DEVICE);
+ dev_kfree_skb_any(amsdu_txhdr->skb);
+ }
+ }
+#endif
+ kmem_cache_free(rwnx_hw->sw_txhdr_cache, sw_txhdr);
+ //dma_unmap_single(rwnx_hw->dev, sw_txhdr->dma_addr, sw_txhdr->map_len,
+ // DMA_TO_DEVICE);
+
+#ifdef CONFIG_RWNX_FULLMAC
+ dev_kfree_skb_any(skb);
+#endif /* CONFIG_RWNX_FULLMAC */
+ }
+}
+
+/**
+ * rwnx_txq_deinit - De-initialize a TX queue
+ *
+ * @rwnx_hw: Driver main data
+ * @txq: TX queue to be de-initialized
+ * Any buffer stuck in a queue will be freed.
+ */
+static void rwnx_txq_deinit(struct rwnx_hw *rwnx_hw, struct rwnx_txq *txq)
+{
+ if (txq->idx == TXQ_INACTIVE)
+ return;
+
+ spin_lock_bh(&rwnx_hw->tx_lock);
+ rwnx_txq_del_from_hw_list(txq);
+ txq->idx = TXQ_INACTIVE;
+ spin_unlock_bh(&rwnx_hw->tx_lock);
+
+ rwnx_txq_flush(rwnx_hw, txq);
+}
+
+/**
+ * rwnx_txq_vif_init - Initialize all TXQ linked to a vif
+ *
+ * @rwnx_hw: main driver data
+ * @rwnx_vif: Pointer on VIF
+ * @status: Intial txq status
+ *
+ * Softmac : 1 VIF TXQ per HWQ
+ *
+ * Fullmac : 1 VIF TXQ for BC/MC
+ * 1 VIF TXQ for MGMT to unknown STA
+ */
+void rwnx_txq_vif_init(struct rwnx_hw *rwnx_hw, struct rwnx_vif *rwnx_vif,
+ u8 status)
+{
+ struct rwnx_txq *txq;
+ int idx;
+
+#ifdef CONFIG_RWNX_FULLMAC
+ txq = rwnx_txq_vif_get(rwnx_vif, NX_BCMC_TXQ_TYPE);
+ idx = rwnx_txq_vif_idx(rwnx_vif, NX_BCMC_TXQ_TYPE);
+ rwnx_txq_init(txq, idx, status, &rwnx_hw->hwq[RWNX_HWQ_BE], 0,
+ &rwnx_hw->sta_table[rwnx_vif->ap.bcmc_index], rwnx_vif->ndev);
+
+ txq = rwnx_txq_vif_get(rwnx_vif, NX_UNK_TXQ_TYPE);
+ idx = rwnx_txq_vif_idx(rwnx_vif, NX_UNK_TXQ_TYPE);
+ rwnx_txq_init(txq, idx, status, &rwnx_hw->hwq[RWNX_HWQ_VO], TID_MGT,
+ NULL, rwnx_vif->ndev);
+
+#endif /* CONFIG_RWNX_FULLMAC */
+}
+
+/**
+ * rwnx_txq_vif_deinit - Deinitialize all TXQ linked to a vif
+ *
+ * @rwnx_hw: main driver data
+ * @rwnx_vif: Pointer on VIF
+ */
+void rwnx_txq_vif_deinit(struct rwnx_hw * rwnx_hw, struct rwnx_vif *rwnx_vif)
+{
+ struct rwnx_txq *txq;
+
+#ifdef CONFIG_RWNX_FULLMAC
+ txq = rwnx_txq_vif_get(rwnx_vif, NX_BCMC_TXQ_TYPE);
+ rwnx_txq_deinit(rwnx_hw, txq);
+
+ txq = rwnx_txq_vif_get(rwnx_vif, NX_UNK_TXQ_TYPE);
+ rwnx_txq_deinit(rwnx_hw, txq);
+
+#endif /* CONFIG_RWNX_FULLMAC */
+}
+
+
+/**
+ * rwnx_txq_sta_init - Initialize TX queues for a STA
+ *
+ * @rwnx_hw: Main driver data
+ * @rwnx_sta: STA for which tx queues need to be initialized
+ * @status: Intial txq status
+ *
+ * This function initialize all the TXQ associated to a STA.
+ * Softmac : 1 TXQ per TID
+ *
+ * Fullmac : 1 TXQ per TID (limited to 8)
+ * 1 TXQ for MGMT
+ */
+void rwnx_txq_sta_init(struct rwnx_hw *rwnx_hw, struct rwnx_sta *rwnx_sta,
+ u8 status)
+{
+ struct rwnx_txq *txq;
+ int tid, idx;
+
+#ifdef CONFIG_RWNX_FULLMAC
+ struct rwnx_vif *rwnx_vif = rwnx_hw->vif_table[rwnx_sta->vif_idx];
+ idx = rwnx_txq_sta_idx(rwnx_sta, 0);
+
+ foreach_sta_txq(rwnx_sta, txq, tid, rwnx_hw) {
+ rwnx_txq_init(txq, idx, status, &rwnx_hw->hwq[rwnx_tid2hwq[tid]], tid,
+ rwnx_sta, rwnx_vif->ndev);
+ txq->ps_id = rwnx_sta->uapsd_tids & (1 << tid) ? UAPSD_ID : LEGACY_PS_ID;
+ idx++;
+ }
+
+#endif /* CONFIG_RWNX_FULLMAC*/
+}
+
+/**
+ * rwnx_txq_sta_deinit - Deinitialize TX queues for a STA
+ *
+ * @rwnx_hw: Main driver data
+ * @rwnx_sta: STA for which tx queues need to be deinitialized
+ */
+void rwnx_txq_sta_deinit(struct rwnx_hw *rwnx_hw, struct rwnx_sta *rwnx_sta)
+{
+ struct rwnx_txq *txq;
+ int tid;
+
+ foreach_sta_txq(rwnx_sta, txq, tid, rwnx_hw) {
+ rwnx_txq_deinit(rwnx_hw, txq);
+ }
+}
+
+#ifdef CONFIG_RWNX_FULLMAC
+/**
+ * rwnx_txq_unk_vif_init - Initialize TXQ for unknown STA linked to a vif
+ *
+ * @rwnx_vif: Pointer on VIF
+ */
+void rwnx_txq_unk_vif_init(struct rwnx_vif *rwnx_vif)
+{
+ struct rwnx_hw *rwnx_hw = rwnx_vif->rwnx_hw;
+ struct rwnx_txq *txq;
+ int idx;
+
+ txq = rwnx_txq_vif_get(rwnx_vif, NX_UNK_TXQ_TYPE);
+ idx = rwnx_txq_vif_idx(rwnx_vif, NX_UNK_TXQ_TYPE);
+ rwnx_txq_init(txq, idx, 0, &rwnx_hw->hwq[RWNX_HWQ_VO], TID_MGT, NULL, rwnx_vif->ndev);
+}
+
+/**
+ * rwnx_txq_tdls_vif_deinit - Deinitialize TXQ for unknown STA linked to a vif
+ *
+ * @rwnx_vif: Pointer on VIF
+ */
+void rwnx_txq_unk_vif_deinit(struct rwnx_vif *rwnx_vif)
+{
+ struct rwnx_txq *txq;
+
+ txq = rwnx_txq_vif_get(rwnx_vif, NX_UNK_TXQ_TYPE);
+ rwnx_txq_deinit(rwnx_vif->rwnx_hw, txq);
+}
+
+/**
+ * rwnx_init_unk_txq - Initialize TX queue for the transmission on a offchannel
+ *
+ * @vif: Interface for which the queue has to be initialized
+ *
+ * NOTE: Offchannel txq is only active for the duration of the ROC
+ */
+void rwnx_txq_offchan_init(struct rwnx_vif *rwnx_vif)
+{
+ struct rwnx_hw *rwnx_hw = rwnx_vif->rwnx_hw;
+ struct rwnx_txq *txq;
+ int nx_off_chan_txq_idx = NX_OFF_CHAN_TXQ_IDX;
+
+ if((g_rwnx_plat->usbdev->chipid == PRODUCT_ID_AIC8801) ||
+ ((g_rwnx_plat->usbdev->chipid == PRODUCT_ID_AIC8800DC ||
+ g_rwnx_plat->usbdev->chipid == PRODUCT_ID_AIC8800DW) && chip_id < 3)){
+ nx_off_chan_txq_idx = NX_OFF_CHAN_TXQ_IDX_FOR_OLD_IC;
+ }
+
+
+ txq = &rwnx_hw->txq[nx_off_chan_txq_idx];
+ rwnx_txq_init(txq, nx_off_chan_txq_idx, RWNX_TXQ_STOP_CHAN,
+ &rwnx_hw->hwq[RWNX_HWQ_VO], TID_MGT, NULL, rwnx_vif->ndev);
+}
+
+/**
+ * rwnx_deinit_offchan_txq - Deinitialize TX queue for offchannel
+ *
+ * @vif: Interface that manages the STA
+ *
+ * This function deintialize txq for one STA.
+ * Any buffer stuck in a queue will be freed.
+ */
+void rwnx_txq_offchan_deinit(struct rwnx_vif *rwnx_vif)
+{
+ struct rwnx_txq *txq;
+ int nx_off_chan_txq_idx = NX_OFF_CHAN_TXQ_IDX;
+
+ if((g_rwnx_plat->usbdev->chipid == PRODUCT_ID_AIC8801) ||
+ ((g_rwnx_plat->usbdev->chipid == PRODUCT_ID_AIC8800DC ||
+ g_rwnx_plat->usbdev->chipid == PRODUCT_ID_AIC8800DW) && chip_id < 3)){
+ nx_off_chan_txq_idx = NX_OFF_CHAN_TXQ_IDX_FOR_OLD_IC;
+ }
+
+ txq = &rwnx_vif->rwnx_hw->txq[nx_off_chan_txq_idx];
+ rwnx_txq_deinit(rwnx_vif->rwnx_hw, txq);
+}
+
+
+/**
+ * rwnx_txq_tdls_vif_init - Initialize TXQ vif for TDLS
+ *
+ * @rwnx_vif: Pointer on VIF
+ */
+void rwnx_txq_tdls_vif_init(struct rwnx_vif *rwnx_vif)
+{
+ struct rwnx_hw *rwnx_hw = rwnx_vif->rwnx_hw;
+
+ if (!(rwnx_hw->wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS))
+ return;
+
+ rwnx_txq_unk_vif_init(rwnx_vif);
+}
+
+/**
+ * rwnx_txq_tdls_vif_deinit - Deinitialize TXQ vif for TDLS
+ *
+ * @rwnx_vif: Pointer on VIF
+ */
+void rwnx_txq_tdls_vif_deinit(struct rwnx_vif *rwnx_vif)
+{
+ struct rwnx_hw *rwnx_hw = rwnx_vif->rwnx_hw;
+
+ if (!(rwnx_hw->wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS))
+ return;
+
+ rwnx_txq_unk_vif_deinit(rwnx_vif);
+}
+#endif
+
+/******************************************************************************
+ * Start/Stop functions
+ *****************************************************************************/
+/**
+ * rwnx_txq_add_to_hw_list - Add TX queue to a HW queue schedule list.
+ *
+ * @txq: TX queue to add
+ *
+ * Add the TX queue if not already present in the HW queue list.
+ * To be called with tx_lock hold
+ */
+void rwnx_txq_add_to_hw_list(struct rwnx_txq *txq)
+{
+ if (!(txq->status & RWNX_TXQ_IN_HWQ_LIST)) {
+#ifdef CREATE_TRACE_POINTS
+ trace_txq_add_to_hw(txq);
+#endif
+ txq->status |= RWNX_TXQ_IN_HWQ_LIST;
+ list_add_tail(&txq->sched_list, &txq->hwq->list);
+ txq->hwq->need_processing = true;
+ }
+}
+
+/**
+ * rwnx_txq_del_from_hw_list - Delete TX queue from a HW queue schedule list.
+ *
+ * @txq: TX queue to delete
+ *
+ * Remove the TX queue from the HW queue list if present.
+ * To be called with tx_lock hold
+ */
+void rwnx_txq_del_from_hw_list(struct rwnx_txq *txq)
+{
+ if (txq->status & RWNX_TXQ_IN_HWQ_LIST) {
+#ifdef CREATE_TRACE_POINTS
+ trace_txq_del_from_hw(txq);
+#endif
+ txq->status &= ~RWNX_TXQ_IN_HWQ_LIST;
+ list_del(&txq->sched_list);
+ }
+}
+
+/**
+ * rwnx_txq_skb_ready - Check if skb are available for the txq
+ *
+ * @txq: Pointer on txq
+ * @return True if there are buffer ready to be pushed on this txq,
+ * false otherwise
+ */
+static inline bool rwnx_txq_skb_ready(struct rwnx_txq *txq)
+{
+#ifdef CONFIG_MAC80211_TXQ
+ if (txq->nb_ready_mac80211 != NOT_MAC80211_TXQ)
+ return ((txq->nb_ready_mac80211 > 0) || !skb_queue_empty(&txq->sk_list));
+ else
+#endif
+ return !skb_queue_empty(&txq->sk_list);
+}
+
+/**
+ * rwnx_txq_start - Try to Start one TX queue
+ *
+ * @txq: TX queue to start
+ * @reason: reason why the TX queue is started (among RWNX_TXQ_STOP_xxx)
+ *
+ * Re-start the TX queue for one reason.
+ * If after this the txq is no longer stopped and some buffers are ready,
+ * the TX queue is also added to HW queue list.
+ * To be called with tx_lock hold
+ */
+void rwnx_txq_start(struct rwnx_txq *txq, u16 reason)
+{
+ BUG_ON(txq==NULL);
+ if (txq->idx != TXQ_INACTIVE && (txq->status & reason))
+ {
+#ifdef CREATE_TRACE_POINTS
+ trace_txq_start(txq, reason);
+#endif
+ txq->status &= ~reason;
+ if (!rwnx_txq_is_stopped(txq) && rwnx_txq_skb_ready(txq))
+ rwnx_txq_add_to_hw_list(txq);
+ }
+}
+
+/**
+ * rwnx_txq_stop - Stop one TX queue
+ *
+ * @txq: TX queue to stop
+ * @reason: reason why the TX queue is stopped (among RWNX_TXQ_STOP_xxx)
+ *
+ * Stop the TX queue. It will remove the TX queue from HW queue list
+ * To be called with tx_lock hold
+ */
+void rwnx_txq_stop(struct rwnx_txq *txq, u16 reason)
+{
+ BUG_ON(txq==NULL);
+ if (txq->idx != TXQ_INACTIVE)
+ {
+#ifdef CREATE_TRACE_POINTS
+ trace_txq_stop(txq, reason);
+#endif
+ txq->status |= reason;
+ rwnx_txq_del_from_hw_list(txq);
+ }
+}
+
+
+/**
+ * rwnx_txq_sta_start - Start all the TX queue linked to a STA
+ *
+ * @sta: STA whose TX queues must be re-started
+ * @reason: Reason why the TX queue are restarted (among RWNX_TXQ_STOP_xxx)
+ * @rwnx_hw: Driver main data
+ *
+ * This function will re-start all the TX queues of the STA for the reason
+ * specified. It can be :
+ * - RWNX_TXQ_STOP_STA_PS: the STA is no longer in power save mode
+ * - RWNX_TXQ_STOP_VIF_PS: the VIF is in power save mode (p2p absence)
+ * - RWNX_TXQ_STOP_CHAN: the STA's VIF is now on the current active channel
+ *
+ * Any TX queue with buffer ready and not Stopped for other reasons, will be
+ * added to the HW queue list
+ * To be called with tx_lock hold
+ */
+void rwnx_txq_sta_start(struct rwnx_sta *rwnx_sta, u16 reason
+#ifdef CONFIG_RWNX_FULLMAC
+ , struct rwnx_hw *rwnx_hw
+#endif
+ )
+{
+ struct rwnx_txq *txq;
+ int tid;
+#ifdef CREATE_TRACE_POINTS
+ trace_txq_sta_start(rwnx_sta->sta_idx);
+#endif
+ foreach_sta_txq(rwnx_sta, txq, tid, rwnx_hw) {
+ rwnx_txq_start(txq, reason);
+ }
+}
+
+
+/**
+ * rwnx_stop_sta_txq - Stop all the TX queue linked to a STA
+ *
+ * @sta: STA whose TX queues must be stopped
+ * @reason: Reason why the TX queue are stopped (among RWNX_TX_STOP_xxx)
+ * @rwnx_hw: Driver main data
+ *
+ * This function will stop all the TX queues of the STA for the reason
+ * specified. It can be :
+ * - RWNX_TXQ_STOP_STA_PS: the STA is in power save mode
+ * - RWNX_TXQ_STOP_VIF_PS: the VIF is in power save mode (p2p absence)
+ * - RWNX_TXQ_STOP_CHAN: the STA's VIF is not on the current active channel
+ *
+ * Any TX queue present in a HW queue list will be removed from this list.
+ * To be called with tx_lock hold
+ */
+void rwnx_txq_sta_stop(struct rwnx_sta *rwnx_sta, u16 reason
+#ifdef CONFIG_RWNX_FULLMAC
+ , struct rwnx_hw *rwnx_hw
+#endif
+ )
+{
+ struct rwnx_txq *txq;
+ int tid;
+
+ if (!rwnx_sta)
+ return;
+#ifdef CREATE_TRACE_POINTS
+ trace_txq_sta_stop(rwnx_sta->sta_idx);
+#endif
+ foreach_sta_txq(rwnx_sta, txq, tid, rwnx_hw) {
+ rwnx_txq_stop(txq, reason);
+ }
+}
+
+#ifdef CONFIG_RWNX_FULLMAC
+void rwnx_txq_tdls_sta_start(struct rwnx_vif *rwnx_vif, u16 reason,
+ struct rwnx_hw *rwnx_hw)
+{
+#ifdef CREATE_TRACE_POINTS
+ trace_txq_vif_start(rwnx_vif->vif_index);
+#endif
+ spin_lock_bh(&rwnx_hw->tx_lock);
+
+ if (rwnx_vif->sta.tdls_sta)
+ rwnx_txq_sta_start(rwnx_vif->sta.tdls_sta, reason, rwnx_hw);
+
+ spin_unlock_bh(&rwnx_hw->tx_lock);
+}
+#endif
+
+#ifdef CONFIG_RWNX_FULLMAC
+void rwnx_txq_tdls_sta_stop(struct rwnx_vif *rwnx_vif, u16 reason,
+ struct rwnx_hw *rwnx_hw)
+{
+#ifdef CREATE_TRACE_POINTS
+ trace_txq_vif_stop(rwnx_vif->vif_index);
+#endif
+ spin_lock_bh(&rwnx_hw->tx_lock);
+
+ if (rwnx_vif->sta.tdls_sta)
+ rwnx_txq_sta_stop(rwnx_vif->sta.tdls_sta, reason, rwnx_hw);
+
+ spin_unlock_bh(&rwnx_hw->tx_lock);
+}
+#endif
+
+#ifdef CONFIG_RWNX_FULLMAC
+static inline
+void rwnx_txq_vif_for_each_sta(struct rwnx_hw *rwnx_hw, struct rwnx_vif *rwnx_vif,
+ void (*f)(struct rwnx_sta *, u16, struct rwnx_hw *),
+ u16 reason)
+{
+
+ switch (RWNX_VIF_TYPE(rwnx_vif)) {
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_P2P_CLIENT:
+ {
+ if (rwnx_vif->tdls_status == TDLS_LINK_ACTIVE)
+ f(rwnx_vif->sta.tdls_sta, reason, rwnx_hw);
+ if (!WARN_ON(rwnx_vif->sta.ap == NULL))
+ f(rwnx_vif->sta.ap, reason, rwnx_hw);
+ break;
+ }
+ case NL80211_IFTYPE_AP_VLAN:
+ rwnx_vif = rwnx_vif->ap_vlan.master;
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_MESH_POINT:
+ case NL80211_IFTYPE_P2P_GO:
+ {
+ struct rwnx_sta *sta;
+ list_for_each_entry(sta, &rwnx_vif->ap.sta_list, list) {
+ f(sta, reason, rwnx_hw);
+ }
+ break;
+ }
+ default:
+ BUG();
+ break;
+ }
+}
+
+#endif
+
+/**
+ * rwnx_txq_vif_start - START TX queues of all STA associated to the vif
+ * and vif's TXQ
+ *
+ * @vif: Interface to start
+ * @reason: Start reason (RWNX_TXQ_STOP_CHAN or RWNX_TXQ_STOP_VIF_PS)
+ * @rwnx_hw: Driver main data
+ *
+ * Iterate over all the STA associated to the vif and re-start them for the
+ * reason @reason
+ * Take tx_lock
+ */
+void rwnx_txq_vif_start(struct rwnx_vif *rwnx_vif, u16 reason,
+ struct rwnx_hw *rwnx_hw)
+{
+ struct rwnx_txq *txq;
+#ifdef CREATE_TRACE_POINTS
+ trace_txq_vif_start(rwnx_vif->vif_index);
+#endif
+ spin_lock_bh(&rwnx_hw->tx_lock);
+
+#ifdef CONFIG_RWNX_FULLMAC
+ //Reject if monitor interface
+ if (rwnx_vif->wdev.iftype == NL80211_IFTYPE_MONITOR)
+ goto end;
+
+ if (rwnx_vif->roc_tdls && rwnx_vif->sta.tdls_sta && rwnx_vif->sta.tdls_sta->tdls.chsw_en) {
+ rwnx_txq_sta_start(rwnx_vif->sta.tdls_sta, reason, rwnx_hw);
+ }
+ if (!rwnx_vif->roc_tdls) {
+ rwnx_txq_vif_for_each_sta(rwnx_hw, rwnx_vif, rwnx_txq_sta_start, reason);
+ }
+
+ txq = rwnx_txq_vif_get(rwnx_vif, NX_BCMC_TXQ_TYPE);
+ rwnx_txq_start(txq, reason);
+ txq = rwnx_txq_vif_get(rwnx_vif, NX_UNK_TXQ_TYPE);
+ rwnx_txq_start(txq, reason);
+
+end:
+#endif /* CONFIG_RWNX_FULLMAC */
+
+ spin_unlock_bh(&rwnx_hw->tx_lock);
+}
+
+
+/**
+ * rwnx_txq_vif_stop - STOP TX queues of all STA associated to the vif
+ *
+ * @vif: Interface to stop
+ * @arg: Stop reason (RWNX_TXQ_STOP_CHAN or RWNX_TXQ_STOP_VIF_PS)
+ * @rwnx_hw: Driver main data
+ *
+ * Iterate over all the STA associated to the vif and stop them for the
+ * reason RWNX_TXQ_STOP_CHAN or RWNX_TXQ_STOP_VIF_PS
+ * Take tx_lock
+ */
+void rwnx_txq_vif_stop(struct rwnx_vif *rwnx_vif, u16 reason,
+ struct rwnx_hw *rwnx_hw)
+{
+ struct rwnx_txq *txq;
+
+ //RWNX_DBG(RWNX_FN_ENTRY_STR);
+#ifdef CREATE_TRACE_POINTS
+ trace_txq_vif_stop(rwnx_vif->vif_index);
+#endif
+ spin_lock_bh(&rwnx_hw->tx_lock);
+
+#ifdef CONFIG_RWNX_FULLMAC
+ //Reject if monitor interface
+ if (rwnx_vif->wdev.iftype == NL80211_IFTYPE_MONITOR)
+ goto end;
+
+ rwnx_txq_vif_for_each_sta(rwnx_hw, rwnx_vif, rwnx_txq_sta_stop, reason);
+
+ txq = rwnx_txq_vif_get(rwnx_vif, NX_BCMC_TXQ_TYPE);
+ rwnx_txq_stop(txq, reason);
+ txq = rwnx_txq_vif_get(rwnx_vif, NX_UNK_TXQ_TYPE);
+ rwnx_txq_stop(txq, reason);
+
+end:
+#endif /* CONFIG_RWNX_FULLMAC*/
+
+ spin_unlock_bh(&rwnx_hw->tx_lock);
+}
+
+#ifdef CONFIG_RWNX_FULLMAC
+
+/**
+ * rwnx_start_offchan_txq - START TX queue for offchannel frame
+ *
+ * @rwnx_hw: Driver main data
+ */
+void rwnx_txq_offchan_start(struct rwnx_hw *rwnx_hw)
+{
+ struct rwnx_txq *txq;
+ int nx_off_chan_txq_idx = NX_OFF_CHAN_TXQ_IDX;
+
+ if((g_rwnx_plat->usbdev->chipid == PRODUCT_ID_AIC8801) ||
+ ((g_rwnx_plat->usbdev->chipid == PRODUCT_ID_AIC8800DC ||
+ g_rwnx_plat->usbdev->chipid == PRODUCT_ID_AIC8800DW) && chip_id < 3)){
+ nx_off_chan_txq_idx = NX_OFF_CHAN_TXQ_IDX_FOR_OLD_IC;
+ }
+
+
+ txq = &rwnx_hw->txq[nx_off_chan_txq_idx];
+ spin_lock_bh(&rwnx_hw->tx_lock);
+ rwnx_txq_start(txq, RWNX_TXQ_STOP_CHAN);
+ spin_unlock_bh(&rwnx_hw->tx_lock);
+}
+
+/**
+ * rwnx_switch_vif_sta_txq - Associate TXQ linked to a STA to a new vif
+ *
+ * @sta: STA whose txq must be switched
+ * @old_vif: Vif currently associated to the STA (may no longer be active)
+ * @new_vif: vif which should be associated to the STA for now on
+ *
+ * This function will switch the vif (i.e. the netdev) associated to all STA's
+ * TXQ. This is used when AP_VLAN interface are created.
+ * If one STA is associated to an AP_vlan vif, it will be moved from the master
+ * AP vif to the AP_vlan vif.
+ * If an AP_vlan vif is removed, then STA will be moved back to mastert AP vif.
+ *
+ */
+void rwnx_txq_sta_switch_vif(struct rwnx_sta *sta, struct rwnx_vif *old_vif,
+ struct rwnx_vif *new_vif)
+{
+ struct rwnx_hw *rwnx_hw = new_vif->rwnx_hw;
+ struct rwnx_txq *txq;
+ int i;
+
+ /* start TXQ on the new interface, and update ndev field in txq */
+ if (!netif_carrier_ok(new_vif->ndev))
+ netif_carrier_on(new_vif->ndev);
+ txq = rwnx_txq_sta_get(sta, 0, rwnx_hw);
+ for (i = 0; i < NX_NB_TID_PER_STA; i++, txq++) {
+ txq->ndev = new_vif->ndev;
+ netif_wake_subqueue(txq->ndev, txq->ndev_idx);
+ }
+}
+#endif /* CONFIG_RWNX_FULLMAC */
+
+/******************************************************************************
+ * TXQ queue/schedule functions
+ *****************************************************************************/
+/**
+ * rwnx_txq_queue_skb - Queue a buffer in a TX queue
+ *
+ * @skb: Buffer to queue
+ * @txq: TX Queue in which the buffer must be added
+ * @rwnx_hw: Driver main data
+ * @retry: Should it be queued in the retry list
+ *
+ * @return: Retrun 1 if txq has been added to hwq list, 0 otherwise
+ *
+ * Add a buffer in the buffer list of the TX queue
+ * and add this TX queue in the HW queue list if the txq is not stopped.
+ * If this is a retry packet it is added after the last retry packet or at the
+ * beginning if there is no retry packet queued.
+ *
+ * If the STA is in PS mode and this is the first packet queued for this txq
+ * update TIM.
+ *
+ * To be called with tx_lock hold
+ */
+int rwnx_txq_queue_skb(struct sk_buff *skb, struct rwnx_txq *txq,
+ struct rwnx_hw *rwnx_hw, bool retry)
+{
+#ifndef CONFIG_ONE_TXQ
+ unsigned long flags;
+#endif
+
+#ifdef CONFIG_RWNX_FULLMAC
+ if (unlikely(txq->sta && txq->sta->ps.active)) {
+ txq->sta->ps.pkt_ready[txq->ps_id]++;
+#ifdef CREATE_TRACE_POINT
+ trace_ps_queue(txq->sta);
+#endif
+ if (txq->sta->ps.pkt_ready[txq->ps_id] == 1) {
+ rwnx_set_traffic_status(rwnx_hw, txq->sta, true, txq->ps_id);
+ }
+ }
+#endif
+
+ if (!retry) {
+ /* add buffer in the sk_list */
+ skb_queue_tail(&txq->sk_list, skb);
+ } else {
+ if (txq->last_retry_skb)
+ rwnx_skb_append(txq->last_retry_skb, skb, &txq->sk_list);
+ else
+ skb_queue_head(&txq->sk_list, skb);
+
+ txq->last_retry_skb = skb;
+ txq->nb_retry++;
+ }
+#ifdef CREATE_TRACE_POINTS
+ trace_txq_queue_skb(skb, txq, retry);
+#endif
+ /* Flowctrl corresponding netdev queue if needed */
+#ifdef CONFIG_RWNX_FULLMAC
+#ifndef CONFIG_ONE_TXQ
+ /* If too many buffer are queued for this TXQ stop netdev queue */
+ spin_lock_irqsave(&rwnx_hw->usbdev->tx_flow_lock, flags);
+ if ((txq->ndev_idx != NDEV_NO_TXQ) && !rwnx_hw->usbdev->tbusy && ((skb_queue_len(&txq->sk_list) > RWNX_NDEV_FLOW_CTRL_STOP))) {
+ txq->status |= RWNX_TXQ_NDEV_FLOW_CTRL;
+ netif_stop_subqueue(txq->ndev, txq->ndev_idx);
+#ifdef CREATE_TRACE_POINTS
+ trace_txq_flowctrl_stop(txq);
+#endif
+ }
+ spin_unlock_irqrestore(&rwnx_hw->usbdev->tx_flow_lock, flags);
+
+#endif /* CONFIG_ONE_TXQ */
+#else /* ! CONFIG_RWNX_FULLMAC */
+
+ if (!retry && ++txq->hwq->len == txq->hwq->len_stop) {
+#ifdef CREATE_TRACE_POINTS
+ trace_hwq_flowctrl_stop(txq->hwq->id);
+#endif
+ ieee80211_stop_queue(rwnx_hw->hw, txq->hwq->id);
+ rwnx_hw->stats.queues_stops++;
+ }
+
+#endif /* CONFIG_RWNX_FULLMAC */
+
+ /* add it in the hwq list if not stopped and not yet present */
+ if (!rwnx_txq_is_stopped(txq)) {
+ rwnx_txq_add_to_hw_list(txq);
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * rwnx_txq_confirm_any - Process buffer confirmed by fw
+ *
+ * @rwnx_hw: Driver main data
+ * @txq: TX Queue
+ * @hwq: HW Queue
+ * @sw_txhdr: software descriptor of the confirmed packet
+ *
+ * Process a buffer returned by the fw. It doesn't check buffer status
+ * and only does systematic counter update:
+ * - hw credit
+ * - buffer pushed to fw
+ *
+ * To be called with tx_lock hold
+ */
+void rwnx_txq_confirm_any(struct rwnx_hw *rwnx_hw, struct rwnx_txq *txq,
+ struct rwnx_hwq *hwq, struct rwnx_sw_txhdr *sw_txhdr)
+{
+ int user = 0;
+
+#ifdef CONFIG_RWNX_MUMIMO_TX
+ int group_id;
+
+ user = RWNX_MUMIMO_INFO_POS_ID(sw_txhdr->desc.host.mumimo_info);
+ group_id = RWNX_MUMIMO_INFO_GROUP_ID(sw_txhdr->desc.host.mumimo_info);
+
+ if ((txq->idx != TXQ_INACTIVE) &&
+ (txq->pkt_pushed[user] == 1) &&
+ (txq->status & RWNX_TXQ_STOP_MU_POS))
+ rwnx_txq_start(txq, RWNX_TXQ_STOP_MU_POS);
+
+#endif /* CONFIG_RWNX_MUMIMO_TX */
+
+ if (txq->pkt_pushed[user])
+ txq->pkt_pushed[user]--;
+
+ hwq->need_processing = true;
+ rwnx_hw->stats.cfm_balance[hwq->id]--;
+}
+
+/******************************************************************************
+ * HWQ processing
+ *****************************************************************************/
+static inline
+bool rwnx_txq_take_mu_lock(struct rwnx_hw *rwnx_hw)
+{
+ bool res = false;
+#ifdef CONFIG_RWNX_MUMIMO_TX
+ if (rwnx_hw->mod_params->mutx)
+ res = (down_trylock(&rwnx_hw->mu.lock) == 0);
+#endif /* CONFIG_RWNX_MUMIMO_TX */
+ return res;
+}
+
+static inline
+void rwnx_txq_release_mu_lock(struct rwnx_hw *rwnx_hw)
+{
+#ifdef CONFIG_RWNX_MUMIMO_TX
+ up(&rwnx_hw->mu.lock);
+#endif /* CONFIG_RWNX_MUMIMO_TX */
+}
+
+static inline
+void rwnx_txq_set_mu_info(struct rwnx_hw *rwnx_hw, struct rwnx_txq *txq,
+ int group_id, int pos)
+{
+#ifdef CONFIG_RWNX_MUMIMO_TX
+ trace_txq_select_mu_group(txq, group_id, pos);
+ if (group_id) {
+ txq->mumimo_info = group_id | (pos << 6);
+ rwnx_mu_set_active_group(rwnx_hw, group_id);
+ } else
+ txq->mumimo_info = 0;
+#endif /* CONFIG_RWNX_MUMIMO_TX */
+}
+
+static inline
+s8 rwnx_txq_get_credits(struct rwnx_txq *txq)
+{
+ s8 cred = txq->credits;
+ /* if destination is in PS mode, push_limit indicates the maximum
+ number of packet that can be pushed on this txq. */
+ if (txq->push_limit && (cred > txq->push_limit)) {
+ cred = txq->push_limit;
+ }
+ return cred;
+}
+
+/**
+ * skb_queue_extract - Extract buffer from skb list
+ *
+ * @list: List of skb to extract from
+ * @head: List of skb to append to
+ * @nb_elt: Number of skb to extract
+ *
+ * extract the first @nb_elt of @list and append them to @head
+ * It is assume that:
+ * - @list contains more that @nb_elt
+ * - There is no need to take @list nor @head lock to modify them
+ */
+static inline void skb_queue_extract(struct sk_buff_head *list,
+ struct sk_buff_head *head, int nb_elt)
+{
+ int i;
+ struct sk_buff *first, *last, *ptr;
+
+ first = ptr = list->next;
+ for (i = 0; i < nb_elt; i++) {
+ ptr = ptr->next;
+ }
+ last = ptr->prev;
+
+ /* unlink nb_elt in list */
+ list->qlen -= nb_elt;
+ list->next = ptr;
+ ptr->prev = (struct sk_buff *)list;
+
+ /* append nb_elt at end of head */
+ head->qlen += nb_elt;
+ last->next = (struct sk_buff *)head;
+ head->prev->next = first;
+ first->prev = head->prev;
+ head->prev = last;
+}
+
+
+#ifdef CONFIG_MAC80211_TXQ
+/**
+ * rwnx_txq_mac80211_dequeue - Dequeue buffer from mac80211 txq and
+ * add them to push list
+ *
+ * @rwnx_hw: Main driver data
+ * @sk_list: List of buffer to push (initialized without lock)
+ * @txq: TXQ to dequeue buffers from
+ * @max: Max number of buffer to dequeue
+ *
+ * Dequeue buffer from mac80211 txq, prepare them for transmission and chain them
+ * to the list of buffer to push.
+ *
+ * @return true if no more buffer are queued in mac80211 txq and false otherwise.
+ */
+static bool rwnx_txq_mac80211_dequeue(struct rwnx_hw *rwnx_hw,
+ struct sk_buff_head *sk_list,
+ struct rwnx_txq *txq, int max)
+{
+ struct ieee80211_txq *mac_txq;
+ struct sk_buff *skb;
+ unsigned long mac_txq_len;
+
+ if (txq->nb_ready_mac80211 == NOT_MAC80211_TXQ)
+ return true;
+
+ mac_txq = container_of((void *)txq, struct ieee80211_txq, drv_priv);
+
+ for (; max > 0; max--) {
+ skb = rwnx_tx_dequeue_prep(rwnx_hw, mac_txq);
+ if (skb == NULL)
+ return true;
+
+ __skb_queue_tail(sk_list, skb);
+ }
+
+ /* re-read mac80211 txq current length.
+ It is mainly for debug purpose to trace dropped packet. There is no
+ problems to have nb_ready_mac80211 != actual mac80211 txq length */
+ ieee80211_txq_get_depth(mac_txq, &mac_txq_len, NULL);
+ if (txq->nb_ready_mac80211 > mac_txq_len)
+ trace_txq_drop(txq, txq->nb_ready_mac80211 - mac_txq_len);
+ txq->nb_ready_mac80211 = mac_txq_len;
+
+ return (txq->nb_ready_mac80211 == 0);
+}
+#endif
+
+/**
+ * rwnx_txq_get_skb_to_push - Get list of buffer to push for one txq
+ *
+ * @rwnx_hw: main driver data
+ * @hwq: HWQ on wich buffers will be pushed
+ * @txq: TXQ to get buffers from
+ * @user: user postion to use
+ * @sk_list_push: list to update
+ *
+ *
+ * This function will returned a list of buffer to push for one txq.
+ * It will take into account the number of credit of the HWQ for this user
+ * position and TXQ (and push_limit).
+ * This allow to get a list that can be pushed without having to test for
+ * hwq/txq status after each push
+ *
+ * If a MU group has been selected for this txq, it will also update the
+ * counter for the group
+ *
+ * @return true if txq no longer have buffer ready after the ones returned.
+ * false otherwise
+ */
+static
+bool rwnx_txq_get_skb_to_push(struct rwnx_hw *rwnx_hw, struct rwnx_hwq *hwq,
+ struct rwnx_txq *txq, int user,
+ struct sk_buff_head *sk_list_push)
+{
+ int nb_ready = skb_queue_len(&txq->sk_list);
+ int credits = rwnx_txq_get_credits(txq);
+ bool res = false;
+
+ __skb_queue_head_init(sk_list_push);
+
+ if (credits >= nb_ready) {
+ skb_queue_splice_init(&txq->sk_list, sk_list_push);
+#ifdef CONFIG_MAC80211_TXQ
+ res = rwnx_txq_mac80211_dequeue(rwnx_hw, sk_list_push, txq, credits - nb_ready);
+ credits = skb_queue_len(sk_list_push);
+#else
+ res = true;
+ credits = nb_ready;
+#endif
+ } else {
+ skb_queue_extract(&txq->sk_list, sk_list_push, credits);
+
+ /* When processing PS service period (i.e. push_limit != 0), no longer
+ process this txq if the buffers extracted will complete the SP for
+ this txq */
+ if (txq->push_limit && (credits == txq->push_limit))
+ res = true;
+ }
+
+ rwnx_mu_set_active_sta(rwnx_hw, rwnx_txq_2_sta(txq), credits);
+
+ return res;
+}
+
+/**
+ * rwnx_txq_select_user - Select User queue for a txq
+ *
+ * @rwnx_hw: main driver data
+ * @mu_lock: true is MU lock is taken
+ * @txq: TXQ to select MU group for
+ * @hwq: HWQ for the TXQ
+ * @user: Updated with user position selected
+ *
+ * @return false if it is no possible to process this txq.
+ * true otherwise
+ *
+ * This function selects the MU group to use for a TXQ.
+ * The selection is done as follow:
+ *
+ * - return immediately for STA that don't belongs to any group and select
+ * group 0 / user 0
+ *
+ * - If MU tx is disabled (by user mutx_on, or because mu group are being
+ * updated !mu_lock), select group 0 / user 0
+ *
+ * - Use the best group selected by @rwnx_mu_group_sta_select.
+ *
+ * Each time a group is selected (except for the first case where sta
+ * doesn't belongs to a MU group), the function checks that no buffer is
+ * pending for this txq on another user position. If this is the case stop
+ * the txq (RWNX_TXQ_STOP_MU_POS) and return false.
+ *
+ */
+static
+bool rwnx_txq_select_user(struct rwnx_hw *rwnx_hw, bool mu_lock,
+ struct rwnx_txq *txq, struct rwnx_hwq *hwq, int *user)
+{
+ int pos = 0;
+#ifdef CONFIG_RWNX_MUMIMO_TX
+ int id, group_id = 0;
+ struct rwnx_sta *sta = rwnx_txq_2_sta(txq);
+
+ /* for sta that belong to no group return immediately */
+ if (!sta || !sta->group_info.cnt)
+ goto end;
+
+ /* If MU is disabled, need to check user */
+ if (!rwnx_hw->mod_params->mutx_on || !mu_lock)
+ goto check_user;
+
+ /* Use the "best" group selected */
+ group_id = sta->group_info.group;
+
+ if (group_id > 0)
+ pos = rwnx_mu_group_sta_get_pos(rwnx_hw, sta, group_id);
+
+ check_user:
+ /* check that we can push on this user position */
+#if CONFIG_USER_MAX == 2
+ id = (pos + 1) & 0x1;
+ if (txq->pkt_pushed[id]) {
+ rwnx_txq_stop(txq, RWNX_TXQ_STOP_MU_POS);
+ return false;
+ }
+
+#else
+ for (id = 0 ; id < CONFIG_USER_MAX ; id++) {
+ if (id != pos && txq->pkt_pushed[id]) {
+ rwnx_txq_stop(txq, RWNX_TXQ_STOP_MU_POS);
+ return false;
+ }
+ }
+#endif
+
+ end:
+ rwnx_txq_set_mu_info(rwnx_hw, txq, group_id, pos);
+#endif /* CONFIG_RWNX_MUMIMO_TX */
+
+ *user = pos;
+ return true;
+}
+
+
+/**
+ * rwnx_hwq_process - Process one HW queue list
+ *
+ * @rwnx_hw: Driver main data
+ * @hw_queue: HW queue index to process
+ *
+ * The function will iterate over all the TX queues linked in this HW queue
+ * list. For each TX queue, push as many buffers as possible in the HW queue.
+ * (NB: TX queue have at least 1 buffer, otherwise it wouldn't be in the list)
+ * - If TX queue no longer have buffer, remove it from the list and check next
+ * TX queue
+ * - If TX queue no longer have credits or has a push_limit (PS mode) and it
+ * is reached , remove it from the list and check next TX queue
+ * - If HW queue is full, update list head to start with the next TX queue on
+ * next call if current TX queue already pushed "too many" pkt in a row, and
+ * return
+ *
+ * To be called when HW queue list is modified:
+ * - when a buffer is pushed on a TX queue
+ * - when new credits are received
+ * - when a STA returns from Power Save mode or receives traffic request.
+ * - when Channel context change
+ *
+ * To be called with tx_lock hold
+ */
+#define ALL_HWQ_MASK ((1 << CONFIG_USER_MAX) - 1)
+
+void rwnx_hwq_process(struct rwnx_hw *rwnx_hw, struct rwnx_hwq *hwq)
+{
+ struct rwnx_txq *txq, *next;
+ int user, credit_map = 0;
+ bool mu_enable;
+#ifndef CONFIG_ONE_TXQ
+ unsigned long flags;
+#endif
+#ifdef CREATE_TRACE_POINTS
+ trace_process_hw_queue(hwq);
+#endif
+ hwq->need_processing = false;
+
+ mu_enable = rwnx_txq_take_mu_lock(rwnx_hw);
+ if (!mu_enable)
+ credit_map = ALL_HWQ_MASK - 1;
+
+ list_for_each_entry_safe(txq, next, &hwq->list, sched_list) {
+ struct rwnx_txhdr *txhdr = NULL;
+ struct sk_buff_head sk_list_push;
+ struct sk_buff *skb;
+ bool txq_empty;
+#ifdef CREATE_TRACE_POINTS
+ trace_process_txq(txq);
+#endif
+ /* sanity check for debug */
+ BUG_ON(!(txq->status & RWNX_TXQ_IN_HWQ_LIST));
+ if(txq->idx == TXQ_INACTIVE){
+ printk("%s txq->idx == TXQ_INACTIVE \r\n", __func__);
+ continue;
+ }
+ BUG_ON(txq->idx == TXQ_INACTIVE);
+ BUG_ON(txq->credits <= 0);
+ BUG_ON(!rwnx_txq_skb_ready(txq));
+
+ if (!rwnx_txq_select_user(rwnx_hw, mu_enable, txq, hwq, &user))
+ continue;
+
+ txq_empty = rwnx_txq_get_skb_to_push(rwnx_hw, hwq, txq, user,
+ &sk_list_push);
+
+ while ((skb = __skb_dequeue(&sk_list_push)) != NULL) {
+ txhdr = (struct rwnx_txhdr *)skb->data;
+ rwnx_tx_push(rwnx_hw, txhdr, 0);
+ }
+
+ if (txq_empty) {
+ rwnx_txq_del_from_hw_list(txq);
+ txq->pkt_sent = 0;
+ } else if (rwnx_txq_is_scheduled(txq)) {
+ /* txq not empty,
+ - To avoid starving need to process other txq in the list
+ - For better aggregation, need to send "as many consecutive
+ pkt as possible" for he same txq
+ ==> Add counter to trigger txq switch
+ */
+ if (txq->pkt_sent > hwq->size) {
+ txq->pkt_sent = 0;
+ list_rotate_left(&hwq->list);
+ }
+ }
+
+#ifdef CONFIG_RWNX_FULLMAC
+ /* Unable to complete PS traffic request because of hwq credit */
+ if (txq->push_limit && txq->sta) {
+ if (txq->ps_id == LEGACY_PS_ID) {
+ /* for legacy PS abort SP and wait next ps-poll */
+ txq->sta->ps.sp_cnt[txq->ps_id] -= txq->push_limit;
+ txq->push_limit = 0;
+ }
+ /* for u-apsd need to complete the SP to send EOSP frame */
+ }
+#ifndef CONFIG_ONE_TXQ
+ /* restart netdev queue if number of queued buffer is below threshold */
+ spin_lock_irqsave(&rwnx_hw->usbdev->tx_flow_lock, flags);
+ if (unlikely(txq->status & RWNX_TXQ_NDEV_FLOW_CTRL) &&
+ skb_queue_len(&txq->sk_list) < RWNX_NDEV_FLOW_CTRL_RESTART) {
+ txq->status &= ~RWNX_TXQ_NDEV_FLOW_CTRL;
+ if(!rwnx_hw->usbdev->tbusy)
+ netif_wake_subqueue(txq->ndev, txq->ndev_idx);
+#ifdef CREATE_TRACE_POINTS
+ trace_txq_flowctrl_restart(txq);
+#endif
+ }
+ spin_unlock_irqrestore(&rwnx_hw->usbdev->tx_flow_lock, flags);
+#endif /* CONFIG_ONE_TXQ */
+#endif /* CONFIG_RWNX_FULLMAC */
+ }
+
+
+ if (mu_enable)
+ rwnx_txq_release_mu_lock(rwnx_hw);
+}
+
+/**
+ * rwnx_hwq_process_all - Process all HW queue list
+ *
+ * @rwnx_hw: Driver main data
+ *
+ * Loop over all HWQ, and process them if needed
+ * To be called with tx_lock hold
+ */
+void rwnx_hwq_process_all(struct rwnx_hw *rwnx_hw)
+{
+ int id;
+
+ rwnx_mu_group_sta_select(rwnx_hw);
+
+ for (id = ARRAY_SIZE(rwnx_hw->hwq) - 1; id >= 0 ; id--) {
+ if (rwnx_hw->hwq[id].need_processing) {
+ rwnx_hwq_process(rwnx_hw, &rwnx_hw->hwq[id]);
+ }
+ }
+}
+
+/**
+ * rwnx_hwq_init - Initialize all hwq structures
+ *
+ * @rwnx_hw: Driver main data
+ *
+ */
+void rwnx_hwq_init(struct rwnx_hw *rwnx_hw)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(rwnx_hw->hwq); i++) {
+ struct rwnx_hwq *hwq = &rwnx_hw->hwq[i];
+
+ hwq->id = i;
+ hwq->size = nx_txdesc_cnt[i];
+ INIT_LIST_HEAD(&hwq->list);
+
+ }
+}
diff --git a/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_txq.h b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_txq.h
new file mode 100644
index 000000000000..c019c6a6f171
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_txq.h
@@ -0,0 +1,397 @@
+/**
+ ****************************************************************************************
+ *
+ * @file rwnx_txq.h
+ *
+ * Copyright (C) RivieraWaves 2012-2019
+ *
+ ****************************************************************************************
+ */
+#ifndef _RWNX_TXQ_H_
+#define _RWNX_TXQ_H_
+
+#include <linux/types.h>
+#include <linux/bitops.h>
+#include <linux/ieee80211.h>
+
+#include <net/mac80211.h>
+
+#ifdef CONFIG_RWNX_FULLMAC
+/**
+ * Fullmac TXQ configuration:
+ * - STA: 1 TXQ per TID (limited to 8)
+ * 1 TXQ for bufferable MGT frames
+ * - VIF: 1 TXQ for Multi/Broadcast +
+ * 1 TXQ for MGT for unknown STAs or non-bufferable MGT frames
+ * - 1 TXQ for offchannel transmissions
+ *
+ *
+ * Txq mapping looks like
+ * for NX_REMOTE_STA_MAX=10 and NX_VIRT_DEV_MAX=4
+ *
+ * | TXQ | NDEV_ID | VIF | STA | TID | HWQ |
+ * |-----+---------+-----+-------+------+-----|-
+ * | 0 | 0 | | 0 | 0 | 1 | 9 TXQ per STA
+ * | 1 | 1 | | 0 | 1 | 0 | (8 data + 1 mgmt)
+ * | 2 | 2 | | 0 | 2 | 0 |
+ * | 3 | 3 | | 0 | 3 | 1 |
+ * | 4 | 4 | | 0 | 4 | 2 |
+ * | 5 | 5 | | 0 | 5 | 2 |
+ * | 6 | 6 | | 0 | 6 | 3 |
+ * | 7 | 7 | | 0 | 7 | 3 |
+ * | 8 | N/A | | 0 | MGMT | 3 |
+ * |-----+---------+-----+-------+------+-----|-
+ * | ... | | | | | | Same for all STAs
+ * |-----+---------+-----+-------+------+-----|-
+ * | 90 | 80 | 0 | BC/MC | 0 | 1/4 | 1 TXQ for BC/MC per VIF
+ * | ... | | | | | |
+ * | 93 | 80 | 3 | BC/MC | 0 | 1/4 |
+ * |-----+---------+-----+-------+------+-----|-
+ * | 94 | N/A | 0 | N/A | MGMT | 3 | 1 TXQ for unknown STA per VIF
+ * | ... | | | | | |
+ * | 97 | N/A | 3 | N/A | MGMT | 3 |
+ * |-----+---------+-----+-------+------+-----|-
+ * | 98 | N/A | | N/A | MGMT | 3 | 1 TXQ for offchannel frame
+ */
+#define NX_NB_TID_PER_STA 8
+#define NX_NB_TXQ_PER_STA (NX_NB_TID_PER_STA + 1)
+#define NX_NB_TXQ_PER_VIF 2
+#define NX_NB_TXQ ((NX_NB_TXQ_PER_STA * NX_REMOTE_STA_MAX) + \
+ (NX_NB_TXQ_PER_VIF * NX_VIRT_DEV_MAX) + 1)
+
+#define NX_FIRST_VIF_TXQ_IDX (NX_REMOTE_STA_MAX * NX_NB_TXQ_PER_STA)
+#define NX_FIRST_BCMC_TXQ_IDX NX_FIRST_VIF_TXQ_IDX
+#define NX_FIRST_UNK_TXQ_IDX (NX_FIRST_BCMC_TXQ_IDX + NX_VIRT_DEV_MAX)
+
+#define NX_FIRST_VIF_TXQ_IDX_FOR_OLD_IC (NX_REMOTE_STA_MAX_FOR_OLD_IC * NX_NB_TXQ_PER_STA)
+#define NX_FIRST_BCMC_TXQ_IDX_FOR_OLD_IC NX_FIRST_VIF_TXQ_IDX_FOR_OLD_IC
+#define NX_FIRST_UNK_TXQ_IDX_FOR_OLD_IC (NX_FIRST_BCMC_TXQ_IDX_FOR_OLD_IC + NX_VIRT_DEV_MAX)
+
+
+#define NX_OFF_CHAN_TXQ_IDX (NX_FIRST_VIF_TXQ_IDX + \
+ (NX_VIRT_DEV_MAX * NX_NB_TXQ_PER_VIF))
+
+#define NX_OFF_CHAN_TXQ_IDX_FOR_OLD_IC (NX_FIRST_VIF_TXQ_IDX_FOR_OLD_IC + \
+ (NX_VIRT_DEV_MAX * NX_NB_TXQ_PER_VIF))
+
+
+#define NX_BCMC_TXQ_TYPE 0
+#define NX_UNK_TXQ_TYPE 1
+
+/**
+ * Each data TXQ is a netdev queue. TXQ to send MGT are not data TXQ as
+ * they did not recieved buffer from netdev interface.
+ * Need to allocate the maximum case.
+ * AP : all STAs + 1 BC/MC
+ */
+#define NX_NB_NDEV_TXQ ((NX_NB_TID_PER_STA * NX_REMOTE_STA_MAX) + 1 )
+#define NX_NB_NDEV_TXQ_FOR_OLD_IC ((NX_NB_TID_PER_STA * NX_REMOTE_STA_MAX_FOR_OLD_IC) + 1)
+
+#define NX_BCMC_TXQ_NDEV_IDX (NX_NB_TID_PER_STA * NX_REMOTE_STA_MAX)
+#define NX_BCMC_TXQ_NDEV_IDX_FOR_OLD_IC (NX_NB_TID_PER_STA * NX_REMOTE_STA_MAX_FOR_OLD_IC)
+#define NX_STA_NDEV_IDX(tid, sta_idx) ((tid) + (sta_idx) * NX_NB_TID_PER_STA)
+#define NDEV_NO_TXQ 0xffff
+#if (NX_NB_NDEV_TXQ >= NDEV_NO_TXQ)
+#error("Need to increase struct rwnx_txq->ndev_idx size")
+#endif
+
+/* stop netdev queue when number of queued buffers if greater than this */
+#define RWNX_NDEV_FLOW_CTRL_STOP 64
+/* restart netdev queue when number of queued buffers is lower than this */
+#define RWNX_NDEV_FLOW_CTRL_RESTART 64
+
+#endif /* CONFIG_RWNX_FULLMAC */
+
+#define TXQ_INACTIVE 0xffff
+#if (NX_NB_TXQ >= TXQ_INACTIVE)
+#error("Need to increase struct rwnx_txq->idx size")
+#endif
+
+#define NX_TXQ_INITIAL_CREDITS 64
+
+/**
+ * TXQ tid sorted by decreasing priority
+ */
+extern const int nx_tid_prio[NX_NB_TID_PER_STA];
+
+/**
+ * struct rwnx_hwq - Structure used to save information relative to
+ * an AC TX queue (aka HW queue)
+ * @list: List of TXQ, that have buffers ready for this HWQ
+ * @credits: available credit for the queue (i.e. nb of buffers that
+ * can be pushed to FW )
+ * @id Id of the HWQ among RWNX_HWQ_....
+ * @size size of the queue
+ * @need_processing Indicate if hwq should be processed
+ * @len number of packet ready to be pushed to fw for this HW queue
+ * @len_stop threshold to stop mac80211(i.e. netdev) queues. Stop queue when
+ * driver has more than @len_stop packets ready.
+ * @len_start threshold to wake mac8011 queues. Wake queue when driver has
+ * less than @len_start packets ready.
+ */
+struct rwnx_hwq {
+ struct list_head list;
+ u8 size;
+ u8 id;
+ bool need_processing;
+};
+
+/**
+ * enum rwnx_push_flags - Flags of pushed buffer
+ *
+ * @RWNX_PUSH_RETRY Pushing a buffer for retry
+ * @RWNX_PUSH_IMMEDIATE Pushing a buffer without queuing it first
+ */
+enum rwnx_push_flags {
+ RWNX_PUSH_RETRY = BIT(0),
+ RWNX_PUSH_IMMEDIATE = BIT(1),
+};
+
+/**
+ * enum rwnx_txq_flags - TXQ status flag
+ *
+ * @RWNX_TXQ_IN_HWQ_LIST: The queue is scheduled for transmission
+ * @RWNX_TXQ_STOP_FULL: No more credits for the queue
+ * @RWNX_TXQ_STOP_CSA: CSA is in progress
+ * @RWNX_TXQ_STOP_STA_PS: Destiniation sta is currently in power save mode
+ * @RWNX_TXQ_STOP_VIF_PS: Vif owning this queue is currently in power save mode
+ * @RWNX_TXQ_STOP_CHAN: Channel of this queue is not the current active channel
+ * @RWNX_TXQ_STOP_MU_POS: TXQ is stopped waiting for all the buffers pushed to
+ * fw to be confirmed
+ * @RWNX_TXQ_STOP: All possible reason to have a txq stopped
+ * @RWNX_TXQ_NDEV_FLOW_CTRL: associated netdev queue is currently stopped.
+ * Note: when a TXQ is flowctrl it is NOT stopped
+ */
+enum rwnx_txq_flags {
+ RWNX_TXQ_IN_HWQ_LIST = BIT(0),
+ RWNX_TXQ_STOP_FULL = BIT(1),
+ RWNX_TXQ_STOP_CSA = BIT(2),
+ RWNX_TXQ_STOP_STA_PS = BIT(3),
+ RWNX_TXQ_STOP_VIF_PS = BIT(4),
+ RWNX_TXQ_STOP_CHAN = BIT(5),
+ RWNX_TXQ_STOP_MU_POS = BIT(6),
+ RWNX_TXQ_STOP = (RWNX_TXQ_STOP_FULL | RWNX_TXQ_STOP_CSA |
+ RWNX_TXQ_STOP_STA_PS | RWNX_TXQ_STOP_VIF_PS |
+ RWNX_TXQ_STOP_CHAN) ,
+ RWNX_TXQ_NDEV_FLOW_CTRL = BIT(7),
+};
+
+
+/**
+ * struct rwnx_txq - Structure used to save information relative to
+ * a RA/TID TX queue
+ *
+ * @idx: Unique txq idx. Set to TXQ_INACTIVE if txq is not used.
+ * @status: bitfield of @rwnx_txq_flags.
+ * @credits: available credit for the queue (i.e. nb of buffers that
+ * can be pushed to FW).
+ * @pkt_sent: number of consecutive pkt sent without leaving HW queue list
+ * @pkt_pushed: number of pkt currently pending for transmission confirmation
+ * @sched_list: list node for HW queue schedule list (rwnx_hwq.list)
+ * @sk_list: list of buffers to push to fw
+ * @last_retry_skb: pointer on the last skb in @sk_list that is a retry.
+ * (retry skb are stored at the beginning of the list)
+ * NULL if no retry skb is queued in @sk_list
+ * @nb_retry: Number of retry packet queued.
+ * @hwq: Pointer on the associated HW queue.
+ * @push_limit: number of packet to push before removing the txq from hwq list.
+ * (we always have push_limit < skb_queue_len(sk_list))
+ * @tid: TID
+ *
+ * FULLMAC specific
+ * @ps_id: Index to use for Power save mode (LEGACY or UAPSD)
+ * @ndev_idx: txq idx from netdev point of view (0xFF for non netdev queue)
+ * @ndev: pointer to ndev of the corresponding vif
+ * @amsdu: pointer to rwnx_sw_txhdr of the first subframe of the A-MSDU.
+ * NULL if no A-MSDU frame is in construction
+ * @amsdu_len: Maximum size allowed for an A-MSDU. 0 means A-MSDU not allowed
+ */
+struct rwnx_txq {
+ u16 idx;
+ u8 status;
+ s8 credits;
+ u8 pkt_sent;
+ u8 pkt_pushed[CONFIG_USER_MAX];
+ struct list_head sched_list;
+ struct sk_buff_head sk_list;
+ struct sk_buff *last_retry_skb;
+ struct rwnx_hwq *hwq;
+ int nb_retry;
+ u8 push_limit;
+ u8 tid;
+#ifdef CONFIG_MAC80211_TXQ
+ unsigned long nb_ready_mac80211;
+#endif
+#ifdef CONFIG_RWNX_FULLMAC
+ struct rwnx_sta *sta;
+ u8 ps_id;
+ u16 ndev_idx;
+ struct net_device *ndev;
+#ifdef CONFIG_RWNX_AMSDUS_TX
+ struct rwnx_sw_txhdr *amsdu;
+ u16 amsdu_len;
+#endif /* CONFIG_RWNX_AMSDUS_TX */
+#endif /* CONFIG_RWNX_FULLMAC */
+#ifdef CONFIG_RWNX_MUMIMO_TX
+ u8 mumimo_info;
+#endif
+};
+
+struct rwnx_sta;
+struct rwnx_vif;
+struct rwnx_hw;
+struct rwnx_sw_txhdr;
+
+#ifdef CONFIG_RWNX_MUMIMO_TX
+#define RWNX_TXQ_GROUP_ID(txq) ((txq)->mumimo_info & 0x3f)
+#define RWNX_TXQ_POS_ID(txq) (((txq)->mumimo_info >> 6) & 0x3)
+#else
+#define RWNX_TXQ_GROUP_ID(txq) 0
+#define RWNX_TXQ_POS_ID(txq) 0
+#endif /* CONFIG_RWNX_MUMIMO_TX */
+
+static inline bool rwnx_txq_is_stopped(struct rwnx_txq *txq)
+{
+ return (txq->status & RWNX_TXQ_STOP);
+}
+
+static inline bool rwnx_txq_is_full(struct rwnx_txq *txq)
+{
+ return (txq->status & RWNX_TXQ_STOP_FULL);
+}
+
+static inline bool rwnx_txq_is_scheduled(struct rwnx_txq *txq)
+{
+ return (txq->status & RWNX_TXQ_IN_HWQ_LIST);
+}
+
+/**
+ * foreach_sta_txq - Macro to iterate over all TXQ of a STA in increasing
+ * TID order
+ *
+ * @sta: pointer to rwnx_sta
+ * @txq: pointer to rwnx_txq updated with the next TXQ at each iteration
+ * @tid: int updated with the TXQ tid at each iteration
+ * @rwnx_hw: main driver data
+ */
+#ifdef CONFIG_MAC80211_TXQ
+#define foreach_sta_txq(sta, txq, tid, rwnx_hw) \
+ for (tid = 0, txq = rwnx_txq_sta_get(sta, 0); \
+ tid < NX_NB_TXQ_PER_STA; \
+ tid++, txq = rwnx_txq_sta_get(sta, tid))
+
+#elif defined(CONFIG_RWNX_FULLMAC) /* CONFIG_RWNX_FULLMAC */
+#define foreach_sta_txq(sta, txq, tid, rwnx_hw) \
+ for (tid = 0, txq = rwnx_txq_sta_get(sta, 0, rwnx_hw); \
+ tid < (is_multicast_sta(sta->sta_idx) ? 1 : NX_NB_TXQ_PER_STA); \
+ tid++, txq++)
+
+#endif
+
+/**
+ * foreach_sta_txq_prio - Macro to iterate over all TXQ of a STA in
+ * decreasing priority order
+ *
+ * @sta: pointer to rwnx_sta
+ * @txq: pointer to rwnx_txq updated with the next TXQ at each iteration
+ * @tid: int updated with the TXQ tid at each iteration
+ * @i: int updated with ieration count
+ * @rwnx_hw: main driver data
+ *
+ * Note: For fullmac txq for mgmt frame is skipped
+ */
+#ifdef CONFIG_RWNX_FULLMAC
+#define foreach_sta_txq_prio(sta, txq, tid, i, rwnx_hw) \
+ for (i = 0, tid = nx_tid_prio[0], txq = rwnx_txq_sta_get(sta, tid, rwnx_hw); \
+ i < NX_NB_TID_PER_STA; \
+ i++, tid = nx_tid_prio[i], txq = rwnx_txq_sta_get(sta, tid, rwnx_hw))
+#endif
+
+/**
+ * foreach_vif_txq - Macro to iterate over all TXQ of a VIF (in AC order)
+ *
+ * @vif: pointer to rwnx_vif
+ * @txq: pointer to rwnx_txq updated with the next TXQ at each iteration
+ * @ac: int updated with the TXQ ac at each iteration
+ */
+#ifdef CONFIG_MAC80211_TXQ
+#define foreach_vif_txq(vif, txq, ac) \
+ for (ac = RWNX_HWQ_BK, txq = rwnx_txq_vif_get(vif, ac); \
+ ac < NX_NB_TXQ_PER_VIF; \
+ ac++, txq = rwnx_txq_vif_get(vif, ac))
+
+#else
+#define foreach_vif_txq(vif, txq, ac) \
+ for (ac = RWNX_HWQ_BK, txq = &vif->txqs[0]; \
+ ac < NX_NB_TXQ_PER_VIF; \
+ ac++, txq++)
+#endif
+
+#ifdef CONFIG_RWNX_FULLMAC
+struct rwnx_txq *rwnx_txq_sta_get(struct rwnx_sta *sta, u8 tid,
+ struct rwnx_hw * rwnx_hw);
+struct rwnx_txq *rwnx_txq_vif_get(struct rwnx_vif *vif, u8 type);
+#endif /* CONFIG_RWNX_FULLMAC */
+
+/**
+ * rwnx_txq_vif_get_status - return status bits related to the vif
+ *
+ * @rwnx_vif: Pointer to vif structure
+ */
+static inline u8 rwnx_txq_vif_get_status(struct rwnx_vif *rwnx_vif)
+{
+ struct rwnx_txq *txq = rwnx_txq_vif_get(rwnx_vif, 0);
+ return (txq->status & (RWNX_TXQ_STOP_CHAN | RWNX_TXQ_STOP_VIF_PS));
+}
+
+void rwnx_txq_vif_init(struct rwnx_hw * rwnx_hw, struct rwnx_vif *vif,
+ u8 status);
+void rwnx_txq_vif_deinit(struct rwnx_hw * rwnx_hw, struct rwnx_vif *vif);
+void rwnx_txq_sta_init(struct rwnx_hw * rwnx_hw, struct rwnx_sta *rwnx_sta,
+ u8 status);
+void rwnx_txq_sta_deinit(struct rwnx_hw * rwnx_hw, struct rwnx_sta *rwnx_sta);
+#ifdef CONFIG_RWNX_FULLMAC
+void rwnx_txq_unk_vif_init(struct rwnx_vif *rwnx_vif);
+void rwnx_txq_unk_vif_deinit(struct rwnx_vif *vif);
+void rwnx_txq_offchan_init(struct rwnx_vif *rwnx_vif);
+void rwnx_txq_offchan_deinit(struct rwnx_vif *rwnx_vif);
+void rwnx_txq_tdls_vif_init(struct rwnx_vif *rwnx_vif);
+void rwnx_txq_tdls_vif_deinit(struct rwnx_vif *vif);
+void rwnx_txq_tdls_sta_start(struct rwnx_vif *rwnx_vif, u16 reason,
+ struct rwnx_hw *rwnx_hw);
+void rwnx_txq_tdls_sta_stop(struct rwnx_vif *rwnx_vif, u16 reason,
+ struct rwnx_hw *rwnx_hw);
+#endif
+
+
+void rwnx_txq_add_to_hw_list(struct rwnx_txq *txq);
+void rwnx_txq_del_from_hw_list(struct rwnx_txq *txq);
+void rwnx_txq_stop(struct rwnx_txq *txq, u16 reason);
+void rwnx_txq_start(struct rwnx_txq *txq, u16 reason);
+void rwnx_txq_vif_start(struct rwnx_vif *vif, u16 reason,
+ struct rwnx_hw *rwnx_hw);
+void rwnx_txq_vif_stop(struct rwnx_vif *vif, u16 reason,
+ struct rwnx_hw *rwnx_hw);
+
+#ifdef CONFIG_RWNX_FULLMAC
+void rwnx_txq_sta_start(struct rwnx_sta *sta, u16 reason,
+ struct rwnx_hw *rwnx_hw);
+void rwnx_txq_sta_stop(struct rwnx_sta *sta, u16 reason,
+ struct rwnx_hw *rwnx_hw);
+void rwnx_txq_offchan_start(struct rwnx_hw *rwnx_hw);
+void rwnx_txq_sta_switch_vif(struct rwnx_sta *sta, struct rwnx_vif *old_vif,
+ struct rwnx_vif *new_vif);
+
+#endif /* CONFIG_RWNX_FULLMAC */
+
+int rwnx_txq_queue_skb(struct sk_buff *skb, struct rwnx_txq *txq,
+ struct rwnx_hw *rwnx_hw, bool retry);
+void rwnx_txq_confirm_any(struct rwnx_hw *rwnx_hw, struct rwnx_txq *txq,
+ struct rwnx_hwq *hwq, struct rwnx_sw_txhdr *sw_txhdr);
+
+
+void rwnx_hwq_init(struct rwnx_hw *rwnx_hw);
+void rwnx_hwq_process(struct rwnx_hw *rwnx_hw, struct rwnx_hwq *hwq);
+void rwnx_hwq_process_all(struct rwnx_hw *rwnx_hw);
+
+#endif /* _RWNX_TXQ_H_ */
diff --git a/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_utils.c b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_utils.c
new file mode 100644
index 000000000000..c78e58e52216
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_utils.c
@@ -0,0 +1,39 @@
+/**
+ * rwnx_utils.c
+ *
+ * IPC utility function definitions
+ *
+ * Copyright (C) RivieraWaves 2012-2019
+ */
+#include "rwnx_utils.h"
+#include "rwnx_defs.h"
+#include "rwnx_rx.h"
+#include "rwnx_tx.h"
+#include "rwnx_msg_rx.h"
+#include "rwnx_debugfs.h"
+#include "rwnx_prof.h"
+#include "ipc_host.h"
+
+
+extern int get_testmode(void);
+extern void get_fw_path(char* fw_path);
+extern int testmode;
+extern char aic_fw_path[200];
+
+int rwnx_init_aic(struct rwnx_hw *rwnx_hw)
+{
+ RWNX_DBG(RWNX_FN_ENTRY_STR);
+#ifdef AICWF_SDIO_SUPPORT
+ aicwf_sdio_host_init(&(rwnx_hw->sdio_env), NULL, NULL, rwnx_hw);
+#else
+ aicwf_usb_host_init(&(rwnx_hw->usb_env), NULL, NULL, rwnx_hw);
+#endif
+ rwnx_cmd_mgr_init(rwnx_hw->cmd_mgr);
+
+ testmode = get_testmode();
+ memset(aic_fw_path, 0, 200);
+ get_fw_path(aic_fw_path);
+
+ return 0;
+}
+
diff --git a/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_utils.h b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_utils.h
new file mode 100644
index 000000000000..e41185bd4a88
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_utils.h
@@ -0,0 +1,142 @@
+/**
+ * rwnx_ipc_utils.h
+ *
+ * IPC utility function declarations
+ *
+ * Copyright (C) RivieraWaves 2012-2019
+ */
+#ifndef _RWNX_IPC_UTILS_H_
+#define _RWNX_IPC_UTILS_H_
+
+#include <linux/dma-mapping.h>
+#include <linux/dmapool.h>
+#include <linux/skbuff.h>
+
+#include "lmac_msg.h"
+#include "aicwf_debug.h"
+
+#ifdef ANDROID_PLATFORM
+#define HIGH_KERNEL_VERSION KERNEL_VERSION(5, 15, 41)
+#else
+#define HIGH_KERNEL_VERSION KERNEL_VERSION(6, 0, 0)
+#endif
+
+
+#if 0
+#ifdef CONFIG_RWNX_DBG
+/* #define RWNX_DBG(format, arg...) pr_warn(format, ## arg) */
+#define RWNX_DBG printk
+#else
+#define RWNX_DBG(a...) do {} while (0)
+#endif
+#endif
+
+
+
+enum rwnx_dev_flag {
+ RWNX_DEV_RESTARTING,
+ RWNX_DEV_STACK_RESTARTING,
+ RWNX_DEV_STARTED,
+};
+
+struct rwnx_hw;
+struct rwnx_sta;
+
+/**
+ * struct rwnx_ipc_elem - Generic IPC buffer of fixed size
+ *
+ * @addr: Host address of the buffer.
+ * @dma_addr: DMA address of the buffer.
+ */
+struct rwnx_ipc_elem {
+ void *addr;
+ dma_addr_t dma_addr;
+};
+
+/**
+ * struct rwnx_ipc_elem_pool - Generic pool of IPC buffers of fixed size
+ *
+ * @nb: Number of buffers currenlty allocated in the pool
+ * @buf: Array of buffers (size of array is @nb)
+ * @pool: DMA pool in which buffers have been allocated
+ */
+struct rwnx_ipc_elem_pool {
+ int nb;
+ struct rwnx_ipc_elem *buf;
+ struct dma_pool *pool;
+};
+
+/**
+ * struct rwnx_ipc_elem - Generic IPC buffer of variable size
+ *
+ * @addr: Host address of the buffer.
+ * @dma_addr: DMA address of the buffer.
+ * @size: Size, in bytes, of the buffer
+ */
+struct rwnx_ipc_elem_var {
+ void *addr;
+ dma_addr_t dma_addr;
+ size_t size;
+};
+
+/**
+ * struct rwnx_ipc_dbgdump_elem - IPC buffer for debug dump
+ *
+ * @mutex: Mutex to protect access to debug dump
+ * @buf: IPC buffer
+ */
+struct rwnx_ipc_dbgdump_elem {
+ struct mutex mutex;
+ struct rwnx_ipc_elem_var buf;
+};
+
+static const u32 rwnx_rxbuff_pattern = 0xCAFEFADE;
+
+/*
+ * Maximum Length of Radiotap header vendor specific data(in bytes)
+ */
+#define RADIOTAP_HDR_VEND_MAX_LEN 16
+
+/*
+ * Maximum Radiotap Header Length without vendor specific data (in bytes)
+ */
+#define RADIOTAP_HDR_MAX_LEN 80
+
+/*
+ * Unsupported HT Frame data length (in bytes)
+ */
+#define UNSUP_RX_VEC_DATA_LEN 2
+
+/**
+ * struct rwnx_ipc_skb_elem - IPC buffer for SKB element
+ *
+ * @skb: Pointer to the skb buffer allocated
+ * @dma_addr: DMA address of the data buffer fo skb
+ *
+ */
+struct rwnx_ipc_skb_elem {
+ struct sk_buff *skb;
+ dma_addr_t dma_addr;
+};
+
+#ifdef CONFIG_RWNX_FULLMAC
+
+/* Maximum number of rx buffer the fw may use at the same time */
+#define RWNX_RXBUFF_MAX (64 * NX_REMOTE_STA_MAX)
+
+/**
+ * struct rwnx_ipc_rxbuf_elems - IPC buffers for RX
+ *
+ * @skb: Array of buffer push to FW.
+ * @idx: Index of the last pushed skb.(Use to find the next free entry quicker)
+ *
+ * Note: contrary to softmac version, dma_addr are stored inside skb->cb.
+ * (cf &struct rwnx_skb_cb)
+ */
+struct rwnx_ipc_rxbuf_elems {
+ struct sk_buff *skb[RWNX_RXBUFF_MAX];
+ int idx;
+};
+
+#endif /* CONFIG_RWNX_FULLMAC */
+#endif /* _RWNX_IPC_UTILS_H_ */
diff --git a/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_v7.c b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_v7.c
new file mode 100644
index 000000000000..c8bcfe116a58
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_v7.c
@@ -0,0 +1,192 @@
+/**
+ ******************************************************************************
+ *
+ * @file rwnx_v7.c - Support for v7 platform
+ *
+ * Copyright (C) RivieraWaves 2012-2019
+ *
+ ******************************************************************************
+ */
+
+#include "rwnx_v7.h"
+#include "rwnx_defs.h"
+#include "rwnx_irqs.h"
+#include "reg_access.h"
+#include "hal_desc.h"
+
+struct rwnx_v7
+{
+ u8 *pci_bar0_vaddr;
+ u8 *pci_bar1_vaddr;
+};
+
+static int rwnx_v7_platform_enable(struct rwnx_hw *rwnx_hw)
+{
+ int ret;
+
+ /* sched_setscheduler on ONESHOT threaded irq handler for BCNs ? */
+ ret = request_irq(rwnx_hw->plat->pci_dev->irq, rwnx_irq_hdlr, 0,
+ "rwnx", rwnx_hw);
+ return ret;
+}
+
+static int rwnx_v7_platform_disable(struct rwnx_hw *rwnx_hw)
+{
+ free_irq(rwnx_hw->plat->pci_dev->irq, rwnx_hw);
+ return 0;
+}
+
+static void rwnx_v7_platform_deinit(struct rwnx_plat *rwnx_plat)
+{
+ #ifdef CONFIG_PCI
+ struct rwnx_v7 *rwnx_v7 = (struct rwnx_v7 *)rwnx_plat->priv;
+
+ pci_disable_device(rwnx_plat->pci_dev);
+ iounmap(rwnx_v7->pci_bar0_vaddr);
+ iounmap(rwnx_v7->pci_bar1_vaddr);
+ pci_release_regions(rwnx_plat->pci_dev);
+ pci_clear_master(rwnx_plat->pci_dev);
+ pci_disable_msi(rwnx_plat->pci_dev);
+ #endif
+ kfree(rwnx_plat);
+}
+
+static u8* rwnx_v7_get_address(struct rwnx_plat *rwnx_plat, int addr_name,
+ unsigned int offset)
+{
+ struct rwnx_v7 *rwnx_v7 = (struct rwnx_v7 *)rwnx_plat->priv;
+
+ if (WARN(addr_name >= RWNX_ADDR_MAX, "Invalid address %d", addr_name))
+ return NULL;
+
+ if (addr_name == RWNX_ADDR_CPU)
+ return rwnx_v7->pci_bar0_vaddr + offset;
+ else
+ return rwnx_v7->pci_bar1_vaddr + offset;
+}
+
+static void rwnx_v7_ack_irq(struct rwnx_plat *rwnx_plat)
+{
+
+}
+
+static const u32 rwnx_v7_config_reg[] = {
+ NXMAC_DEBUG_PORT_SEL_ADDR,
+ SYSCTRL_DIAG_CONF_ADDR,
+ SYSCTRL_PHYDIAG_CONF_ADDR,
+ SYSCTRL_RIUDIAG_CONF_ADDR,
+ RF_V7_DIAGPORT_CONF1_ADDR,
+};
+
+static const u32 rwnx_v7_he_config_reg[] = {
+ SYSCTRL_DIAG_CONF0,
+ SYSCTRL_DIAG_CONF1,
+ SYSCTRL_DIAG_CONF2,
+ SYSCTRL_DIAG_CONF3,
+};
+
+static int rwnx_v7_get_config_reg(struct rwnx_plat *rwnx_plat, const u32 **list)
+{
+ u32 fpga_sign;
+
+ if (!list)
+ return 0;
+
+ fpga_sign = RWNX_REG_READ(rwnx_plat, RWNX_ADDR_SYSTEM, SYSCTRL_SIGNATURE_ADDR);
+ if (__FPGA_TYPE(fpga_sign) == 0xc0ca) {
+ *list = rwnx_v7_he_config_reg;
+ return ARRAY_SIZE(rwnx_v7_he_config_reg);
+ } else {
+ *list = rwnx_v7_config_reg;
+ return ARRAY_SIZE(rwnx_v7_config_reg);
+ }
+}
+
+
+/**
+ * rwnx_v7_platform_init - Initialize the DINI platform
+ *
+ * @pci_dev PCI device
+ * @rwnx_plat Pointer on struct rwnx_stat * to be populated
+ *
+ * @return 0 on success, < 0 otherwise
+ *
+ * Allocate and initialize a rwnx_plat structure for the dini platform.
+ */
+int rwnx_v7_platform_init(struct pci_dev *pci_dev, struct rwnx_plat **rwnx_plat)
+{
+ struct rwnx_v7 *rwnx_v7;
+ u16 pci_cmd;
+ int ret = 0;
+
+ *rwnx_plat = kzalloc(sizeof(struct rwnx_plat) + sizeof(struct rwnx_v7),
+ GFP_KERNEL);
+ if (!*rwnx_plat)
+ return -ENOMEM;
+
+ rwnx_v7 = (struct rwnx_v7 *)(*rwnx_plat)->priv;
+
+ /* Hotplug fixups */
+ pci_read_config_word(pci_dev, PCI_COMMAND, &pci_cmd);
+ pci_cmd |= PCI_COMMAND_PARITY | PCI_COMMAND_SERR;
+ pci_write_config_word(pci_dev, PCI_COMMAND, pci_cmd);
+ pci_write_config_byte(pci_dev, PCI_CACHE_LINE_SIZE, L1_CACHE_BYTES >> 2);
+
+ if ((ret = pci_enable_device(pci_dev))) {
+ dev_err(&(pci_dev->dev), "pci_enable_device failed\n");
+ goto out_enable;
+ }
+
+ pci_set_master(pci_dev);
+
+ if ((ret = pci_request_regions(pci_dev, KBUILD_MODNAME))) {
+ dev_err(&(pci_dev->dev), "pci_request_regions failed\n");
+ goto out_request;
+ }
+
+ #ifdef CONFIG_PCI
+ if (pci_enable_msi(pci_dev))
+ {
+ dev_err(&(pci_dev->dev), "pci_enable_msi failed\n");
+ goto out_msi;
+
+ }
+ #endif
+
+ if (!(rwnx_v7->pci_bar0_vaddr = (u8 *)pci_ioremap_bar(pci_dev, 0))) {
+ dev_err(&(pci_dev->dev), "pci_ioremap_bar(%d) failed\n", 0);
+ ret = -ENOMEM;
+ goto out_bar0;
+ }
+ if (!(rwnx_v7->pci_bar1_vaddr = (u8 *)pci_ioremap_bar(pci_dev, 1))) {
+ dev_err(&(pci_dev->dev), "pci_ioremap_bar(%d) failed\n", 1);
+ ret = -ENOMEM;
+ goto out_bar1;
+ }
+
+ (*rwnx_plat)->enable = rwnx_v7_platform_enable;
+ (*rwnx_plat)->disable = rwnx_v7_platform_disable;
+ (*rwnx_plat)->deinit = rwnx_v7_platform_deinit;
+ (*rwnx_plat)->get_address = rwnx_v7_get_address;
+ (*rwnx_plat)->ack_irq = rwnx_v7_ack_irq;
+ (*rwnx_plat)->get_config_reg = rwnx_v7_get_config_reg;
+
+ return 0;
+
+ out_bar1:
+ iounmap(rwnx_v7->pci_bar0_vaddr);
+ out_bar0:
+#ifdef CONFIG_PCI
+ pci_disable_msi(pci_dev);
+ out_msi:
+#endif
+ pci_release_regions(pci_dev);
+ out_request:
+#ifdef CONFIG_PCI
+ pci_clear_master(pci_dev);
+#endif
+ pci_disable_device(pci_dev);
+ out_enable:
+ kfree(*rwnx_plat);
+ return ret;
+}
diff --git a/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_v7.h b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_v7.h
new file mode 100644
index 000000000000..8bb99df7b1c5
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_v7.h
@@ -0,0 +1,20 @@
+/**
+ ******************************************************************************
+ *
+ * @file rwnx_v7.h
+ *
+ * Copyright (C) RivieraWaves 2012-2019
+ *
+ ******************************************************************************
+ */
+
+#ifndef _RWNX_V7_H_
+#define _RWNX_V7_H_
+
+#include <linux/pci.h>
+#include "rwnx_platform.h"
+
+int rwnx_v7_platform_init(struct pci_dev *pci_dev,
+ struct rwnx_plat **rwnx_plat);
+
+#endif /* _RWNX_V7_H_ */
diff --git a/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_version.h b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_version.h
new file mode 100644
index 000000000000..8eec30b25e49
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_version.h
@@ -0,0 +1,11 @@
+#ifndef _RWNX_VERSION_H_
+#define _RWNX_VERSION_H_
+
+#include "rwnx_version_gen.h"
+
+static inline void rwnx_print_version(void)
+{
+ AICWFDBG(LOGINFO, RWNX_VERS_BANNER"\n");
+}
+
+#endif /* _RWNX_VERSION_H_ */
diff --git a/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_version_gen.h b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_version_gen.h
new file mode 100644
index 000000000000..6d51acfb5c5b
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic8800_fdrv/rwnx_version_gen.h
@@ -0,0 +1,5 @@
+#define RWNX_VERS_REV "1a4b0054d2M (master)"
+#define RWNX_VERS_MOD "6.4.3.0"
+#define RWNX_VERS_BANNER "rwnx v6.4.3.0 - 1a4b0054d2M (master)"
+#define RELEASE_DATE "2023_0707_1001"
+
diff --git a/drivers/net/wireless/aic8800/aic8800_fdrv/sdio_host.c b/drivers/net/wireless/aic8800/aic8800_fdrv/sdio_host.c
new file mode 100644
index 000000000000..59225b3afd3f
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic8800_fdrv/sdio_host.c
@@ -0,0 +1,142 @@
+/**
+ * sdio_host.c
+ *
+ * SDIO host function declarations
+ *
+ * Copyright (C) AICSemi 2018-2020
+ */
+
+
+#include "sdio_host.h"
+//#include "ipc_compat.h"
+#include "rwnx_tx.h"
+#include "rwnx_platform.h"
+
+/**
+ ****************************************************************************************
+ */
+void aicwf_sdio_host_init(struct sdio_host_env_tag *env,
+ void *cb,
+ void *shared_env_ptr,
+ void *pthis)
+{
+ // Reset the environments
+
+ // Reset the Host environment
+ memset(env, 0, sizeof(struct sdio_host_env_tag));
+ // Save the pointer to the register base
+ env->pthis = pthis;
+}
+
+/**
+ ****************************************************************************************
+ */
+volatile struct txdesc_host *aicwf_sdio_host_txdesc_get(struct sdio_host_env_tag *env, const int queue_idx)
+{
+ // struct ipc_shared_env_tag *shared_env_ptr = env->shared;
+ volatile struct txdesc_host *txdesc_free;
+ uint32_t used_idx = env->txdesc_used_idx[queue_idx];
+ uint32_t free_idx = env->txdesc_free_idx[queue_idx];
+
+ // ASSERT_ERR(queue_idx < SDIO_TXQUEUE_CNT);
+ // ASSERT_ERR((free_idx - used_idx) <= SDIO_TXDESC_CNT);
+
+ // Check if a free descriptor is available
+ if (free_idx != (used_idx + SDIO_TXDESC_CNT))
+ {
+ // Get the pointer to the first free descriptor
+ // txdesc_free = shared_env_ptr->txdesc[queue_idx] + (free_idx % IPC_TXDESC_CNT);
+ }
+ else
+ {
+ txdesc_free = NULL;
+ }
+
+ return txdesc_free;
+}
+
+/**
+ ****************************************************************************************
+ */
+void aicwf_sdio_host_txdesc_push(struct sdio_host_env_tag *env, const int queue_idx, const uint64_t host_id)
+{
+ //printk("push, %d, %d, 0x%llx \r\n", queue_idx, env->txdesc_free_idx[queue_idx], host_id);
+ // Save the host id in the environment
+ env->tx_host_id[queue_idx][env->txdesc_free_idx[queue_idx] % SDIO_TXDESC_CNT] = host_id;
+
+ // Increment the index
+ env->txdesc_free_idx[queue_idx]++;
+ if(env->txdesc_free_idx[queue_idx]==0x40000000)
+ env->txdesc_free_idx[queue_idx] = 0;
+}
+
+/**
+ ****************************************************************************************
+ */
+void aicwf_sdio_host_tx_cfm_handler(struct sdio_host_env_tag *env, u32 *data)
+{
+ u32 queue_idx = 0;// data[0];
+ //struct rwnx_hw *rwnx_hw = (struct rwnx_hw *)env->pthis;
+ struct sk_buff *skb = NULL;
+ struct rwnx_txhdr *txhdr;
+
+ // TX confirmation descriptors have been received
+ // REG_SW_SET_PROFILING(env->pthis, SW_PROF_IRQ_E2A_TXCFM);
+ //while (1)
+ {
+ // Get the used index and increase it. We do the increase before knowing if the
+ // current buffer is confirmed because the callback function may call the
+ // ipc_host_txdesc_get() in case flow control was enabled and the index has to be
+ // already at the good value to ensure that the test of FIFO full is correct
+ //uint32_t used_idx = env->txdesc_used_idx[queue_idx]++;
+ uint32_t used_idx = data[1];
+ uint32_t host_id = env->tx_host_id[queue_idx][used_idx % SDIO_TXDESC_CNT];
+
+ // Reset the host id in the array
+ env->tx_host_id[queue_idx][used_idx % SDIO_TXDESC_CNT] = 0;
+
+ // call the external function to indicate that a TX packet is freed
+ if (host_id == 0)
+ {
+ // No more confirmations, so put back the used index at its initial value
+ env->txdesc_used_idx[queue_idx] = used_idx;
+ printk("ERROR:No more confirmations\r\n");
+ //break;
+ }
+ // set the cfm status
+ skb = (struct sk_buff *)(uint32_t)host_id;
+ txhdr = (struct rwnx_txhdr *)skb->data;
+ txhdr->hw_hdr.cfm.status = (union rwnx_hw_txstatus)data[0];
+ printk("sdio_host_tx_cfm_handler:used_idx=%d, 0x%p, status=%x\r\n",used_idx, env->pthis, txhdr->hw_hdr.cfm.status.value);
+ //if (env->cb.send_data_cfm(env->pthis, host_id) != 0)
+ if (rwnx_txdatacfm(env->pthis, (void *)host_id) != 0)
+ {
+ // No more confirmations, so put back the used index at its initial value
+ env->txdesc_used_idx[queue_idx] = used_idx;
+ env->tx_host_id[queue_idx][used_idx % SDIO_TXDESC_CNT] = host_id;
+ // and exit the loop
+ printk("ERROR:rwnx_txdatacfm,\r\n");
+ // break;
+ }
+
+ }
+}
+
+int aicwf_rwnx_sdio_platform_init(struct aic_sdio_dev *sdiodev)
+{
+ struct rwnx_plat *rwnx_plat = NULL;
+ void *drvdata;
+ int ret = -ENODEV;
+
+ rwnx_plat = kzalloc(sizeof(struct rwnx_plat), GFP_KERNEL);
+
+ if (!rwnx_plat) {
+ return -ENOMEM;
+ }
+
+ rwnx_plat->sdiodev = sdiodev;
+ ret = rwnx_platform_init(rwnx_plat, &drvdata);
+
+ return ret;
+}
+
diff --git a/drivers/net/wireless/aic8800/aic8800_fdrv/sdio_host.h b/drivers/net/wireless/aic8800/aic8800_fdrv/sdio_host.h
new file mode 100644
index 000000000000..e20f1473f8d5
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic8800_fdrv/sdio_host.h
@@ -0,0 +1,42 @@
+/**
+ * sdio_host.h
+ *
+ * SDIO host function declarations
+ *
+ * Copyright (C) AICSemi 2018-2020
+ */
+
+
+#ifndef _SDIO_HOST_H_
+#define _SDIO_HOST_H_
+
+#include "lmac_types.h"
+#include "aicwf_sdio.h"
+
+#define SDIO_TXQUEUE_CNT NX_TXQ_CNT
+#define SDIO_TXDESC_CNT NX_TXDESC_CNT
+
+
+/// Definition of the IPC Host environment structure.
+struct sdio_host_env_tag
+{
+ // Index used that points to the first free TX desc
+ uint32_t txdesc_free_idx[SDIO_TXQUEUE_CNT];
+ // Index used that points to the first used TX desc
+ uint32_t txdesc_used_idx[SDIO_TXQUEUE_CNT];
+ // Array storing the currently pushed host ids, per IPC queue
+ uint64_t tx_host_id[SDIO_TXQUEUE_CNT][SDIO_TXDESC_CNT];
+
+ /// Pointer to the attached object (used in callbacks and register accesses)
+ void *pthis;
+};
+
+extern void aicwf_sdio_host_init(struct sdio_host_env_tag *env,
+ void *cb, void *shared_env_ptr, void *pthis);
+
+extern void aicwf_sdio_host_txdesc_push(struct sdio_host_env_tag *env, const int queue_idx, const uint64_t host_id);
+
+extern void aicwf_sdio_host_tx_cfm_handler(struct sdio_host_env_tag *env, u32 *data);
+extern int aicwf_rwnx_sdio_platform_init(struct aic_sdio_dev *sdiodev);
+
+#endif
diff --git a/drivers/net/wireless/aic8800/aic8800_fdrv/usb_host.c b/drivers/net/wireless/aic8800/aic8800_fdrv/usb_host.c
new file mode 100644
index 000000000000..fca8dc42c96d
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic8800_fdrv/usb_host.c
@@ -0,0 +1,154 @@
+/**
+ * usb_host.c
+ *
+ * USB host function declarations
+ *
+ * Copyright (C) AICSemi 2018-2020
+ */
+
+
+#include "usb_host.h"
+//#include "ipc_compat.h"
+#include "rwnx_tx.h"
+#include "rwnx_platform.h"
+#include "aicwf_debug.h"
+
+/**
+ ****************************************************************************************
+ */
+void aicwf_usb_host_init(struct usb_host_env_tag *env,
+ void *cb,
+ void *shared_env_ptr,
+ void *pthis)
+{
+ // Reset the environments
+
+ // Reset the Host environment
+ memset(env, 0, sizeof(struct usb_host_env_tag));
+ // Save the pointer to the register base
+ env->pthis = pthis;
+}
+
+/**
+ ****************************************************************************************
+ */
+volatile struct txdesc_host *aicwf_usb_host_txdesc_get(struct usb_host_env_tag *env, const int queue_idx)
+{
+ // struct ipc_shared_env_tag *shared_env_ptr = env->shared;
+ volatile struct txdesc_host *txdesc_free = NULL;
+ uint32_t used_idx = env->txdesc_used_idx[queue_idx];
+ uint32_t free_idx = env->txdesc_free_idx[queue_idx];
+
+ // ASSERT_ERR(queue_idx < SDIO_TXQUEUE_CNT);
+ // ASSERT_ERR((free_idx - used_idx) <= USB_TXDESC_CNT);
+
+ // Check if a free descriptor is available
+ if (free_idx != (used_idx + USB_TXDESC_CNT))
+ {
+ // Get the pointer to the first free descriptor
+ // txdesc_free = shared_env_ptr->txdesc[queue_idx] + (free_idx % IPC_TXDESC_CNT);
+ }
+ else
+ {
+ //txdesc_free = NULL;
+ }
+
+ return txdesc_free;
+}
+
+/**
+ ****************************************************************************************
+ */
+void aicwf_usb_host_txdesc_push(struct usb_host_env_tag *env, const int queue_idx, const uint64_t host_id)
+{
+ //printk("push, %d, %d, 0x%llx \r\n", queue_idx, env->txdesc_free_idx[queue_idx], host_id);
+ // Save the host id in the environment
+ env->tx_host_id[queue_idx][env->txdesc_free_idx[queue_idx] % USB_TXDESC_CNT] = host_id;
+
+ // Increment the index
+ env->txdesc_free_idx[queue_idx]++;
+ if(env->txdesc_free_idx[queue_idx]==0x40000000)
+ env->txdesc_free_idx[queue_idx] = 0;
+}
+
+/**
+ ****************************************************************************************
+ */
+void aicwf_usb_host_tx_cfm_handler(struct usb_host_env_tag *env, u32 *data)
+{
+ u32 queue_idx = 0;//data[0];
+ //struct rwnx_hw *rwnx_hw = (struct rwnx_hw *)env->pthis;
+ struct sk_buff *skb = NULL;
+ struct rwnx_txhdr *txhdr;
+ AICWFDBG(LOGTRACE, "%s Enter \n", __func__);
+ //printk("sdio_host_tx_cfm_handler, %d, 0x%08x\r\n", queue_idx, data[1]);
+ // TX confirmation descriptors have been received
+ // REG_SW_SET_PROFILING(env->pthis, SW_PROF_IRQ_E2A_TXCFM);
+ //while (1)
+ {
+ // Get the used index and increase it. We do the increase before knowing if the
+ // current buffer is confirmed because the callback function may call the
+ // ipc_host_txdesc_get() in case flow control was enabled and the index has to be
+ // already at the good value to ensure that the test of FIFO full is correct
+ //uint32_t used_idx = env->txdesc_used_idx[queue_idx]++;
+ uint32_t used_idx = data[1];
+ //uint64_t host_id = env->tx_host_id[queue_idx][used_idx % USB_TXDESC_CNT];
+ unsigned long host_id = env->tx_host_id[queue_idx][used_idx % USB_TXDESC_CNT];
+
+ // Reset the host id in the array
+ env->tx_host_id[queue_idx][used_idx % USB_TXDESC_CNT] = 0;
+
+ // call the external function to indicate that a TX packet is freed
+ if (host_id == 0)
+ {
+ // No more confirmations, so put back the used index at its initial value
+ env->txdesc_used_idx[queue_idx] = used_idx;
+ AICWFDBG(LOGERROR, "ERROR:No more confirmations\r\n");
+ return;
+ //break;
+ }
+ AICWFDBG(LOGINFO, "usb_host_tx_cfm_handler:used_idx=%d, hostid=%p, 0x%p, status=%x\r\n",used_idx, (void *)host_id, env->pthis, data[0]);
+
+ // set the cfm status
+ skb = (struct sk_buff *)host_id;
+ txhdr = (struct rwnx_txhdr *)skb->data;
+ txhdr->hw_hdr.cfm.status = (union rwnx_hw_txstatus)data[0];
+ //txhdr->hw_hdr.status = data[1];
+ //if (env->cb.send_data_cfm(env->pthis, host_id) != 0)
+ if (rwnx_txdatacfm(env->pthis, (void *)host_id) != 0)
+ {
+ // No more confirmations, so put back the used index at its initial value
+ env->txdesc_used_idx[queue_idx] = used_idx;
+ env->tx_host_id[queue_idx][used_idx % USB_TXDESC_CNT] = host_id;
+ // and exit the loop
+ AICWFDBG(LOGERROR, "ERROR:rwnx_txdatacfm,\r\n");
+ // break;
+ }
+
+ }
+}
+
+int aicwf_rwnx_usb_platform_init(struct aic_usb_dev *usbdev)
+{
+ struct rwnx_plat *rwnx_plat = NULL;
+ void *drvdata;
+ int ret = -ENODEV;
+
+ rwnx_plat = kzalloc(sizeof(struct rwnx_plat), GFP_KERNEL);
+
+ if (!rwnx_plat)
+ return -ENOMEM;
+
+// rwnx_plat->pci_dev = pci_dev;
+ rwnx_plat->usbdev = usbdev;
+
+ ret = rwnx_platform_init(rwnx_plat, &drvdata);
+#if 0
+ pci_set_drvdata(pci_dev, drvdata);
+
+ if (ret)
+ rwnx_plat->deinit(rwnx_plat);
+#endif
+ return ret;
+}
+
diff --git a/drivers/net/wireless/aic8800/aic8800_fdrv/usb_host.h b/drivers/net/wireless/aic8800/aic8800_fdrv/usb_host.h
new file mode 100644
index 000000000000..70b7eb260e52
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic8800_fdrv/usb_host.h
@@ -0,0 +1,43 @@
+/**
+ * usb_host.h
+ *
+ * USB host function declarations
+ *
+ * Copyright (C) AICSemi 2018-2020
+ */
+
+
+#ifndef _USB_HOST_H_
+#define _USB_HOST_H_
+
+#include "lmac_types.h"
+#include "aicwf_usb.h"
+
+#define USB_TXQUEUE_CNT NX_TXQ_CNT
+#define USB_TXDESC_CNT NX_TXDESC_CNT
+
+
+/// Definition of the IPC Host environment structure.
+struct usb_host_env_tag
+{
+ // Index used that points to the first free TX desc
+ uint32_t txdesc_free_idx[USB_TXQUEUE_CNT];
+ // Index used that points to the first used TX desc
+ uint32_t txdesc_used_idx[USB_TXQUEUE_CNT];
+ // Array storing the currently pushed host ids, per IPC queue
+ //uint64_t tx_host_id[USB_TXQUEUE_CNT][USB_TXDESC_CNT];
+ unsigned long tx_host_id[USB_TXQUEUE_CNT][USB_TXDESC_CNT];
+
+ /// Pointer to the attached object (used in callbacks and register accesses)
+ void *pthis;
+};
+
+extern void aicwf_usb_host_init(struct usb_host_env_tag *env,
+ void *cb, void *shared_env_ptr, void *pthis);
+
+extern void aicwf_usb_host_txdesc_push(struct usb_host_env_tag *env, const int queue_idx, const uint64_t host_id);
+
+extern void aicwf_usb_host_tx_cfm_handler(struct usb_host_env_tag *env, u32 *data);
+extern int aicwf_rwnx_usb_platform_init(struct aic_usb_dev *usbdev);
+
+#endif
diff --git a/drivers/net/wireless/aic8800/aic_load_fw/Kconfig b/drivers/net/wireless/aic8800/aic_load_fw/Kconfig
new file mode 100644
index 000000000000..4c2e90a57f39
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic_load_fw/Kconfig
@@ -0,0 +1,5 @@
+config AIC_LOADFW_SUPPORT
+ tristate "AIC8800 Load Firmware Support"
+ help
+ This is support for aic load firmware driver.
+
diff --git a/drivers/net/wireless/aic8800/aic_load_fw/Makefile b/drivers/net/wireless/aic8800/aic_load_fw/Makefile
new file mode 100644
index 000000000000..e6444e8ce23d
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic_load_fw/Makefile
@@ -0,0 +1,105 @@
+CONFIG_USB_SUPPORT =y
+CONFIG_RFTEST =y
+CONFIG_USB_BT =y
+CONFIG_USB_MSG_EP = y
+CONFIG_USB_NO_TRANS_DMA_MAP = n
+CONFIG_M2D_OTA_AUTO_SUPPORT = n
+CONFIG_LINK_DET_5G = y
+# Need to set fw path in BOARD_KERNEL_CMDLINE
+CONFIG_USE_FW_REQUEST = n
+CONFIG_PREALLOC_RX_SKB = n
+CONFIG_PREALLOC_TXQ = n
+
+# Platform support list
+CONFIG_PLATFORM_ROCKCHIP ?= y
+CONFIG_PLATFORM_ALLWINNER ?= n
+CONFIG_PLATFORM_AMLOGIC ?= n
+CONFIG_PLATFORM_UBUNTU ?= n
+
+CONFIG_AIC_LOADFW_SUPPORT = m
+MODULE_NAME = aic_load_fw
+# CONFIG_AIC_FW_PATH = "/vendor/etc/firmware"
+# export CONFIG_AIC_FW_PATH
+
+ifeq ($(CONFIG_USB_SUPPORT), y)
+ccflags-y += -DAICWF_USB_SUPPORT
+endif
+ccflags-$(CONFIG_RFTEST) += -DCONFIG_RFTEST
+ccflags-$(CONFIG_USB_BT) += -DCONFIG_USB_BT
+ccflags-$(CONFIG_USB_MSG_EP) += -DCONFIG_USB_MSG_EP
+ccflags-$(CONFIG_USB_NO_TRANS_DMA_MAP) += -DCONFIG_USB_NO_TRANS_DMA_MAP
+ccflags-$(CONFIG_M2D_OTA_AUTO_SUPPORT) += -DCONFIG_M2D_OTA_AUTO_SUPPORT
+ccflags-$(CONFIG_LINK_DET_5G) += -DCONFIG_LINK_DET_5G
+ccflags-$(CONFIG_USE_FW_REQUEST) += -DCONFIG_USE_FW_REQUEST
+ccflags-$(CONFIG_PREALLOC_RX_SKB) += -DCONFIG_PREALLOC_RX_SKB
+ccflags-$(CONFIG_PREALLOC_TXQ) += -DCONFIG_PREALLOC_TXQ
+
+
+obj-$(CONFIG_AIC_LOADFW_SUPPORT) := $(MODULE_NAME).o
+$(MODULE_NAME)-y := aic_bluetooth_main.o \
+ aicbluetooth.o \
+ aicwf_usb.o \
+ aic_txrxif.o \
+ aicbluetooth_cmds.o \
+ aic_compat_8800d80.o \
+ md5.o
+
+$(MODULE_NAME)-$(CONFIG_PREALLOC_RX_SKB) += aicwf_rx_prealloc.o
+$(MODULE_NAME)-$(CONFIG_PREALLOC_TXQ) += aicwf_txq_prealloc.o
+
+
+
+ifeq ($(CONFIG_PLATFORM_ROCKCHIP), y)
+ccflags-$(CONFIG_PLATFORM_ROCKCHIP) += -DCONFIG_PLATFORM_ROCKCHIP
+#KDIR := /home/yaya/E/Rockchip/3229/Android7/RK3229_ANDROID7.1_v1.01_20170914/rk3229_Android7.1_v1.01_xml0914/kernel
+#ARCH ?= arm
+#CROSS_COMPILE ?= /home/yaya/E/Rockchip/3229/Android7/RK3229_ANDROID7.1_v1.01_20170914/rk3229_Android7.1_v1.01_xml0914/prebuilts/gcc/linux-x86/arm/arm-eabi-4.6/bin/arm-eabi-
+KDIR := /home/yaya/E/Rockchip/3229/Android9/rk3229_android9.0_box/kernel
+ARCH ?= arm
+CROSS_COMPILE ?= /home/yaya/E/Rockchip/3229/Android9/rk3229_android9.0_box/prebuilts/gcc/linux-x86/arm/gcc-linaro-6.3.1-2017.05-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf-
+#KDIR := /home/yaya/E/Rockchip/3399/rk3399-android-10/kernel
+#ARCH ?= arm64
+#CROSS_COMPILE ?= /home/yaya/E/Rockchip/3399/rk3399-android-10/prebuilts/gcc/linux-x86/aarch64/gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-
+endif
+
+ifeq ($(CONFIG_PLATFORM_ALLWINNER), y)
+ccflags-$(CONFIG_PLATFORM_ALLWINNER) += -DCONFIG_PLATFORM_ALLWINNER
+KDIR := /home/yaya/E/Allwinner/R818/R818/AndroidQ/lichee/kernel/linux-4.9
+ARCH ?= arm64
+CROSS_COMPILE ?= /home/yaya/E/Allwinner/R818/R818/AndroidQ/lichee/out/gcc-linaro-5.3.1-2016.05-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-
+endif
+
+ifeq ($(CONFIG_PLATFORM_AMLOGIC), y)
+ccflags-$(CONFIG_PLATFORM_AMLOGIC) += -DCONFIG_PLATFORM_AMLOGIC
+ARCH := arm
+CROSS_COMPILE := /home/yaya/D/Workspace/CyberQuantum/JinHaoYue/amls905x3/SDK/20191101-0tt-asop/android9.0/prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.9/bin/arm-linux-androidkernel-
+KDIR := /home/yaya/D/Workspace/CyberQuantum/JinHaoYue/amls905x3/SDK/20191101-0tt-asop/android9.0/out/target/product/u202/obj/KERNEL_OBJ/
+
+endif
+
+ifeq ($(CONFIG_PLATFORM_UBUNTU), y)
+ccflags-$(CONFIG_PLATFORM_UBUNTU) += -DCONFIG_PLATFORM_UBUNTU
+KDIR := /lib/modules/$(shell uname -r)/build
+PWD := $(shell pwd)
+KVER := $(shell uname -r)
+MODDESTDIR := /lib/modules/$(KVER)/kernel/drivers/net/wireless/aic8800
+ARCH := x86_64
+CROSS_COMPILE :=
+endif
+
+
+all: modules
+modules:
+ make -C $(KDIR) M=$(PWD) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) modules
+
+install:
+ mkdir -p $(MODDESTDIR)
+ install -p -m 644 $(MODULE_NAME).ko $(MODDESTDIR)/
+ /sbin/depmod -a ${KVER}
+
+uninstall:
+ rm -rfv $(MODDESTDIR)/$(MODULE_NAME).ko
+ /sbin/depmod -a ${KVER}
+
+clean:
+ rm -rf *.o *.ko *.o.* *.mod.* modules.* Module.* .a* .o* .*.o.* *.mod .tmp* .cache.mk .modules.order.cmd .Module.symvers.cmd
diff --git a/drivers/net/wireless/aic8800/aic_load_fw/aic_bluetooth_main.c b/drivers/net/wireless/aic8800/aic_load_fw/aic_bluetooth_main.c
new file mode 100644
index 000000000000..8eed551d5437
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic_load_fw/aic_bluetooth_main.c
@@ -0,0 +1,71 @@
+#include <linux/module.h>
+#include <linux/inetdevice.h>
+#include "aicwf_usb.h"
+#include "rwnx_version_gen.h"
+#include "aicwf_rx_prealloc.h"
+#include "aicwf_debug.h"
+#include "aicwf_txq_prealloc.h"
+
+
+#define DRV_CONFIG_FW_NAME "fw.bin"
+#define DRV_DESCRIPTION "AIC BLUETOOTH"
+#define DRV_COPYRIGHT "Copyright(c) 2015-2020 AICSemi"
+#define DRV_AUTHOR "AICSemi"
+#define DRV_VERS_MOD "1.0"
+
+int testmode = FW_NORMAL_MODE;
+int adap_test = 0;
+char paringid[100];
+int n_para = 1;
+int ble_scan_wakeup_reboot_time = 1000;
+int aicwf_dbg_level = LOGERROR|LOGINFO|LOGDEBUG|LOGTRACE;
+uint32_t ad_data_filter_mask = 0;
+
+module_param(aicwf_dbg_level, int, 0660);
+module_param(ble_scan_wakeup_reboot_time, int, 0660);
+module_param(testmode, int, 0660);
+module_param(adap_test, int, 0660);
+module_param_string(paringid, paringid, 100, 0660);
+
+
+static void aicsmac_driver_register(void)
+{
+ aicwf_usb_register();
+}
+
+static int __init aic_bluetooth_mod_init(void)
+{
+ printk("%s \n", __func__);
+ printk("RELEASE DATE:%s \r\n", RELEASE_DATE);
+#ifdef CONFIG_PREALLOC_RX_SKB
+ aicwf_prealloc_init();
+#endif
+
+ aicsmac_driver_register();
+ return 0;
+}
+
+static void __exit aic_bluetooth_mod_exit(void)
+{
+ printk("%s\n", __func__);
+ aicwf_usb_exit();
+
+#ifdef CONFIG_PREALLOC_RX_SKB
+ aicwf_prealloc_exit();
+#endif
+
+#ifdef CONFIG_PREALLOC_TXQ
+ aicwf_prealloc_txq_free();
+#endif
+}
+
+
+module_init(aic_bluetooth_mod_init);
+module_exit(aic_bluetooth_mod_exit);
+
+MODULE_FIRMWARE(DRV_CONFIG_FW_NAME);
+MODULE_DESCRIPTION(DRV_DESCRIPTION);
+MODULE_VERSION(DRV_VERS_MOD);
+MODULE_AUTHOR(DRV_COPYRIGHT " " DRV_AUTHOR);
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/net/wireless/aic8800/aic_load_fw/aic_compat_8800d80.c b/drivers/net/wireless/aic8800/aic_load_fw/aic_compat_8800d80.c
new file mode 100644
index 000000000000..b37ad772bcd6
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic_load_fw/aic_compat_8800d80.c
@@ -0,0 +1,331 @@
+#include "aic_txrxif.h"
+#include "aicwf_usb.h"
+#include "aicbluetooth.h"
+#include "aic_compat_8800d80.h"
+#include "aicwf_debug.h"
+
+int rwnx_plat_bin_fw_upload_2(struct aic_usb_dev *usbdev, u32 fw_addr,
+ char *filename);
+int rwnx_request_firmware_common(struct aic_usb_dev *usbdev,
+ u32** buffer, const char *filename);
+void rwnx_plat_userconfig_parsing(char *buffer, int size);
+void rwnx_release_firmware_common(u32** buffer);
+
+extern int testmode;
+extern int chip_id;
+
+typedef u32 (*array2_tbl_t)[2];
+
+#define AIC_PATCH_MAGIG_NUM 0x48435450 // "PTCH"
+#define AIC_PATCH_MAGIG_NUM_2 0x50544348 // "HCTP"
+#define AIC_PATCH_BLOCK_MAX 4
+
+typedef struct {
+ uint32_t magic_num;
+ uint32_t pair_start;
+ uint32_t magic_num_2;
+ uint32_t pair_count;
+ uint32_t block_dst[AIC_PATCH_BLOCK_MAX];
+ uint32_t block_src[AIC_PATCH_BLOCK_MAX];
+ uint32_t block_size[AIC_PATCH_BLOCK_MAX]; // word count
+} aic_patch_t;
+
+
+#define AIC_PATCH_OFST(mem) ((size_t) &((aic_patch_t *)0)->mem)
+#define AIC_PATCH_ADDR(mem) ((u32) (aic_patch_str_base + AIC_PATCH_OFST(mem)))
+
+u32 patch_tbl_d80[][2] =
+{
+ #ifdef USE_5G
+ {0x00b4, 0xf3010001},
+ #else
+ {0x00b4, 0xf3010000},
+ #endif
+};
+
+//adap test
+u32 adaptivity_patch_tbl_d80[][2] = {
+ {0x000C, 0x0000320A}, //linkloss_thd
+ {0x009C, 0x00000000}, //ac_param_conf
+ {0x0154, 0x00010000}, //tx_adaptivity_en
+};
+
+u32 syscfg_tbl_masked_8800d80[][3] = {
+};
+
+u32 syscfg_tbl_8800d80[][2] = {
+ #ifdef CONFIG_PMIC_SETTING
+ {0x70001408, 0x00000000}, // stop wdg
+ #endif /* CONFIG_PMIC_SETTING */
+};
+
+extern int adap_test;
+
+int aicwf_patch_config_8800d80(struct aic_usb_dev *usb_dev)
+{
+ u32 rd_patch_addr;
+ u32 aic_patch_addr;
+ u32 config_base, aic_patch_str_base;
+ uint32_t start_addr = 0x001D7000;
+ u32 patch_addr = start_addr;
+ u32 patch_cnt = sizeof(patch_tbl_d80) / 4 / 2;
+ struct dbg_mem_read_cfm rd_patch_addr_cfm;
+ int ret = 0;
+ int cnt = 0;
+ //adap test
+ int adap_patch_cnt = 0;
+
+ if (adap_test) {
+ adap_patch_cnt = sizeof(adaptivity_patch_tbl_d80)/sizeof(u32)/2;
+ }
+
+ if (chip_id == CHIP_REV_U01) {
+ rd_patch_addr = RAM_FMAC_FW_ADDR_8800D80 + 0x0198;
+ } else {
+ rd_patch_addr = RAM_FMAC_FW_ADDR_8800D80_U02 + 0x0198;
+ }
+ aic_patch_addr = rd_patch_addr + 8;
+
+ AICWFDBG(LOGERROR, "Read FW mem: %08x\n", rd_patch_addr);
+ if ((ret = rwnx_send_dbg_mem_read_req(usb_dev, rd_patch_addr, &rd_patch_addr_cfm))) {
+ AICWFDBG(LOGERROR, "setting base[0x%x] rd fail: %d\n", rd_patch_addr, ret);
+ return ret;
+ }
+ AICWFDBG(LOGERROR, "%x=%x\n", rd_patch_addr_cfm.memaddr, rd_patch_addr_cfm.memdata);
+ config_base = rd_patch_addr_cfm.memdata;
+
+ if ((ret = rwnx_send_dbg_mem_read_req(usb_dev, aic_patch_addr, &rd_patch_addr_cfm))) {
+ AICWFDBG(LOGERROR, "patch_str_base[0x%x] rd fail: %d\n", aic_patch_addr, ret);
+ return ret;
+ }
+ AICWFDBG(LOGERROR, "%x=%x\n", rd_patch_addr_cfm.memaddr, rd_patch_addr_cfm.memdata);
+ aic_patch_str_base = rd_patch_addr_cfm.memdata;
+
+ if ((ret = rwnx_send_dbg_mem_write_req(usb_dev, AIC_PATCH_ADDR(magic_num), AIC_PATCH_MAGIG_NUM))) {
+ AICWFDBG(LOGERROR, "maigic_num[0x%x] write fail: %d\n", AIC_PATCH_ADDR(magic_num), ret);
+ return ret;
+ }
+
+ if ((ret = rwnx_send_dbg_mem_write_req(usb_dev, AIC_PATCH_ADDR(magic_num_2), AIC_PATCH_MAGIG_NUM_2))) {
+ AICWFDBG(LOGERROR, "maigic_num[0x%x] write fail: %d\n", AIC_PATCH_ADDR(magic_num_2), ret);
+ return ret;
+ }
+
+ if ((ret = rwnx_send_dbg_mem_write_req(usb_dev, AIC_PATCH_ADDR(pair_start), patch_addr))) {
+ AICWFDBG(LOGERROR, "pair_start[0x%x] write fail: %d\n", AIC_PATCH_ADDR(pair_start), ret);
+ return ret;
+ }
+
+ if ((ret = rwnx_send_dbg_mem_write_req(usb_dev, AIC_PATCH_ADDR(pair_count), patch_cnt + adap_patch_cnt))) {
+ AICWFDBG(LOGERROR, "pair_count[0x%x] write fail: %d\n", AIC_PATCH_ADDR(pair_count), ret);
+ return ret;
+ }
+
+ for (cnt = 0; cnt < patch_cnt; cnt++) {
+ if ((ret = rwnx_send_dbg_mem_write_req(usb_dev, start_addr+8*cnt, patch_tbl_d80[cnt][0]+config_base))) {
+ AICWFDBG(LOGERROR, "%x write fail\n", start_addr+8*cnt);
+ return ret;
+ }
+ if ((ret = rwnx_send_dbg_mem_write_req(usb_dev, start_addr+8*cnt+4, patch_tbl_d80[cnt][1]))) {
+ AICWFDBG(LOGERROR, "%x write fail\n", start_addr+8*cnt+4);
+ return ret;
+ }
+ }
+
+ if (adap_test){
+ int tmp_cnt = patch_cnt + adap_patch_cnt;
+ for (cnt = patch_cnt; cnt < tmp_cnt; cnt++) {
+ int tbl_idx = cnt - patch_cnt;
+ if ((ret = rwnx_send_dbg_mem_write_req(usb_dev, start_addr+8*cnt, adaptivity_patch_tbl_d80[tbl_idx][0]+config_base))) {
+ AICWFDBG(LOGERROR, "%x write fail\n", start_addr+8*cnt);
+ return ret;
+ }
+ if ((ret = rwnx_send_dbg_mem_write_req(usb_dev, start_addr+8*cnt+4, adaptivity_patch_tbl_d80[tbl_idx][1]))) {
+ AICWFDBG(LOGERROR, "%x write fail\n", start_addr+8*cnt+4);
+ return ret;
+ }
+ }
+ }
+
+ /*
+ * Patch block 0 ~ 3, that is void by default, can be set as:
+ *
+ * const u32 patch_block_0[3] = {0x11223344, 0x55667788, 0xaabbccdd};
+ * if ((ret = rwnx_send_dbg_mem_write_req(usb_dev, (u32)(&aic_patch->block_dst[0]), 0x160000))) {
+ * printk("block_dst [0x%x] write fail: %d\n", (u32)(&aic_patch->block_dst[0]), ret);
+ * }
+ * if ((ret = rwnx_send_dbg_mem_write_req(usb_dev, (u32)(&aic_patch->block_src[0]), 0x307000))) {
+ * printk("block_src [0x%x] write fail: %d\n", (u32)(&aic_patch->block_src[0]), ret);
+ * }
+ * if ((ret = rwnx_send_dbg_mem_write_req(usb_dev, (u32)(&aic_patch->block_size[0]), sizeof(patch_block_0) / sizeof(u32)))) {
+ * printk("block_size[0x%x] write fail: %d\n", (u32)(&aic_patch->block_size[0]), ret);
+ * }
+ * if ((ret = rwnx_send_dbg_mem_block_write_req(usb_dev, 0x307000, sizeof(patch_block_0), patch_block_0))) {
+ * printk("blk set fail: %d\n", ret);
+ * }
+ */
+ if ((ret = rwnx_send_dbg_mem_write_req(usb_dev, AIC_PATCH_ADDR(block_size[0]), 0))) {
+ AICWFDBG(LOGERROR, "block_size[0x%x] write fail: %d\n", AIC_PATCH_ADDR(block_size[0]), ret);
+ return ret;
+ }
+ if ((ret = rwnx_send_dbg_mem_write_req(usb_dev, AIC_PATCH_ADDR(block_size[1]), 0))) {
+ AICWFDBG(LOGERROR, "block_size[0x%x] write fail: %d\n", AIC_PATCH_ADDR(block_size[1]), ret);
+ return ret;
+ }
+ if ((ret = rwnx_send_dbg_mem_write_req(usb_dev, AIC_PATCH_ADDR(block_size[2]), 0))) {
+ AICWFDBG(LOGERROR, "block_size[0x%x] write fail: %d\n", AIC_PATCH_ADDR(block_size[2]), ret);
+ return ret;
+ }
+ if ((ret = rwnx_send_dbg_mem_write_req(usb_dev, AIC_PATCH_ADDR(block_size[3]), 0))) {
+ AICWFDBG(LOGERROR, "block_size[0x%x] write fail: %d\n", AIC_PATCH_ADDR(block_size[3]), ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+
+#if 0
+extern char aic_fw_path[200];
+
+int rwnx_plat_userconfig_load_8800d80(struct aic_usb_dev *usb_dev){
+ int size;
+ u32 *dst=NULL;
+ char *filename = FW_USERCONFIG_NAME_8800D80;
+
+ AICWFDBG(LOGINFO, "userconfig file path:%s \r\n", filename);
+
+ /* load file */
+ size = rwnx_request_firmware_common(usb_dev, &dst, filename);
+ if (size <= 0) {
+ AICWFDBG(LOGERROR, "wrong size of firmware file\n");
+ dst = NULL;
+ return 0;
+ }
+
+ /* Copy the file on the Embedded side */
+ AICWFDBG(LOGINFO, "### Load file done: %s, size=%d\n", filename, size);
+
+ rwnx_plat_userconfig_parsing((char *)dst, size);
+
+ rwnx_release_firmware_common(&dst);
+
+ AICWFDBG(LOGINFO, "userconfig download complete\n\n");
+ return 0;
+
+}
+#endif
+int system_config_8800d80(struct aic_usb_dev *usb_dev){
+ int syscfg_num;
+ int ret, cnt;
+ const u32 mem_addr = 0x40500000;
+ struct dbg_mem_read_cfm rd_mem_addr_cfm;
+ ret = rwnx_send_dbg_mem_read_req(usb_dev, mem_addr, &rd_mem_addr_cfm);
+ if (ret) {
+ printk("%x rd fail: %d\n", mem_addr, ret);
+ return ret;
+ }
+ chip_id = (u8)(rd_mem_addr_cfm.memdata >> 16);
+ printk("chip_id=%x\n", chip_id);
+ #if 1
+ syscfg_num = sizeof(syscfg_tbl_8800d80) / sizeof(u32) / 2;
+ for (cnt = 0; cnt < syscfg_num; cnt++) {
+ ret = rwnx_send_dbg_mem_write_req(usb_dev, syscfg_tbl_8800d80[cnt][0], syscfg_tbl_8800d80[cnt][1]);
+ if (ret) {
+ printk("%x write fail: %d\n", syscfg_tbl_8800d80[cnt][0], ret);
+ return ret;
+ }
+ }
+ syscfg_num = sizeof(syscfg_tbl_masked_8800d80) / sizeof(u32) / 3;
+ for (cnt = 0; cnt < syscfg_num; cnt++) {
+ ret = rwnx_send_dbg_mem_mask_write_req(usb_dev,
+ syscfg_tbl_masked_8800d80[cnt][0], syscfg_tbl_masked_8800d80[cnt][1], syscfg_tbl_masked_8800d80[cnt][2]);
+ if (ret) {
+ printk("%x mask write fail: %d\n", syscfg_tbl_masked_8800d80[cnt][0], ret);
+ return ret;
+ }
+ }
+ #endif
+ return 0;
+}
+
+int aicfw_download_fw_8800d80(struct aic_usb_dev *usb_dev){
+ if(testmode == FW_NORMAL_MODE){
+ if (chip_id != CHIP_REV_U01){
+ if(rwnx_plat_bin_fw_upload_android(usb_dev, FW_RAM_ADID_BASE_ADDR_8800D80_U02, FW_ADID_BASE_NAME_8800D80_U02)) {
+ return -1;
+ }
+ if(rwnx_plat_bin_fw_upload_android(usb_dev, FW_RAM_PATCH_BASE_ADDR_8800D80_U02, FW_PATCH_BASE_NAME_8800D80_U02)) {
+ return -1;
+ }
+ if (rwnx_plat_bin_fw_patch_table_upload_android(usb_dev, FW_PATCH_TABLE_NAME_8800D80_U02)) {
+ return -1;
+ }
+ if(rwnx_plat_bin_fw_upload_android(usb_dev, RAM_FMAC_FW_ADDR_8800D80_U02, FW_BASE_NAME_8800D80_U02)) {
+ return -1;
+ }
+ #if 0
+ if(rwnx_plat_bin_fw_upload_android(usb_dev, FW_RAM_CALIBMODE_ADDR_8800D80_U02, FW_CALIBMODE_NAME_8800D80_U02)) {
+ return -1;
+ }
+ if (rwnx_send_dbg_mem_write_req(usb_dev, 0x40500048, 0x1e0000))
+ return -1;
+ #endif
+ if (aicwf_patch_config_8800d80(usb_dev)) {
+ return -1;
+ }
+ if (rwnx_send_dbg_start_app_req(usb_dev, RAM_FMAC_FW_ADDR_8800D80_U02, HOST_START_APP_AUTO)) {
+ return -1;
+ }
+ }else {
+ if(rwnx_plat_bin_fw_upload_android(usb_dev, FW_RAM_ADID_BASE_ADDR_8800D80, FW_ADID_BASE_NAME_8800D80)) {
+ return -1;
+ }
+ if(rwnx_plat_bin_fw_upload_android(usb_dev, FW_RAM_PATCH_BASE_ADDR_8800D80, FW_PATCH_BASE_NAME_8800D80)) {
+ return -1;
+ }
+ if (rwnx_plat_bin_fw_patch_table_upload_android(usb_dev, FW_PATCH_TABLE_NAME_8800D80)) {
+ return -1;
+ }
+ if(rwnx_plat_bin_fw_upload_android(usb_dev, RAM_FMAC_FW_ADDR_8800D80, FW_BASE_NAME_8800D80)) {
+ return -1;
+ }
+ if (rwnx_send_dbg_start_app_req(usb_dev, RAM_FMAC_FW_ADDR_8800D80, HOST_START_APP_AUTO)) {
+ return -1;
+ }
+ }
+ }else if(testmode == FW_TEST_MODE){
+ if (chip_id != CHIP_REV_U01){
+
+ if(rwnx_plat_bin_fw_upload_android(usb_dev, FW_RAM_ADID_BASE_ADDR_8800D80_U02, FW_ADID_BASE_NAME_8800D80_U02)) {
+ return -1;
+ }
+
+ if(rwnx_plat_bin_fw_upload_android(usb_dev, FW_RAM_PATCH_BASE_ADDR_8800D80_U02, FW_PATCH_BASE_NAME_8800D80_U02)) {
+ return -1;
+ }
+ if (rwnx_plat_bin_fw_patch_table_upload_android(usb_dev, FW_PATCH_TABLE_NAME_8800D80_U02)) {
+ return -1;
+ }
+
+ if(rwnx_plat_bin_fw_upload_android(usb_dev, RAM_FMAC_RF_FW_ADDR_8800D80_U02, FW_RF_BASE_NAME_8800D80_U02)) {
+ AICWFDBG(LOGERROR,"%s wifi fw download fail \r\n", __func__);
+ return -1;
+ }
+ if (rwnx_send_dbg_start_app_req(usb_dev, RAM_FMAC_RF_FW_ADDR_8800D80_U02, HOST_START_APP_AUTO)) {
+ return -1;
+ }
+ } else {
+ if(rwnx_plat_bin_fw_upload_android(usb_dev, RAM_FMAC_RF_FW_ADDR_8800D80, FW_RF_BASE_NAME_8800D80)) {
+ AICWFDBG(LOGERROR,"%s wifi fw download fail \r\n", __func__);
+ return -1;
+ }
+ if (rwnx_send_dbg_start_app_req(usb_dev, RAM_FMAC_RF_FW_ADDR_8800D80, HOST_START_APP_AUTO)) {
+ return -1;
+ }
+ }
+ }
+ return 0;
+}
+
+
diff --git a/drivers/net/wireless/aic8800/aic_load_fw/aic_compat_8800d80.h b/drivers/net/wireless/aic8800/aic_load_fw/aic_compat_8800d80.h
new file mode 100644
index 000000000000..0249f935235f
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic_load_fw/aic_compat_8800d80.h
@@ -0,0 +1,37 @@
+#include <linux/types.h>
+#include "aicwf_usb.h"
+
+#define USB_DEVICE_ID_AIC_8800D80 0x8D80
+#define USB_DEVICE_ID_AIC_8800D81 0x8D81
+
+#define FW_BASE_NAME_8800D80 "fmacfw_8800d80.bin"
+#define FW_RF_BASE_NAME_8800D80 "fmacfw_rf_8800d80.bin"
+#define FW_PATCH_BASE_NAME_8800D80 "fw_patch_8800d80.bin"
+#define FW_ADID_BASE_NAME_8800D80 "fw_adid_8800d80.bin"
+#define FW_PATCH_TABLE_NAME_8800D80 "fw_patch_table_8800d80.bin"
+
+#define FW_BASE_NAME_8800D80_U02 "fmacfw_8800d80_u02.bin"
+#define FW_RF_BASE_NAME_8800D80_U02 "lmacfw_rf_8800d80_u02.bin"
+#define FW_PATCH_BASE_NAME_8800D80_U02 "fw_patch_8800d80_u02.bin"
+#define FW_ADID_BASE_NAME_8800D80_U02 "fw_adid_8800d80_u02.bin"
+#define FW_CALIBMODE_NAME_8800D80_U02 "calibmode_8800d80.bin"
+#define FW_PATCH_TABLE_NAME_8800D80_U02 "fw_patch_table_8800d80_u02.bin"
+
+#define FW_USERCONFIG_NAME_8800D80 "aic_userconfig_8800d80.txt"
+
+#define RAM_FMAC_FW_ADDR_8800D80 0x100000
+#define RAM_FMAC_RF_FW_ADDR_8800D80 0x110000
+#define FW_RAM_ADID_BASE_ADDR_8800D80 0x002017E0
+#define FW_RAM_PATCH_BASE_ADDR_8800D80 0x0020B2B0
+
+#define RAM_FMAC_FW_ADDR_8800D80_U02 0x120000
+#define RAM_FMAC_RF_FW_ADDR_8800D80_U02 0x120000
+#define FW_RAM_ADID_BASE_ADDR_8800D80_U02 0x00201940
+#define FW_RAM_CALIBMODE_ADDR_8800D80_U02 0x1e0000
+#define FW_RAM_PATCH_BASE_ADDR_8800D80_U02 0x0020B43c
+
+int aicwf_patch_config_8800d80(struct aic_usb_dev *usb_dev);
+int rwnx_plat_userconfig_load_8800d80(struct aic_usb_dev *usbdev);
+int system_config_8800d80(struct aic_usb_dev *usb_dev);
+int aicfw_download_fw_8800d80(struct aic_usb_dev *usb_dev);
+
diff --git a/drivers/net/wireless/aic8800/aic_load_fw/aic_txrxif.c b/drivers/net/wireless/aic8800/aic_load_fw/aic_txrxif.c
new file mode 100644
index 000000000000..a892b68ec521
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic_load_fw/aic_txrxif.c
@@ -0,0 +1,486 @@
+/**
+ * aicwf_bus.c
+ *
+ * bus function declarations
+ *
+ * Copyright (C) AICSemi 2018-2020
+ */
+
+#include <linux/kthread.h>
+#include <linux/netdevice.h>
+#include <linux/printk.h>
+#include <linux/interrupt.h>
+#include <linux/sched.h>
+#include <linux/completion.h>
+#include <linux/semaphore.h>
+#include <linux/debugfs.h>
+#include <linux/atomic.h>
+#include <linux/vmalloc.h>
+#include <linux/version.h>
+#ifdef CONFIG_PLATFORM_UBUNTU
+#if LINUX_VERSION_CODE > KERNEL_VERSION(4, 10, 0)
+#include <linux/sched/signal.h>
+#endif
+#endif
+#include "aic_txrxif.h"
+#include "aicbluetooth.h"
+#include "aicbluetooth_cmds.h"
+
+int aicwf_bus_init(uint bus_hdrlen, struct device *dev)
+{
+ int ret = 0;
+ struct aicwf_bus *bus_if;
+
+ if (!dev) {
+ txrx_err("device not found\n");
+ return -1;
+ }
+ bus_if = dev_get_drvdata(dev);
+ #if defined CONFIG_USB_SUPPORT && defined CONFIG_USB_NO_TRANS_DMA_MAP
+ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35))
+ bus_if->cmd_buf = usb_alloc_coherent(bus_if->bus_priv.usb->udev, CMD_BUF_MAX, (in_interrupt() ? GFP_ATOMIC : GFP_KERNEL), &bus_if->bus_priv.usb->cmd_dma_trans_addr);
+ #else
+ bus_if->cmd_buf = usb_buffer_alloc(bus_if->bus_priv.usb->udev, CMD_BUF_MAX, (in_interrupt() ? GFP_ATOMIC : GFP_KERNEL), &bus_if->bus_priv.usb->cmd_dma_trans_addr);
+ #endif
+ #else
+ bus_if->cmd_buf = kzalloc(CMD_BUF_MAX, GFP_KERNEL);
+ #endif
+ if(!bus_if->cmd_buf) {
+ ret = -ENOMEM;
+ txrx_err("proto_attach failed\n");
+ goto fail;
+ }
+ memset(bus_if->cmd_buf, '\0', CMD_BUF_MAX);
+
+ init_completion(&bus_if->bustx_trgg);
+ init_completion(&bus_if->busrx_trgg);
+#ifdef AICWF_SDIO_SUPPORT
+ bus_if->bustx_thread = kthread_run(sdio_bustx_thread, (void *)bus_if, "aicwf_bustx_thread");
+ bus_if->busrx_thread = kthread_run(sdio_busrx_thread, (void *)bus_if->bus_priv.sdio->rx_priv, "aicwf_busrx_thread");
+#endif
+#ifdef AICWF_USB_SUPPORT
+ bus_if->bustx_thread = kthread_run(usb_bustx_thread, (void *)bus_if, "aicwf_bustx_thread");
+ bus_if->busrx_thread = kthread_run(usb_busrx_thread, (void *)bus_if->bus_priv.usb->rx_priv, "aicwf_busrx_thread");
+#endif
+
+ if (IS_ERR(bus_if->bustx_thread)) {
+ bus_if->bustx_thread = NULL;
+ txrx_err("aicwf_bustx_thread run fail\n");
+ goto fail;
+ }
+
+ if (IS_ERR(bus_if->busrx_thread)) {
+ bus_if->busrx_thread = NULL;
+ txrx_err("aicwf_bustx_thread run fail\n");
+ goto fail;
+ }
+
+ return ret;
+fail:
+ aicwf_bus_deinit(dev);
+
+ return ret;
+}
+
+void aicwf_bus_deinit(struct device *dev)
+{
+ struct aicwf_bus *bus_if;
+ struct aic_usb_dev *usbdev;
+
+ if (!dev) {
+ txrx_err("device not found\n");
+ return;
+ }
+ printk("%s", __func__);
+
+ mdelay(500);
+ bus_if = dev_get_drvdata(dev);
+ aicwf_bus_stop(bus_if);
+
+ usbdev = bus_if->bus_priv.usb;
+ usbdev->app_cmp = false;
+ aic_bt_platform_deinit(usbdev);
+
+ if (bus_if->cmd_buf) {
+ #if defined CONFIG_USB_SUPPORT && defined CONFIG_USB_NO_TRANS_DMA_MAP
+ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35))
+ usb_free_coherent(bus_if->bus_priv.usb->udev, CMD_BUF_MAX, bus_if->cmd_buf, bus_if->bus_priv.usb->cmd_dma_trans_addr);
+ #else
+ usb_buffer_free(bus_if->bus_priv.usb->udev, CMD_BUF_MAX, bus_if->cmd_buf, bus_if->bus_priv.usb->cmd_dma_trans_addr);
+ #endif
+ #else
+ kfree(bus_if->cmd_buf);
+ #endif
+ bus_if->cmd_buf = NULL;
+ }
+
+ if (bus_if->bustx_thread) {
+ complete_all(&bus_if->bustx_trgg);
+ kthread_stop(bus_if->bustx_thread);
+ bus_if->bustx_thread = NULL;
+ }
+ printk("exit %s\n", __func__);
+}
+
+void aicwf_frame_tx(void *dev, struct sk_buff *skb)
+{
+ struct aic_usb_dev *usbdev = (struct aic_usb_dev *)dev;
+ aicwf_bus_txdata(usbdev->bus_if, skb);
+}
+
+struct aicwf_tx_priv* aicwf_tx_init(void *arg)
+{
+ struct aicwf_tx_priv* tx_priv;
+
+ tx_priv = kzalloc(sizeof(struct aicwf_tx_priv), GFP_KERNEL);
+ if (!tx_priv)
+ return NULL;
+
+ tx_priv->usbdev = (struct aic_usb_dev *)arg;
+
+ atomic_set(&tx_priv->aggr_count, 0);
+ tx_priv->aggr_buf = dev_alloc_skb(MAX_AGGR_TXPKT_LEN);
+ if(!tx_priv->aggr_buf) {
+ txrx_err("Alloc bus->txdata_buf failed!\n");
+ kfree(tx_priv);
+ return NULL;
+ }
+ tx_priv->head = tx_priv->aggr_buf->data;
+ tx_priv->tail = tx_priv->aggr_buf->data;
+
+ return tx_priv;
+}
+
+void aicwf_tx_deinit(struct aicwf_tx_priv* tx_priv)
+{
+ if (tx_priv && tx_priv->aggr_buf)
+ dev_kfree_skb(tx_priv->aggr_buf);
+
+ kfree(tx_priv);
+ tx_priv = NULL;
+}
+
+static bool aicwf_another_ptk(struct sk_buff *skb)
+{
+ u8 *data;
+ u16 aggr_len = 0;
+
+ if(skb->data == NULL || skb->len == 0) {
+ return false;
+ }
+ data = skb->data;
+ aggr_len = (*skb->data | (*(skb->data + 1) << 8));
+ if(aggr_len == 0) {
+ return false;
+ }
+
+ return true;
+}
+
+int aicwf_process_rxframes(struct aicwf_rx_priv *rx_priv)
+{
+ int ret = 0;
+ unsigned long flags = 0;
+ struct sk_buff *skb = NULL;
+ u16 pkt_len = 0;
+ struct sk_buff *skb_inblock = NULL;
+ u16 aggr_len = 0, adjust_len = 0;
+ u8 *data = NULL;
+
+ while (1) {
+ spin_lock_irqsave(&rx_priv->rxqlock, flags);
+ if(aicwf_is_framequeue_empty(&rx_priv->rxq)) {
+ spin_unlock_irqrestore(&rx_priv->rxqlock,flags);
+ break;
+ }
+ skb = aicwf_frame_dequeue(&rx_priv->rxq);
+ spin_unlock_irqrestore(&rx_priv->rxqlock,flags);
+ if (skb == NULL) {
+ txrx_err("skb_error\r\n");
+ break;
+ }
+ while(aicwf_another_ptk(skb)) {
+ data = skb->data;
+ pkt_len = (*skb->data | (*(skb->data + 1) << 8));
+
+ if((skb->data[2] & USB_TYPE_CFG) != USB_TYPE_CFG) { // type : data
+ aggr_len = pkt_len + RX_HWHRD_LEN;
+
+ if (aggr_len & (RX_ALIGNMENT - 1))
+ adjust_len = roundup(aggr_len, RX_ALIGNMENT);
+ else
+ adjust_len = aggr_len;
+
+ skb_inblock = __dev_alloc_skb(aggr_len + CCMP_OR_WEP_INFO, GFP_KERNEL);//8 is for ccmp mic or wep icv
+ if(skb_inblock == NULL){
+ txrx_err("no more space!\n");
+ aicwf_dev_skb_free(skb);
+ return -EBADE;
+ }
+
+ skb_put(skb_inblock, aggr_len);
+ memcpy(skb_inblock->data, data, aggr_len);
+ skb_pull(skb, adjust_len);
+ }
+ else { // type : config
+ aggr_len = pkt_len;
+
+ if (aggr_len & (RX_ALIGNMENT - 1))
+ adjust_len = roundup(aggr_len, RX_ALIGNMENT);
+ else
+ adjust_len = aggr_len;
+
+ skb_inblock = __dev_alloc_skb(aggr_len+4, GFP_KERNEL);
+ if(skb_inblock == NULL){
+ txrx_err("no more space!\n");
+ aicwf_dev_skb_free(skb);
+ return -EBADE;
+ }
+
+ skb_put(skb_inblock, aggr_len+4);
+ memcpy(skb_inblock->data, data, aggr_len+4);
+ if((*(skb_inblock->data + 2) & 0x7f) == USB_TYPE_CFG_CMD_RSP)
+ rwnx_rx_handle_msg(rx_priv->usbdev, (struct ipc_e2a_msg *)(skb_inblock->data + 4));
+ skb_pull(skb, adjust_len+4);
+ }
+ }
+
+ dev_kfree_skb(skb);
+ atomic_dec(&rx_priv->rx_cnt);
+ }
+
+ return ret;
+}
+
+static struct recv_msdu *aicwf_rxframe_queue_init(struct list_head *q, int qsize)
+{
+ int i;
+ struct recv_msdu *req, *reqs;
+
+ reqs = vmalloc(qsize*sizeof(struct recv_msdu));
+ if (reqs == NULL)
+ return NULL;
+
+ req = reqs;
+ for (i = 0; i < qsize; i++) {
+ INIT_LIST_HEAD(&req->rxframe_list);
+ list_add(&req->rxframe_list, q);
+ req->len = 0;
+ req++;
+ }
+
+ return reqs;
+}
+
+struct aicwf_rx_priv *aicwf_rx_init(void *arg)
+{
+ struct aicwf_rx_priv* rx_priv;
+ rx_priv = kzalloc(sizeof(struct aicwf_rx_priv), GFP_KERNEL);
+ if (!rx_priv)
+ return NULL;
+
+ rx_priv->usbdev = (struct aic_usb_dev *)arg;
+ aicwf_frame_queue_init(&rx_priv->rxq, 1, MAX_RXQLEN);
+ spin_lock_init(&rx_priv->rxqlock);
+ atomic_set(&rx_priv->rx_cnt, 0);
+
+ INIT_LIST_HEAD(&rx_priv->rxframes_freequeue);
+ spin_lock_init(&rx_priv->freeq_lock);
+ rx_priv->recv_frames = aicwf_rxframe_queue_init(&rx_priv->rxframes_freequeue, MAX_REORD_RXFRAME);
+ if (!rx_priv->recv_frames) {
+ txrx_err("no enough buffer for free recv frame queue!\n");
+ kfree(rx_priv);
+ return NULL;
+ }
+ spin_lock_init(&rx_priv->stas_reord_lock);
+ INIT_LIST_HEAD(&rx_priv->stas_reord_list);
+
+ return rx_priv;
+}
+
+
+static void aicwf_recvframe_queue_deinit(struct list_head *q)
+{
+ struct recv_msdu *req, *next;
+
+ list_for_each_entry_safe(req, next, q, rxframe_list) {
+ list_del_init(&req->rxframe_list);
+ }
+}
+
+void aicwf_rx_deinit(struct aicwf_rx_priv* rx_priv)
+{
+ //struct reord_ctrl_info *reord_info, *tmp;
+ aicwf_frame_queue_flush(&rx_priv->rxq);
+ aicwf_recvframe_queue_deinit(&rx_priv->rxframes_freequeue);
+ if (rx_priv->recv_frames)
+ vfree(rx_priv->recv_frames);
+
+ if (rx_priv->usbdev->bus_if->busrx_thread) {
+ complete_all(&rx_priv->usbdev->bus_if->busrx_trgg);
+ send_sig(SIGTERM, rx_priv->usbdev->bus_if->busrx_thread, 1);
+ kthread_stop(rx_priv->usbdev->bus_if->busrx_thread);
+ rx_priv->usbdev->bus_if->busrx_thread = NULL;
+ }
+
+ kfree(rx_priv);
+ rx_priv = NULL;
+}
+
+bool aicwf_rxframe_enqueue(struct device *dev, struct frame_queue *q, struct sk_buff *pkt)
+{
+ return aicwf_frame_enq(dev, q, pkt, 0);
+}
+
+
+void aicwf_dev_skb_free(struct sk_buff *skb)
+{
+ if (!skb)
+ return;
+
+ dev_kfree_skb_any(skb);
+}
+
+static struct sk_buff *aicwf_frame_queue_penq(struct frame_queue *pq, int prio, struct sk_buff *p)
+{
+ struct sk_buff_head *q;
+
+ if (pq->queuelist[prio].qlen >= pq->qmax)
+ return NULL;
+
+ q = &pq->queuelist[prio];
+ __skb_queue_tail(q, p);
+ pq->qcnt++;
+ if (pq->hi_prio < prio)
+ pq->hi_prio = (u16)prio;
+
+ return p;
+}
+
+void aicwf_frame_queue_flush(struct frame_queue *pq)
+{
+ int prio;
+ struct sk_buff_head *q;
+ struct sk_buff *p, *next;
+
+ for (prio = 0; prio < pq->num_prio; prio++)
+ {
+ q = &pq->queuelist[prio];
+ skb_queue_walk_safe(q, p, next) {
+ skb_unlink(p, q);
+ aicwf_dev_skb_free(p);
+ pq->qcnt--;
+ }
+ }
+}
+
+void aicwf_frame_queue_init(struct frame_queue *pq, int num_prio, int max_len)
+{
+ int prio;
+
+ memset(pq, 0, offsetof(struct frame_queue, queuelist) + (sizeof(struct sk_buff_head) * num_prio));
+ pq->num_prio = (u16)num_prio;
+ pq->qmax = (u16)max_len;
+
+ for (prio = 0; prio < num_prio; prio++) {
+ skb_queue_head_init(&pq->queuelist[prio]);
+ }
+}
+
+struct sk_buff *aicwf_frame_queue_peek_tail(struct frame_queue *pq, int *prio_out)
+{
+ int prio;
+
+ if (pq->qcnt == 0)
+ return NULL;
+
+ for (prio = 0; prio < pq->hi_prio; prio++)
+ if (!skb_queue_empty(&pq->queuelist[prio]))
+ break;
+
+ if (prio_out)
+ *prio_out = prio;
+
+ return skb_peek_tail(&pq->queuelist[prio]);
+}
+
+bool aicwf_is_framequeue_empty(struct frame_queue *pq)
+{
+ int prio, len = 0;
+
+ for (prio = 0; prio <= pq->hi_prio; prio++)
+ len += pq->queuelist[prio].qlen;
+
+ if(len > 0)
+ return false;
+ else
+ return true;
+}
+
+struct sk_buff *aicwf_frame_dequeue(struct frame_queue *pq)
+{
+ struct sk_buff_head *q;
+ struct sk_buff *p;
+ int prio;
+
+ if (pq->qcnt == 0)
+ return NULL;
+
+ while ((prio = pq->hi_prio) > 0 && skb_queue_empty(&pq->queuelist[prio]))
+ pq->hi_prio--;
+
+ q = &pq->queuelist[prio];
+ p = __skb_dequeue(q);
+ if (p == NULL)
+ return NULL;
+
+ pq->qcnt--;
+
+ return p;
+}
+
+static struct sk_buff *aicwf_skb_dequeue_tail(struct frame_queue *pq, int prio)
+{
+ struct sk_buff_head *q = &pq->queuelist[prio];
+ struct sk_buff *p = skb_dequeue_tail(q);
+
+ if (!p)
+ return NULL;
+
+ pq->qcnt--;
+ return p;
+}
+
+bool aicwf_frame_enq(struct device *dev, struct frame_queue *q, struct sk_buff *pkt, int prio)
+{
+ struct sk_buff *p = NULL;
+ int prio_modified = -1;
+
+ if (q->queuelist[prio].qlen < q->qmax && q->qcnt < q->qmax) {
+ aicwf_frame_queue_penq(q, prio, pkt);
+ return true;
+ }
+ if (q->queuelist[prio].qlen >= q->qmax) {
+ prio_modified = prio;
+ } else if (q->qcnt >= q->qmax) {
+ p = aicwf_frame_queue_peek_tail(q, &prio_modified);
+ if (prio_modified > prio)
+ return false;
+ }
+
+ if (prio_modified >= 0) {
+ if (prio_modified == prio)
+ return false;
+
+ p = aicwf_skb_dequeue_tail(q, prio_modified);
+ aicwf_dev_skb_free(p);
+
+ p = aicwf_frame_queue_penq(q, prio_modified, pkt);
+ if (p == NULL)
+ txrx_err("failed\n");
+ }
+
+ return p != NULL;
+}
+
+
diff --git a/drivers/net/wireless/aic8800/aic_load_fw/aic_txrxif.h b/drivers/net/wireless/aic8800/aic_load_fw/aic_txrxif.h
new file mode 100644
index 000000000000..d566a11706c4
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic_load_fw/aic_txrxif.h
@@ -0,0 +1,216 @@
+/**
+ * aicwf_txrxif.h
+ *
+ * bus function declarations
+ *
+ * Copyright (C) AICSemi 2018-2020
+ */
+
+#ifndef _AICWF_TXRXIF_H_
+#define _AICWF_TXRXIF_H_
+
+#include <linux/skbuff.h>
+#include <linux/sched.h>
+//#include "aicsdio.h"
+#include "aicwf_usb.h"
+
+#define CMD_BUF_MAX 1536
+#define DATA_BUF_MAX 2048
+#define TXPKT_BLOCKSIZE 512
+#define MAX_AGGR_TXPKT_LEN (1536*32)
+#define CMD_TX_TIMEOUT 5000
+#define TX_ALIGNMENT 4
+
+#define RX_HWHRD_LEN 60 //58->60 word allined
+#define CCMP_OR_WEP_INFO 8
+#define MAX_RXQLEN 2000
+#define RX_ALIGNMENT 4
+
+#define DEBUG_ERROR_LEVEL 0
+#define DEBUG_DEBUG_LEVEL 1
+#define DEBUG_INFO_LEVEL 2
+
+#define DBG_LEVEL DEBUG_DEBUG_LEVEL
+
+#define txrx_err(fmt, ...) pr_err("txrx_err:<%s,%d>: " fmt, __func__, __LINE__, ##__VA_ARGS__)
+#define sdio_err(fmt, ...) pr_err("sdio_err:<%s,%d>: " fmt, __func__, __LINE__, ##__VA_ARGS__)
+#define usb_err(fmt, ...) pr_err("usb_err:<%s,%d>: " fmt, __func__, __LINE__, ##__VA_ARGS__)
+#if DBG_LEVEL >= DEBUG_DEBUG_LEVEL
+#define sdio_dbg(fmt, ...) printk("aicbt: " fmt, ##__VA_ARGS__)
+#define usb_dbg(fmt, ...) printk("aicbt: " fmt, ##__VA_ARGS__)
+#else
+#define sdio_dbg(fmt, ...)
+#define usb_dbg(fmt, ...)
+#endif
+#if DBG_LEVEL >= DEBUG_INFO_LEVEL
+#define sdio_info(fmt, ...) printk("aicbt: " fmt, ##__VA_ARGS__)
+#define usb_info(fmt, ...) printk("aicbt: " fmt, ##__VA_ARGS__)
+#else
+#define sdio_info(fmt, ...)
+#define usb_info(fmt, ...)
+#endif
+
+enum aicwf_bus_state {
+ BUS_DOWN_ST,
+ BUS_UP_ST
+};
+
+struct aicwf_bus_ops {
+ int (*start) (struct device * dev);
+ void (*stop) (struct device * dev);
+ int (*txdata) (struct device * dev, struct sk_buff * skb);
+ int (*txmsg) (struct device * dev, u8 * msg, uint len);
+};
+
+struct frame_queue {
+ u16 num_prio;
+ u16 hi_prio;
+ u16 qmax; /* max number of queued frames */
+ u16 qcnt;
+ struct sk_buff_head queuelist[8];
+};
+
+struct aicwf_bus {
+ union {
+ struct aic_sdio_dev *sdio;
+ struct aic_usb_dev *usb;
+ } bus_priv;
+ struct device *dev;
+ struct aicwf_bus_ops *ops;
+ enum aicwf_bus_state state;
+ u8 *cmd_buf;
+ struct completion bustx_trgg;
+ struct completion busrx_trgg;
+ struct task_struct *bustx_thread;
+ struct task_struct *busrx_thread;
+};
+
+struct aicwf_tx_priv {
+#ifdef AICWF_SDIO_SUPPORT
+ struct aic_sdio_dev *sdiodev;
+ int fw_avail_bufcnt;
+ //for cmd tx
+ u8 *cmd_buf;
+ uint cmd_len;
+ bool cmd_txstate;
+ bool cmd_tx_succ;
+ struct semaphore cmd_txsema;
+ wait_queue_head_t cmd_txdone_wait;
+ //for data tx
+ atomic_t tx_pktcnt;
+
+ struct frame_queue txq;
+ spinlock_t txqlock;
+ struct semaphore txctl_sema;
+#endif
+#ifdef AICWF_USB_SUPPORT
+ struct aic_usb_dev *usbdev;
+#endif
+ struct sk_buff *aggr_buf;
+ atomic_t aggr_count;
+ u8 *head;
+ u8 *tail;
+};
+
+
+#define MAX_REORD_RXFRAME 250
+#define REORDER_UPDATE_TIME 50
+#define AICWF_REORDER_WINSIZE 64
+#define SN_LESS(a, b) (((a-b)&0x800)!=0)
+#define SN_EQUAL(a, b) (a == b)
+
+struct reord_ctrl {
+ struct aicwf_rx_priv *rx_priv;
+ u8 enable;
+ u16 ind_sn;
+ u8 wsize_b;
+ spinlock_t reord_list_lock;
+ struct list_head reord_list;
+ struct timer_list reord_timer;
+ struct work_struct reord_timer_work;
+};
+
+struct reord_ctrl_info {
+ u8 mac_addr[6];
+ struct reord_ctrl preorder_ctrl[8];
+ struct list_head list;
+};
+
+struct recv_msdu {
+ struct sk_buff *pkt;
+ u8 tid;
+ u16 seq_num;
+ uint len;
+ u8 *rx_data;
+ //for pending rx reorder list
+ struct list_head reord_pending_list;
+ //for total frame list, when rxframe from busif, dequeue, when submit frame to net, enqueue
+ struct list_head rxframe_list;
+ struct reord_ctrl *preorder_ctrl;
+};
+
+struct aicwf_rx_priv {
+ struct aic_usb_dev *usbdev;
+ void *rwnx_vif;
+ atomic_t rx_cnt;
+ u32 data_len;
+ spinlock_t rxqlock;
+ struct frame_queue rxq;
+
+ spinlock_t freeq_lock;
+ struct list_head rxframes_freequeue;
+ struct list_head stas_reord_list;
+ spinlock_t stas_reord_lock;
+ struct recv_msdu *recv_frames;
+};
+
+static inline int aicwf_bus_start(struct aicwf_bus *bus)
+{
+ return bus->ops->start(bus->dev);
+}
+
+static inline void aicwf_bus_stop(struct aicwf_bus *bus)
+{
+ bus->ops->stop(bus->dev);
+}
+
+static inline int aicwf_bus_txdata(struct aicwf_bus *bus, struct sk_buff *skb)
+{
+ return bus->ops->txdata(bus->dev, skb);
+}
+
+static inline int aicwf_bus_txmsg(struct aicwf_bus *bus, u8 *msg, uint len)
+{
+ return bus->ops->txmsg(bus->dev, msg, len);
+}
+
+static inline void aicwf_sched_timeout(u32 millisec)
+{
+ ulong timeout = 0, expires = 0;
+ expires = jiffies + msecs_to_jiffies(millisec);
+ timeout = millisec;
+
+ while (timeout) {
+ timeout = schedule_timeout(timeout);
+ if (time_after(jiffies, expires))
+ break;
+ }
+}
+
+int aicwf_bus_init(uint bus_hdrlen, struct device *dev);
+void aicwf_bus_deinit(struct device *dev);
+void aicwf_tx_deinit(struct aicwf_tx_priv* tx_priv);
+void aicwf_rx_deinit(struct aicwf_rx_priv* rx_priv);
+struct aicwf_tx_priv* aicwf_tx_init(void *arg);
+struct aicwf_rx_priv* aicwf_rx_init(void *arg);
+void aicwf_frame_queue_init(struct frame_queue *pq, int num_prio, int max_len);
+void aicwf_frame_queue_flush(struct frame_queue *pq);
+bool aicwf_frame_enq(struct device *dev, struct frame_queue *q, struct sk_buff *pkt, int prio);
+bool aicwf_rxframe_enqueue(struct device *dev, struct frame_queue *q, struct sk_buff *pkt);
+bool aicwf_is_framequeue_empty(struct frame_queue *pq);
+void aicwf_frame_tx(void *dev, struct sk_buff *skb);
+void aicwf_dev_skb_free(struct sk_buff *skb);
+struct sk_buff *aicwf_frame_dequeue(struct frame_queue *pq);
+struct sk_buff *aicwf_frame_queue_peek_tail(struct frame_queue *pq, int *prio_out);
+
+#endif /* _AICWF_TXRXIF_H_ */
diff --git a/drivers/net/wireless/aic8800/aic_load_fw/aicbluetooth.c b/drivers/net/wireless/aic8800/aic_load_fw/aicbluetooth.c
new file mode 100644
index 000000000000..c4e7cd1c3266
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic_load_fw/aicbluetooth.c
@@ -0,0 +1,1125 @@
+#include <linux/version.h>
+#include <linux/vmalloc.h>
+#include "aicbluetooth_cmds.h"
+#include "aicwf_usb.h"
+#include "aic_txrxif.h"
+#include "md5.h"
+#ifdef CONFIG_USE_FW_REQUEST
+#include <linux/firmware.h>
+#endif
+
+//Parser state
+#define INIT 0
+#define CMD 1
+#define PRINT 2
+#define GET_VALUE 3
+
+typedef struct
+{
+ int8_t enable;
+ int8_t dsss;
+ int8_t ofdmlowrate_2g4;
+ int8_t ofdm64qam_2g4;
+ int8_t ofdm256qam_2g4;
+ int8_t ofdm1024qam_2g4;
+ int8_t ofdmlowrate_5g;
+ int8_t ofdm64qam_5g;
+ int8_t ofdm256qam_5g;
+ int8_t ofdm1024qam_5g;
+} txpwr_idx_conf_t;
+
+
+txpwr_idx_conf_t userconfig_txpwr_idx = {
+ .enable = 1,
+ .dsss = 9,
+ .ofdmlowrate_2g4 = 8,
+ .ofdm64qam_2g4 = 8,
+ .ofdm256qam_2g4 = 8,
+ .ofdm1024qam_2g4 = 8,
+ .ofdmlowrate_5g = 11,
+ .ofdm64qam_5g = 10,
+ .ofdm256qam_5g = 9,
+ .ofdm1024qam_5g = 9
+
+};
+
+typedef struct
+{
+ int8_t enable;
+ int8_t chan_1_4;
+ int8_t chan_5_9;
+ int8_t chan_10_13;
+ int8_t chan_36_64;
+ int8_t chan_100_120;
+ int8_t chan_122_140;
+ int8_t chan_142_165;
+} txpwr_ofst_conf_t;
+
+txpwr_ofst_conf_t userconfig_txpwr_ofst = {
+ .enable = 1,
+ .chan_1_4 = 0,
+ .chan_5_9 = 0,
+ .chan_10_13 = 0,
+ .chan_36_64 = 0,
+ .chan_100_120 = 0,
+ .chan_122_140 = 0,
+ .chan_142_165 = 0
+};
+
+typedef struct
+{
+ int8_t enable;
+ int8_t xtal_cap;
+ int8_t xtal_cap_fine;
+} xtal_cap_conf_t;
+
+
+xtal_cap_conf_t userconfig_xtal_cap = {
+ .enable = 0,
+ .xtal_cap = 24,
+ .xtal_cap_fine = 31,
+};
+
+
+struct aicbt_patch_table {
+ char *name;
+ uint32_t type;
+ uint32_t *data;
+ uint32_t len;
+ struct aicbt_patch_table *next;
+};
+
+struct aicbt_info_t {
+ uint32_t btmode;
+ uint32_t btport;
+ uint32_t uart_baud;
+ uint32_t uart_flowctrl;
+ uint32_t lpm_enable;
+ uint32_t txpwr_lvl;
+};
+
+struct aicbsp_info_t {
+ int hwinfo;
+ uint32_t cpmode;
+};
+
+#define AICBT_PT_TAG "AICBT_PT_TAG"
+#define AICBT_PT_TRAP 0x01
+#define AICBT_PT_B4 0x02
+#define AICBT_PT_BTMODE 0x03
+#define AICBT_PT_PWRON 0x04
+#define AICBT_PT_AF 0x05
+
+enum aicbt_btport_type {
+ AICBT_BTPORT_NULL,
+ AICBT_BTPORT_MB,
+ AICBT_BTPORT_UART,
+};
+
+/* btmode
+ * used for force bt mode,if not AICBSP_MODE_NULL
+ * efuse valid and vendor_info will be invalid, even has beed set valid
+*/
+enum aicbt_btmode_type {
+ AICBT_BTMODE_BT_ONLY_SW = 0x0, // bt only mode with switch
+ AICBT_BTMODE_BT_WIFI_COMBO, // wifi/bt combo mode
+ AICBT_BTMODE_BT_ONLY, // bt only mode without switch
+ AICBT_BTMODE_BT_ONLY_TEST, // bt only test mode
+ AICBT_BTMODE_BT_WIFI_COMBO_TEST, // wifi/bt combo test mode
+ AICBT_BTMODE_BT_ONLY_COANT, // bt only mode with no external switch
+ AICBT_MODE_NULL = 0xFF, // invalid value
+};
+
+/* uart_baud
+ * used for config uart baud when btport set to uart,
+ * otherwise meaningless
+*/
+enum aicbt_uart_baud_type {
+ AICBT_UART_BAUD_115200 = 115200,
+ AICBT_UART_BAUD_921600 = 921600,
+ AICBT_UART_BAUD_1_5M = 1500000,
+ AICBT_UART_BAUD_3_25M = 3250000,
+};
+
+enum aicbt_uart_flowctrl_type {
+ AICBT_UART_FLOWCTRL_DISABLE = 0x0, // uart without flow ctrl
+ AICBT_UART_FLOWCTRL_ENABLE, // uart with flow ctrl
+};
+
+enum aicbsp_cpmode_type {
+ AICBSP_CPMODE_WORK,
+ AICBSP_CPMODE_TEST,
+};
+#define AIC_M2D_OTA_INFO_ADDR 0x88000020
+#define AIC_M2D_OTA_DATA_ADDR 0x88000040
+#define AIC_M2D_OTA_FLASH_ADDR 0x08004000
+#define AIC_M2D_OTA_CODE_START_ADDR 0x08004188
+#define AIC_M2D_OTA_VER_ADDR 0x0800418c
+///aic bt tx pwr lvl :lsb->msb: first byte, min pwr lvl; second byte, max pwr lvl;
+///pwr lvl:20(min), 30 , 40 , 50 , 60(max)
+#define AICBT_TXPWR_LVL 0x00006020
+#define AICBT_TXPWR_LVL_D80 0x00006F2F
+
+#define AICBSP_MODE_BT_HCI_MODE_NULL 0
+#define AICBSP_MODE_BT_HCI_MODE_MB 1
+#define AICBSP_MODE_BT_HCI_MODE_UART 2
+
+#define AICBSP_HWINFO_DEFAULT (-1)
+#define AICBSP_CPMODE_DEFAULT AICBSP_CPMODE_WORK
+
+#define AICBT_BTMODE_DEFAULT AICBT_BTMODE_BT_ONLY
+#define AICBT_BTPORT_DEFAULT AICBT_BTPORT_MB
+#define AICBT_UART_BAUD_DEFAULT AICBT_UART_BAUD_1_5M
+#define AICBT_UART_FC_DEFAULT AICBT_UART_FLOWCTRL_ENABLE
+#define AICBT_LPM_ENABLE_DEFAULT 0
+#define AICBT_TXPWR_LVL_DEFAULT AICBT_TXPWR_LVL
+
+#define AIC_HW_INFO 0x21
+
+#define FW_PATH_MAX 200
+#if defined(CONFIG_PLATFORM_UBUNTU)
+static const char* aic_default_fw_path = "/lib/firmware";
+#else
+static const char* aic_default_fw_path = "/lib/firmware/aic8800";
+#endif
+char aic_fw_path[FW_PATH_MAX];
+module_param_string(aic_fw_path, aic_fw_path, FW_PATH_MAX, 0660);
+#ifdef CONFIG_M2D_OTA_AUTO_SUPPORT
+char saved_sdk_ver[64];
+module_param_string(saved_sdk_ver, saved_sdk_ver,64, 0660);
+#endif
+
+
+int aic_bt_platform_init(struct aic_usb_dev *usbdev)
+{
+ rwnx_cmd_mgr_init(&usbdev->cmd_mgr);
+ usbdev->cmd_mgr.usbdev = (void *)usbdev;
+ return 0;
+
+}
+
+void aic_bt_platform_deinit(struct aic_usb_dev *usbdev)
+{
+
+}
+
+#define MD5(x) x[0],x[1],x[2],x[3],x[4],x[5],x[6],x[7],x[8],x[9],x[10],x[11],x[12],x[13],x[14],x[15]
+#define MD5PINRT "file md5:%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\r\n"
+
+static int aic_load_firmware(u32 ** fw_buf, const char *name, struct device *device)
+{
+
+#ifdef CONFIG_USE_FW_REQUEST
+ const struct firmware *fw = NULL;
+ u32 *dst = NULL;
+ void *buffer=NULL;
+ MD5_CTX md5;
+ unsigned char decrypt[16];
+ int size = 0;
+ int ret = 0;
+
+ printk("%s: request firmware = %s \n", __func__ ,name);
+
+
+ ret = request_firmware(&fw, name, NULL);
+
+ if (ret < 0) {
+ printk("Load %s fail\n", name);
+ release_firmware(fw);
+ return -1;
+ }
+
+ size = fw->size;
+ dst = (u32 *)fw->data;
+
+ if (size <= 0) {
+ printk("wrong size of firmware file\n");
+ release_firmware(fw);
+ return -1;
+ }
+
+
+ buffer = vmalloc(size);
+ memset(buffer, 0, size);
+ memcpy(buffer, dst, size);
+
+ *fw_buf = buffer;
+
+ MD5Init(&md5);
+ MD5Update(&md5, (unsigned char *)buffer, size);
+ MD5Final(&md5, decrypt);
+ printk(MD5PINRT, MD5(decrypt));
+
+ release_firmware(fw);
+
+ return size;
+#else
+ void *buffer=NULL;
+ char *path=NULL;
+ struct file *fp=NULL;
+ int size = 0, len=0, i=0;
+ ssize_t rdlen=0;
+ u32 *src=NULL, *dst = NULL;
+ MD5_CTX md5;
+ unsigned char decrypt[16];
+#if defined(CONFIG_PLATFORM_UBUNTU)
+ struct aicwf_bus *bus_if = dev_get_drvdata(device);
+ struct aic_usb_dev *usb_dev = bus_if->bus_priv.usb;
+#endif
+
+ /* get the firmware path */
+ path = __getname();
+ if (!path){
+ *fw_buf=NULL;
+ return -1;
+ }
+
+ if (strlen(aic_fw_path) > 0) {
+ printk("%s: use customer define fw_path\n", __func__);
+ len = snprintf(path, FW_PATH_MAX, "%s/%s", aic_fw_path, name);
+ } else {
+ #if defined(CONFIG_PLATFORM_UBUNTU)
+ if (usb_dev->chipid == PRODUCT_ID_AIC8800) {
+ len = snprintf(path, FW_PATH_MAX, "%s/%s/%s",aic_default_fw_path, "aic8800", name);
+ } else if (usb_dev->chipid == PRODUCT_ID_AIC8800D80) {
+ len = snprintf(path, FW_PATH_MAX, "%s/%s/%s",aic_default_fw_path, "aic8800D80", name);
+ } else {
+ printk("%s unknown chipid %d\n", __func__, usb_dev->chipid);
+ }
+ #else
+ len = snprintf(path, FW_PATH_MAX, "%s/%s",aic_default_fw_path, name);
+ #endif
+ }
+
+ if (len >= FW_PATH_MAX) {
+ printk("%s: %s file's path too long\n", __func__, name);
+ *fw_buf=NULL;
+ __putname(path);
+ return -1;
+ }
+
+ printk("%s :firmware path = %s \n", __func__ ,path);
+
+
+ /* open the firmware file */
+ fp=filp_open(path, O_RDONLY, 0);
+ if(IS_ERR(fp) || (!fp)){
+ printk("%s: %s file failed to open\n", __func__, name);
+ if(IS_ERR(fp))
+ printk("is_Err\n");
+ if((!fp))
+ printk("null\n");
+ *fw_buf=NULL;
+ __putname(path);
+ fp=NULL;
+ return -1;
+ }
+
+ size = i_size_read(file_inode(fp));
+ if(size<=0){
+ printk("%s: %s file size invalid %d\n", __func__, name, size);
+ *fw_buf=NULL;
+ __putname(path);
+ filp_close(fp,NULL);
+ fp=NULL;
+ return -1;
+}
+
+ /* start to read from firmware file */
+ buffer = vmalloc(size);
+ memset(buffer, 0, size);
+ if(!buffer){
+ *fw_buf=NULL;
+ __putname(path);
+ filp_close(fp,NULL);
+ fp=NULL;
+ return -1;
+ }
+
+
+ #if LINUX_VERSION_CODE > KERNEL_VERSION(4, 13, 16)
+ rdlen = kernel_read(fp, buffer, size, &fp->f_pos);
+ #else
+ rdlen = kernel_read(fp, fp->f_pos, buffer, size);
+ #endif
+
+ if(size != rdlen){
+ printk("%s: %s file rdlen invalid %d %d\n", __func__, name, (int)rdlen, size);
+ *fw_buf=NULL;
+ __putname(path);
+ filp_close(fp,NULL);
+ fp=NULL;
+ vfree(buffer);
+ buffer=NULL;
+ return -1;
+ }
+ if(rdlen > 0){
+ fp->f_pos += rdlen;
+ //printk("f_pos=%d\n", (int)fp->f_pos);
+ }
+
+
+ /*start to transform the data format*/
+ src = (u32*)buffer;
+ //printk("malloc dst\n");
+ dst = (u32*)vmalloc(size);
+ memset(dst, 0, size);
+
+ if(!dst){
+ *fw_buf=NULL;
+ __putname(path);
+ filp_close(fp,NULL);
+ fp=NULL;
+ vfree(buffer);
+ buffer=NULL;
+ return -1;
+ }
+
+ for(i=0;i<(size/4);i++){
+ dst[i] = src[i];
+ }
+
+ __putname(path);
+ filp_close(fp,NULL);
+ fp=NULL;
+ vfree(buffer);
+ buffer=NULL;
+ *fw_buf = dst;
+
+ MD5Init(&md5);
+ MD5Update(&md5, (unsigned char *)dst, size);
+ MD5Final(&md5, decrypt);
+
+ printk(MD5PINRT, MD5(decrypt));
+
+ return size;
+#endif
+
+}
+
+int rwnx_plat_bin_fw_upload_android(struct aic_usb_dev *usbdev, u32 fw_addr,
+ char *filename)
+{
+ struct device *dev = usbdev->dev;
+ unsigned int i=0;
+ int size;
+ u32 *dst=NULL;
+ int err=0;
+
+ /* load aic firmware */
+ size = aic_load_firmware(&dst, filename, dev);
+ if(size<=0){
+ printk("wrong size of firmware file\n");
+ vfree(dst);
+ dst = NULL;
+ return -1;
+ }
+
+ /* Copy the file on the Embedded side */
+ printk("### Upload %s firmware, @ = %x size=%d\n", filename, fw_addr, size);
+
+ if (size > 1024) {// > 1KB data
+ for (i = 0; i < (size - 1024); i += 1024) {//each time write 1KB
+ err = rwnx_send_dbg_mem_block_write_req(usbdev, fw_addr + i, 1024, dst + i / 4);
+ if (err) {
+ printk("bin upload fail: %x, err:%d\r\n", fw_addr + i, err);
+ break;
+ }
+ }
+ }
+
+ if (!err && (i < size)) {// <1KB data
+ err = rwnx_send_dbg_mem_block_write_req(usbdev, fw_addr + i, size - i, dst + i / 4);
+ if (err) {
+ printk("bin upload fail: %x, err:%d\r\n", fw_addr + i, err);
+ }
+ }
+
+ if (dst) {
+ vfree(dst);
+ dst = NULL;
+ }
+
+ printk("fw download complete\n\n");
+
+ return err;
+}
+
+extern int testmode;
+#ifdef CONFIG_M2D_OTA_AUTO_SUPPORT
+int rwnx_plat_m2d_flash_ota_android(struct aic_usb_dev *usbdev, char *filename)
+{
+ struct device *dev = usbdev->dev;
+ unsigned int i=0;
+ int size;
+ u32 *dst=NULL;
+ int err=0;
+ int ret;
+ u8 bond_id;
+ const u32 mem_addr = 0x40500000;
+ struct dbg_mem_read_cfm rd_mem_addr_cfm;
+
+ ret = rwnx_send_dbg_mem_read_req(usbdev, mem_addr, &rd_mem_addr_cfm);
+ if (ret) {
+ printk("m2d %x rd fail: %d\n", mem_addr, ret);
+ return ret;
+ }
+ bond_id = (u8)(rd_mem_addr_cfm.memdata >> 24);
+ printk("%x=%x\n", rd_mem_addr_cfm.memaddr, rd_mem_addr_cfm.memdata);
+ if (bond_id & (1<<1)) {
+ //flash is invalid
+ printk("m2d flash is invalid\n");
+ return -1;
+ }
+
+ /* load aic firmware */
+ size = aic_load_firmware(&dst, filename, dev);
+ if(size<=0){
+ printk("wrong size of m2d file\n");
+ vfree(dst);
+ dst = NULL;
+ return -1;
+ }
+
+ /* Copy the file on the Embedded side */
+ printk("### Upload m2d %s flash, size=%d\n", filename, size);
+
+ /*send info first*/
+ err = rwnx_send_dbg_mem_block_write_req(usbdev, AIC_M2D_OTA_INFO_ADDR, 4, (u32 *)&size);
+
+ /*send data first*/
+ if (size > 1024) {// > 1KB data
+ for (i = 0; i < (size - 1024); i += 1024) {//each time write 1KB
+ err = rwnx_send_dbg_mem_block_write_req(usbdev, AIC_M2D_OTA_DATA_ADDR, 1024, dst + i / 4);
+ if (err) {
+ printk("m2d upload fail: %x, err:%d\r\n", AIC_M2D_OTA_DATA_ADDR, err);
+ break;
+ }
+ }
+ }
+
+ if (!err && (i < size)) {// <1KB data
+ err = rwnx_send_dbg_mem_block_write_req(usbdev, AIC_M2D_OTA_DATA_ADDR, size - i, dst + i / 4);
+ if (err) {
+ printk("m2d upload fail: %x, err:%d\r\n", AIC_M2D_OTA_DATA_ADDR, err);
+ }
+ }
+
+ if (dst) {
+ vfree(dst);
+ dst = NULL;
+ }
+ testmode = FW_NORMAL_MODE;
+
+ printk("m2d flash update complete\n\n");
+
+ return err;
+}
+
+int rwnx_plat_m2d_flash_ota_check(struct aic_usb_dev *usbdev, char *filename)
+{
+ struct device *dev = usbdev->dev;
+ unsigned int i=0,j=0;
+ int size;
+ u32 *dst=NULL;
+ int err=0;
+ int ret=0;
+ u8 bond_id;
+ const u32 mem_addr = 0x40500000;
+ const u32 mem_addr_code_start = AIC_M2D_OTA_CODE_START_ADDR;
+ const u32 mem_addr_sdk_ver = AIC_M2D_OTA_VER_ADDR;
+ const u32 driver_code_start_idx = (AIC_M2D_OTA_CODE_START_ADDR-AIC_M2D_OTA_FLASH_ADDR)/4;
+ const u32 driver_sdk_ver_idx = (AIC_M2D_OTA_VER_ADDR-AIC_M2D_OTA_FLASH_ADDR)/4;
+ u32 driver_sdk_ver_addr_idx = 0;
+ u32 code_start_addr = 0xffffffff;
+ u32 sdk_ver_addr = 0xffffffff;
+ u32 drv_code_start_addr = 0xffffffff;
+ u32 drv_sdk_ver_addr = 0xffffffff;
+ struct dbg_mem_read_cfm rd_mem_addr_cfm;
+ char m2d_sdk_ver[64];
+ char flash_sdk_ver[64];
+ u32 flash_ver[16];
+ u32 ota_ver[16];
+
+ ret = rwnx_send_dbg_mem_read_req(usbdev, mem_addr, &rd_mem_addr_cfm);
+ if (ret) {
+ printk("m2d %x rd fail: %d\n", mem_addr, ret);
+ return ret;
+ }
+ bond_id = (u8)(rd_mem_addr_cfm.memdata >> 24);
+ printk("%x=%x\n", rd_mem_addr_cfm.memaddr, rd_mem_addr_cfm.memdata);
+ if (bond_id & (1<<1)) {
+ //flash is invalid
+ printk("m2d flash is invalid\n");
+ return -1;
+ }
+ ret = rwnx_send_dbg_mem_read_req(usbdev, mem_addr_code_start, &rd_mem_addr_cfm);
+ if (ret){
+ printk("mem_addr_code_start %x rd fail: %d\n", mem_addr_code_start, ret);
+ return ret;
+ }
+ code_start_addr = rd_mem_addr_cfm.memdata;
+
+ ret = rwnx_send_dbg_mem_read_req(usbdev, mem_addr_sdk_ver, &rd_mem_addr_cfm);
+ if (ret){
+ printk("mem_addr_sdk_ver %x rd fail: %d\n", mem_addr_code_start, ret);
+ return ret;
+ }
+ sdk_ver_addr = rd_mem_addr_cfm.memdata;
+ printk("code_start_addr: 0x%x, sdk_ver_addr: 0x%x\n", code_start_addr,sdk_ver_addr);
+
+ /* load aic firmware */
+ size = aic_load_firmware(&dst, filename, dev);
+ if(size<=0){
+ printk("wrong size of m2d file\n");
+ vfree(dst);
+ dst = NULL;
+ return -1;
+ }
+ if(code_start_addr == 0xffffffff && sdk_ver_addr == 0xffffffff) {
+ printk("########m2d flash old version , must be upgrade\n");
+ drv_code_start_addr = dst[driver_code_start_idx];
+ drv_sdk_ver_addr = dst[driver_sdk_ver_idx];
+
+ printk("drv_code_start_addr: 0x%x, drv_sdk_ver_addr: 0x%x\n", drv_code_start_addr,drv_sdk_ver_addr);
+
+ if(drv_sdk_ver_addr == 0xffffffff){
+ printk("########driver m2d_ota.bin is old ,not need upgrade\n");
+ return -1;
+ }
+
+ } else {
+ for(i=0;i<16;i++){
+ ret = rwnx_send_dbg_mem_read_req(usbdev, (sdk_ver_addr+i*4), &rd_mem_addr_cfm);
+ if (ret){
+ printk("mem_addr_sdk_ver %x rd fail: %d\n", mem_addr_code_start, ret);
+ return ret;
+ }
+ flash_ver[i] = rd_mem_addr_cfm.memdata;
+ }
+ memcpy((u8 *)flash_sdk_ver,(u8 *)flash_ver,64);
+ memcpy((u8 *)saved_sdk_ver,(u8 *)flash_sdk_ver,64);
+ printk("flash SDK Version: %s\r\n\r\n", flash_sdk_ver);
+
+ drv_code_start_addr = dst[driver_code_start_idx];
+ drv_sdk_ver_addr = dst[driver_sdk_ver_idx];
+
+ printk("drv_code_start_addr: 0x%x, drv_sdk_ver_addr: 0x%x\n", drv_code_start_addr,drv_sdk_ver_addr);
+
+ if(drv_sdk_ver_addr == 0xffffffff){
+ printk("########driver m2d_ota.bin is old ,not need upgrade\n");
+ return -1;
+ }
+
+ driver_sdk_ver_addr_idx = (drv_sdk_ver_addr-drv_code_start_addr)/4;
+ printk("driver_sdk_ver_addr_idx %d\n",driver_sdk_ver_addr_idx);
+
+ if (driver_sdk_ver_addr_idx){
+ for(j = 0; j < 16; j++){
+ ota_ver[j] = dst[driver_sdk_ver_addr_idx+j];
+ }
+ memcpy((u8 *)m2d_sdk_ver,(u8 *)ota_ver,64);
+ printk("m2d_ota SDK Version: %s\r\n\r\n", m2d_sdk_ver);
+ } else {
+ return -1;
+ }
+
+ if(!strcmp(m2d_sdk_ver,flash_sdk_ver)){
+ printk("######## m2d %s flash is not need upgrade\r\n", filename);
+ return -1;
+ }
+ }
+
+ /* Copy the file on the Embedded side */
+ printk("### Upload m2d %s flash, size=%d\n", filename, size);
+
+ /*send info first*/
+ err = rwnx_send_dbg_mem_block_write_req(usbdev, AIC_M2D_OTA_INFO_ADDR, 4, (u32 *)&size);
+
+ /*send data first*/
+ if (size > 1024) {// > 1KB data
+ for (i = 0; i < (size - 1024); i += 1024) {//each time write 1KB
+ err = rwnx_send_dbg_mem_block_write_req(usbdev, AIC_M2D_OTA_DATA_ADDR, 1024, dst + i / 4);
+ if (err) {
+ printk("m2d upload fail: %x, err:%d\r\n", AIC_M2D_OTA_DATA_ADDR, err);
+ break;
+ }
+ }
+ }
+
+ if (!err && (i < size)) {// <1KB data
+ err = rwnx_send_dbg_mem_block_write_req(usbdev, AIC_M2D_OTA_DATA_ADDR, size - i, dst + i / 4);
+ if (err) {
+ printk("m2d upload fail: %x, err:%d\r\n", AIC_M2D_OTA_DATA_ADDR, err);
+ }
+ }
+
+ if (dst) {
+ vfree(dst);
+ dst = NULL;
+ }
+ testmode = FW_NORMAL_MODE;
+
+ printk("m2d flash update complete\n\n");
+
+ return err;
+}
+#endif//CONFIG_M2D_OTA_AUTO_SUPPORT
+
+uint32_t rwnx_atoli(char *value){
+ int len = 0;
+ int temp_len = 0;
+ int i = 0;
+ uint32_t result = 0;
+
+ temp_len = strlen(value);
+
+ for(i = 0;i < temp_len; i++){
+ if((value[i] >= 48 && value[i] <= 57) ||
+ (value[i] >= 65 && value[i] <= 70) ||
+ (value[i] >= 97 && value[i] <= 102)){
+ len++;
+ }
+ }
+
+ //printk("%s len:%d \r\n", __func__, len);
+
+ for(i = 0; i < len; i++){
+ result = result * 16;
+ if(value[i] >= 48 && value[i] <= 57){
+ result += value[i] - 48;
+ }else if(value[i] >= 65 && value[i] <= 70){
+ result += (value[i] - 65) + 10;
+ }else if(value[i] >= 97 && value[i] <= 102){
+ result += (value[i] - 97) + 10;
+ }
+ }
+
+ return result;
+}
+
+int8_t rwnx_atoi(char *value){
+ int len = 0;
+ int i = 0;
+ int8_t result = 0;
+ int8_t signal = 1;
+
+ len = strlen(value);
+ //printk("%s len:%d \r\n", __func__, len);
+
+ for(i = 0;i < len ;i++){
+ if(i == 0 && value[0] == '-'){
+ signal = -1;
+ continue;
+ }
+
+ result = result * 10;
+ if(value[i] >= 48 && value[i] <= 57){
+ result += value[i] - 48;
+ }else{
+ result = 0;
+ break;
+ }
+ }
+
+ result = result * signal;
+ //printk("%s result:%d \r\n", __func__, result);
+
+ return result;
+}
+
+void get_fw_path(char* fw_path){
+ if (strlen(aic_fw_path) > 0) {
+ memcpy(fw_path, aic_fw_path, strlen(aic_fw_path));
+ }else{
+ memcpy(fw_path, aic_default_fw_path, strlen(aic_default_fw_path));
+ }
+}
+
+void set_testmode(int val){
+ testmode = val;
+}
+
+int get_testmode(void){
+ return testmode;
+}
+
+int get_hardware_info(void){
+ return AIC_HW_INFO;
+}
+
+extern int adap_test;
+int get_adap_test(void){
+ return adap_test;
+}
+
+EXPORT_SYMBOL(get_fw_path);
+
+EXPORT_SYMBOL(get_testmode);
+
+EXPORT_SYMBOL(set_testmode);
+
+EXPORT_SYMBOL(get_hardware_info);
+
+EXPORT_SYMBOL(get_adap_test);
+
+
+void get_userconfig_xtal_cap(xtal_cap_conf_t *xtal_cap)
+{
+ xtal_cap->enable = userconfig_xtal_cap.enable;
+ xtal_cap->xtal_cap = userconfig_xtal_cap.xtal_cap;
+ xtal_cap->xtal_cap_fine = userconfig_xtal_cap.xtal_cap_fine;
+
+ printk("%s:enable :%d\r\n", __func__, xtal_cap->enable);
+ printk("%s:xtal_cap :%d\r\n", __func__, xtal_cap->xtal_cap);
+ printk("%s:xtal_cap_fine:%d\r\n", __func__, xtal_cap->xtal_cap_fine);
+}
+
+EXPORT_SYMBOL(get_userconfig_xtal_cap);
+
+void get_userconfig_txpwr_idx(txpwr_idx_conf_t *txpwr_idx){
+ txpwr_idx->enable = userconfig_txpwr_idx.enable;
+ txpwr_idx->dsss = userconfig_txpwr_idx.dsss;
+ txpwr_idx->ofdmlowrate_2g4 = userconfig_txpwr_idx.ofdmlowrate_2g4;
+ txpwr_idx->ofdm64qam_2g4 = userconfig_txpwr_idx.ofdm64qam_2g4;
+ txpwr_idx->ofdm256qam_2g4 = userconfig_txpwr_idx.ofdm256qam_2g4;
+ txpwr_idx->ofdm1024qam_2g4 = userconfig_txpwr_idx.ofdm1024qam_2g4;
+ txpwr_idx->ofdmlowrate_5g = userconfig_txpwr_idx.ofdmlowrate_5g;
+ txpwr_idx->ofdm64qam_5g = userconfig_txpwr_idx.ofdm64qam_5g;
+ txpwr_idx->ofdm256qam_5g = userconfig_txpwr_idx.ofdm256qam_5g;
+ txpwr_idx->ofdm1024qam_5g = userconfig_txpwr_idx.ofdm1024qam_5g;
+
+ printk("%s:enable:%d\r\n", __func__, txpwr_idx->enable);
+ printk("%s:dsss:%d\r\n", __func__, txpwr_idx->dsss);
+ printk("%s:ofdmlowrate_2g4:%d\r\n", __func__, txpwr_idx->ofdmlowrate_2g4);
+ printk("%s:ofdm64qam_2g4:%d\r\n", __func__, txpwr_idx->ofdm64qam_2g4);
+ printk("%s:ofdm256qam_2g4:%d\r\n", __func__, txpwr_idx->ofdm256qam_2g4);
+ printk("%s:ofdm1024qam_2g4:%d\r\n", __func__, txpwr_idx->ofdm1024qam_2g4);
+ printk("%s:ofdmlowrate_5g:%d\r\n", __func__, txpwr_idx->ofdmlowrate_5g);
+ printk("%s:ofdm64qam_5g:%d\r\n", __func__, txpwr_idx->ofdm64qam_5g);
+ printk("%s:ofdm256qam_5g:%d\r\n", __func__, txpwr_idx->ofdm256qam_5g);
+ printk("%s:ofdm1024qam_5g:%d\r\n", __func__, txpwr_idx->ofdm1024qam_5g);
+
+}
+
+EXPORT_SYMBOL(get_userconfig_txpwr_idx);
+
+void get_userconfig_txpwr_ofst(txpwr_ofst_conf_t *txpwr_ofst){
+ txpwr_ofst->enable = userconfig_txpwr_ofst.enable;
+ txpwr_ofst->chan_1_4 = userconfig_txpwr_ofst.chan_1_4;
+ txpwr_ofst->chan_5_9 = userconfig_txpwr_ofst.chan_5_9;
+ txpwr_ofst->chan_10_13 = userconfig_txpwr_ofst.chan_10_13;
+ txpwr_ofst->chan_36_64 = userconfig_txpwr_ofst.chan_36_64;
+ txpwr_ofst->chan_100_120 = userconfig_txpwr_ofst.chan_100_120;
+ txpwr_ofst->chan_122_140 = userconfig_txpwr_ofst.chan_122_140;
+ txpwr_ofst->chan_142_165 = userconfig_txpwr_ofst.chan_142_165;
+
+ printk("%s:ofst_enable:%d\r\n", __func__, txpwr_ofst->enable);
+ printk("%s:ofst_chan_1_4:%d\r\n", __func__, txpwr_ofst->chan_1_4);
+ printk("%s:ofst_chan_5_9:%d\r\n", __func__, txpwr_ofst->chan_5_9);
+ printk("%s:ofst_chan_10_13:%d\r\n", __func__, txpwr_ofst->chan_10_13);
+ printk("%s:ofst_chan_36_64:%d\r\n", __func__, txpwr_ofst->chan_36_64);
+ printk("%s:ofst_chan_100_120:%d\r\n", __func__, txpwr_ofst->chan_100_120);
+ printk("%s:ofst_chan_122_140:%d\r\n", __func__, txpwr_ofst->chan_122_140);
+ printk("%s:ofst_chan_142_165:%d\r\n", __func__, txpwr_ofst->chan_142_165);
+
+}
+
+EXPORT_SYMBOL(get_userconfig_txpwr_ofst);
+
+void rwnx_plat_userconfig_set_value(char *command, char *value){
+ //TODO send command
+ printk("%s:command=%s value=%s \r\n", __func__, command, value);
+ if(!strcmp(command, "enable")){
+ userconfig_txpwr_idx.enable = rwnx_atoi(value);
+ }else if(!strcmp(command, "dsss")){
+ userconfig_txpwr_idx.dsss = rwnx_atoi(value);
+ }else if(!strcmp(command, "ofdmlowrate_2g4")){
+ userconfig_txpwr_idx.ofdmlowrate_2g4 = rwnx_atoi(value);
+ }else if(!strcmp(command, "ofdm64qam_2g4")){
+ userconfig_txpwr_idx.ofdm64qam_2g4 = rwnx_atoi(value);
+ }else if(!strcmp(command, "ofdm256qam_2g4")){
+ userconfig_txpwr_idx.ofdm256qam_2g4 = rwnx_atoi(value);
+ }else if(!strcmp(command, "ofdm1024qam_2g4")){
+ userconfig_txpwr_idx.ofdm1024qam_2g4 = rwnx_atoi(value);
+ }else if(!strcmp(command, "ofdmlowrate_5g")){
+ userconfig_txpwr_idx.ofdmlowrate_5g = rwnx_atoi(value);
+ }else if(!strcmp(command, "ofdm64qam_5g")){
+ userconfig_txpwr_idx.ofdm64qam_5g = rwnx_atoi(value);
+ }else if(!strcmp(command, "ofdm256qam_5g")){
+ userconfig_txpwr_idx.ofdm256qam_5g = rwnx_atoi(value);
+ }else if(!strcmp(command, "ofdm1024qam_5g")){
+ userconfig_txpwr_idx.ofdm1024qam_5g = rwnx_atoi(value);
+ }else if(!strcmp(command, "ofst_enable")){
+ userconfig_txpwr_ofst.enable = rwnx_atoi(value);
+ }else if(!strcmp(command, "ofst_chan_1_4")){
+ userconfig_txpwr_ofst.chan_1_4 = rwnx_atoi(value);
+ }else if(!strcmp(command, "ofst_chan_5_9")){
+ userconfig_txpwr_ofst.chan_5_9 = rwnx_atoi(value);
+ }else if(!strcmp(command, "ofst_chan_10_13")){
+ userconfig_txpwr_ofst.chan_10_13 = rwnx_atoi(value);
+ }else if(!strcmp(command, "ofst_chan_36_64")){
+ userconfig_txpwr_ofst.chan_36_64 = rwnx_atoi(value);
+ }else if(!strcmp(command, "ofst_chan_100_120")){
+ userconfig_txpwr_ofst.chan_100_120 = rwnx_atoi(value);
+ }else if(!strcmp(command, "ofst_chan_122_140")){
+ userconfig_txpwr_ofst.chan_122_140 = rwnx_atoi(value);
+ }else if(!strcmp(command, "ofst_chan_142_165")){
+ userconfig_txpwr_ofst.chan_142_165 = rwnx_atoi(value);
+ }else if(!strcmp(command, "xtal_enable")){
+ userconfig_xtal_cap.enable = rwnx_atoi(value);
+ }else if(!strcmp(command, "xtal_cap")){
+ userconfig_xtal_cap.xtal_cap = rwnx_atoi(value);
+ }else if(!strcmp(command, "xtal_cap_fine")){
+ userconfig_xtal_cap.xtal_cap_fine = rwnx_atoi(value);
+ }
+}
+
+void rwnx_plat_userconfig_parsing(char *buffer, int size){
+ int i = 0;
+ int parse_state = 0;
+ char command[30];
+ char value[100];
+ int char_counter = 0;
+
+ memset(command, 0, 30);
+ memset(value, 0, 100);
+
+ for(i = 0; i < size; i++){
+
+ //Send command or print nvram log when char is \r or \n
+ if(buffer[i] == 0x0a || buffer[i] == 0x0d){
+ if(command[0] != 0 && value[0] != 0){
+ if(parse_state == PRINT){
+ printk("%s:%s\r\n", __func__, value);
+ }else if(parse_state == GET_VALUE){
+ rwnx_plat_userconfig_set_value(command, value);
+ }
+ }
+ //Reset command value and char_counter
+ memset(command, 0, 30);
+ memset(value, 0, 100);
+ char_counter = 0;
+ parse_state = INIT;
+ continue;
+ }
+
+ //Switch parser state
+ if(parse_state == INIT){
+ if(buffer[i] == '#'){
+ parse_state = PRINT;
+ continue;
+ }else if(buffer[i] == 0x0a || buffer[i] == 0x0d){
+ parse_state = INIT;
+ continue;
+ }else{
+ parse_state = CMD;
+ }
+ }
+
+ //Fill data to command and value
+ if(parse_state == PRINT){
+ command[0] = 0x01;
+ value[char_counter] = buffer[i];
+ char_counter++;
+ }else if(parse_state == CMD){
+ if(command[0] != 0 && buffer[i] == '='){
+ parse_state = GET_VALUE;
+ char_counter = 0;
+ continue;
+ }
+ command[char_counter] = buffer[i];
+ char_counter++;
+ }else if(parse_state == GET_VALUE){
+ value[char_counter] = buffer[i];
+ char_counter++;
+ }
+ }
+
+
+}
+
+int rwnx_plat_userconfig_upload_android(struct aic_usb_dev *usbdev, char *filename){
+ int size;
+ u32 *dst=NULL;
+ struct device *dev = usbdev->dev;
+
+ printk("userconfig file path:%s \r\n", filename);
+
+ /* load aic firmware */
+ size = aic_load_firmware(&dst, filename, dev);
+ if(size <= 0){
+ printk("wrong size of firmware file\n");
+ vfree(dst);
+ dst = NULL;
+ return 0;
+ }
+
+ /* Copy the file on the Embedded side */
+ printk("### Upload %s userconfig, size=%d\n", filename, size);
+
+ rwnx_plat_userconfig_parsing((char *)dst, size);
+
+ if (dst) {
+ vfree(dst);
+ dst = NULL;
+ }
+
+ printk("userconfig download complete\n\n");
+ return 0;
+}
+
+
+
+int aicbt_patch_table_free(struct aicbt_patch_table **head)
+{
+ struct aicbt_patch_table *p = *head, *n = NULL;
+ while (p) {
+ n = p->next;
+ vfree(p->name);
+ vfree(p->data);
+ vfree(p);
+ p = n;
+ }
+ *head = NULL;
+ return 0;
+}
+
+struct aicbsp_info_t aicbsp_info = {
+ .hwinfo = AICBSP_HWINFO_DEFAULT,
+ .cpmode = AICBSP_CPMODE_DEFAULT,
+};
+
+static struct aicbt_info_t aicbt_info = {
+ .btmode = AICBT_BTMODE_DEFAULT,
+ .btport = AICBT_BTPORT_DEFAULT,
+ .uart_baud = AICBT_UART_BAUD_DEFAULT,
+ .uart_flowctrl = AICBT_UART_FC_DEFAULT,
+ .lpm_enable = AICBT_LPM_ENABLE_DEFAULT,
+ .txpwr_lvl = AICBT_TXPWR_LVL_DEFAULT,
+};
+
+int aicbt_patch_table_load(struct aic_usb_dev *usbdev, struct aicbt_patch_table *_head)
+{
+ struct aicbt_patch_table *head, *p;
+ int ret = 0, i;
+ uint32_t *data = NULL;
+
+ head = _head;
+ if (usbdev->chipid == PRODUCT_ID_AIC8800D80) {
+ //aicbt_info.btmode = AICBT_BTMODE_BT_ONLY_COANT;
+ aicbt_info.txpwr_lvl = AICBT_TXPWR_LVL_D80;
+ }
+ for (p = head; p != NULL; p = p->next) {
+ data = p->data;
+ if(AICBT_PT_BTMODE == p->type){
+ *(data + 1) = aicbsp_info.hwinfo < 0;
+ *(data + 3) = aicbsp_info.hwinfo;
+ *(data + 5) = aicbsp_info.cpmode;
+
+ *(data + 7) = aicbt_info.btmode;
+ *(data + 9) = aicbt_info.btport;
+ *(data + 11) = aicbt_info.uart_baud;
+ *(data + 13) = aicbt_info.uart_flowctrl;
+ *(data + 15) = aicbt_info.lpm_enable;
+ *(data + 17) = aicbt_info.txpwr_lvl;
+
+ }
+ if (p->type == 0x06) {
+ char *data_s = (char *)p->data;
+ printk("patch version %s\n", data_s);
+ continue;
+ }
+ for (i = 0; i < p->len; i++) {
+ ret = rwnx_send_dbg_mem_write_req(usbdev, *data, *(data + 1));
+ if (ret != 0)
+ return ret;
+ data += 2;
+ }
+ if (p->type == AICBT_PT_PWRON)
+ udelay(500);
+ }
+ aicbt_patch_table_free(&head);
+ return 0;
+}
+
+
+int rwnx_plat_bin_fw_patch_table_upload_android(struct aic_usb_dev *usbdev, char *filename){
+ struct device *dev = usbdev->dev;
+ struct aicbt_patch_table *head = NULL;
+ struct aicbt_patch_table *new = NULL;
+ struct aicbt_patch_table *cur = NULL;
+ int size;
+ int ret = 0;
+ uint8_t *rawdata=NULL;
+ uint8_t *p = NULL;
+
+ /* load aic firmware */
+ size = aic_load_firmware((u32 **)&rawdata, filename, dev);
+
+ /* Copy the file on the Embedded side */
+ printk("### Upload %s fw_patch_table, size=%d\n", filename, size);
+
+ if (size <= 0) {
+ printk("wrong size of firmware file\n");
+ ret = -1;
+ goto err;
+ }
+
+ p = rawdata;
+
+ if (memcmp(p, AICBT_PT_TAG, sizeof(AICBT_PT_TAG) < 16 ? sizeof(AICBT_PT_TAG) : 16)) {
+ printk("TAG err\n");
+ ret = -1;
+ goto err;
+ }
+ p += 16;
+
+ while (p - rawdata < size) {
+ //printk("size = %d p - rawdata = %d \r\n", size, p - rawdata);
+ new = (struct aicbt_patch_table *)vmalloc(sizeof(struct aicbt_patch_table));
+ memset(new, 0, sizeof(struct aicbt_patch_table));
+ if (head == NULL) {
+ head = new;
+ cur = new;
+ } else {
+ cur->next = new;
+ cur = cur->next;
+ }
+
+ cur->name = (char *)vmalloc(sizeof(char) * 16);
+ memset(cur->name, 0, sizeof(char) * 16);
+ memcpy(cur->name, p, 16);
+ p += 16;
+
+ cur->type = *(uint32_t *)p;
+ p += 4;
+
+ cur->len = *(uint32_t *)p;
+ p += 4;
+
+ if((cur->type ) >= 1000 || cur->len == 0) {//Temp Workaround
+ cur->len = 0;
+ }else{
+ cur->data = (uint32_t *)vmalloc(sizeof(uint8_t) * cur->len * 8);
+ memset(cur->data, 0, sizeof(uint8_t) * cur->len * 8);
+ memcpy(cur->data, p, cur->len * 8);
+ p += cur->len * 8;
+ }
+ }
+
+ vfree(rawdata);
+ aicbt_patch_table_load(usbdev, head);
+ printk("fw_patch_table download complete\n\n");
+
+ return ret;
+err:
+ //aicbt_patch_table_free(&head);
+
+ if (rawdata){
+ vfree(rawdata);
+ }
+ return ret;
+}
+
+
diff --git a/drivers/net/wireless/aic8800/aic_load_fw/aicbluetooth.h b/drivers/net/wireless/aic8800/aic_load_fw/aicbluetooth.h
new file mode 100644
index 000000000000..d6fe5eacfe70
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic_load_fw/aicbluetooth.h
@@ -0,0 +1,22 @@
+#ifndef _AICBLUETOOTH_H
+#define _AICBLUETOOTH_H
+
+int aic_bt_platform_init(struct aic_usb_dev *sdiodev);
+
+void aic_bt_platform_deinit(struct aic_usb_dev *sdiodev);
+
+int rwnx_plat_bin_fw_upload_android(struct aic_usb_dev *sdiodev, u32 fw_addr,
+ char *filename);
+
+int rwnx_plat_m2d_flash_ota_android(struct aic_usb_dev *usbdev, char *filename);
+
+int rwnx_plat_m2d_flash_ota_check(struct aic_usb_dev *usbdev, char *filename);
+
+int rwnx_plat_bin_fw_patch_table_upload_android(struct aic_usb_dev *usbdev, char *filename);
+
+int rwnx_plat_userconfig_upload_android(struct aic_usb_dev *usbdev, char *filename);
+
+uint8_t rwnx_atoi(char *value);
+uint32_t rwnx_atoli(char *value);
+
+#endif
diff --git a/drivers/net/wireless/aic8800/aic_load_fw/aicbluetooth_cmds.c b/drivers/net/wireless/aic8800/aic_load_fw/aicbluetooth_cmds.c
new file mode 100644
index 000000000000..67078a447c44
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic_load_fw/aicbluetooth_cmds.c
@@ -0,0 +1,472 @@
+/**
+ ******************************************************************************
+ *
+ * rwnx_cmds.c
+ *
+ * Handles queueing (push to IPC, ack/cfm from IPC) of commands issued to
+ * LMAC FW
+ *
+ * Copyright (C) RivieraWaves 2014-2019
+ *
+ ******************************************************************************
+ */
+
+#include <linux/list.h>
+#if 0
+#include <linux/stddef.h>
+#endif
+#include <linux/version.h>
+#include "aicbluetooth_cmds.h"
+#include "aic_txrxif.h"
+#include "aicwf_usb.h"
+
+//extern int aicwf_sdio_writeb(struct aic_sdio_dev *sdiodev, uint regaddr, u8 val);
+
+static void cmd_dump(const struct rwnx_cmd *cmd)
+{
+ printk(KERN_CRIT "tkn[%d] flags:%04x result:%3d cmd:%4d - reqcfm(%4d)\n",
+ cmd->tkn, cmd->flags, cmd->result, cmd->id, cmd->reqid);
+}
+
+static void cmd_complete(struct rwnx_cmd_mgr *cmd_mgr, struct rwnx_cmd *cmd)
+{
+ //printk("cmdcmp\n");
+ lockdep_assert_held(&cmd_mgr->lock);
+
+ list_del(&cmd->list);
+ cmd_mgr->queue_sz--;
+
+ cmd->flags |= RWNX_CMD_FLAG_DONE;
+ if (cmd->flags & RWNX_CMD_FLAG_NONBLOCK) {
+ kfree(cmd);
+ } else {
+ if (RWNX_CMD_WAIT_COMPLETE(cmd->flags)) {
+ cmd->result = 0;
+ complete(&cmd->complete);
+ }
+ }
+}
+
+static int cmd_mgr_queue(struct rwnx_cmd_mgr *cmd_mgr, struct rwnx_cmd *cmd)
+{
+ bool defer_push = false;
+
+ spin_lock_bh(&cmd_mgr->lock);
+
+ if (cmd_mgr->state == RWNX_CMD_MGR_STATE_CRASHED) {
+ printk(KERN_CRIT"cmd queue crashed\n");
+ cmd->result = -EPIPE;
+ spin_unlock_bh(&cmd_mgr->lock);
+ return -EPIPE;
+ }
+
+ if (!list_empty(&cmd_mgr->cmds)) {
+ struct rwnx_cmd *last;
+
+ if (cmd_mgr->queue_sz == cmd_mgr->max_queue_sz) {
+ printk(KERN_CRIT"Too many cmds (%d) already queued\n",
+ cmd_mgr->max_queue_sz);
+ cmd->result = -ENOMEM;
+ spin_unlock_bh(&cmd_mgr->lock);
+ return -ENOMEM;
+ }
+ last = list_entry(cmd_mgr->cmds.prev, struct rwnx_cmd, list);
+ if (last->flags & (RWNX_CMD_FLAG_WAIT_ACK | RWNX_CMD_FLAG_WAIT_PUSH)) {
+ cmd->flags |= RWNX_CMD_FLAG_WAIT_PUSH;
+ defer_push = true;
+ }
+ }
+
+ if (cmd->flags & RWNX_CMD_FLAG_REQ_CFM)
+ cmd->flags |= RWNX_CMD_FLAG_WAIT_CFM;
+
+ cmd->tkn = cmd_mgr->next_tkn++;
+ cmd->result = -EINTR;
+
+ if (!(cmd->flags & RWNX_CMD_FLAG_NONBLOCK))
+ init_completion(&cmd->complete);
+
+ list_add_tail(&cmd->list, &cmd_mgr->cmds);
+ cmd_mgr->queue_sz++;
+ spin_unlock_bh(&cmd_mgr->lock);
+
+ if (!defer_push) {
+ //printk("queue:id=%x, param_len=%u\n",cmd->a2e_msg->id, cmd->a2e_msg->param_len);
+ aicwf_set_cmd_tx((void *)(cmd_mgr->usbdev), cmd->a2e_msg, sizeof(struct lmac_msg) + cmd->a2e_msg->param_len);
+ kfree(cmd->a2e_msg);
+ } else {
+ printk("ERR: never defer push!!!!");
+ return 0;
+ }
+
+ if (!(cmd->flags & RWNX_CMD_FLAG_NONBLOCK)) {
+ unsigned long tout = msecs_to_jiffies(RWNX_80211_CMD_TIMEOUT_MS * cmd_mgr->queue_sz);
+ if (!wait_for_completion_killable_timeout(&cmd->complete, tout)) {
+ printk(KERN_CRIT"cmd timed-out\n");
+
+ cmd_dump(cmd);
+ spin_lock_bh(&cmd_mgr->lock);
+ cmd_mgr->state = RWNX_CMD_MGR_STATE_CRASHED;
+ if (!(cmd->flags & RWNX_CMD_FLAG_DONE)) {
+ cmd->result = -ETIMEDOUT;
+ cmd_complete(cmd_mgr, cmd);
+ }
+ spin_unlock_bh(&cmd_mgr->lock);
+ }
+ else{
+ kfree(cmd);
+ }
+ } else {
+ cmd->result = 0;
+ }
+
+ return 0;
+}
+
+static int cmd_mgr_run_callback(struct rwnx_cmd_mgr *cmd_mgr, struct rwnx_cmd *cmd,
+ struct rwnx_cmd_e2amsg *msg, msg_cb_fct cb)
+{
+ int res;
+
+ if (! cb){
+ return 0;
+ }
+ spin_lock(&cmd_mgr->cb_lock);
+ res = cb(cmd, msg);
+ spin_unlock(&cmd_mgr->cb_lock);
+
+ return res;
+}
+
+static int cmd_mgr_msgind(struct rwnx_cmd_mgr *cmd_mgr, struct rwnx_cmd_e2amsg *msg,
+ msg_cb_fct cb)
+{
+ struct rwnx_cmd *cmd;
+ bool found = false;
+
+ //printk("cmd->id=%x\n", msg->id);
+ spin_lock(&cmd_mgr->lock);
+ list_for_each_entry(cmd, &cmd_mgr->cmds, list) {
+ if (cmd->reqid == msg->id &&
+ (cmd->flags & RWNX_CMD_FLAG_WAIT_CFM)) {
+
+ if (!cmd_mgr_run_callback(cmd_mgr, cmd, msg, cb)) {
+ found = true;
+ cmd->flags &= ~RWNX_CMD_FLAG_WAIT_CFM;
+
+ if (WARN((msg->param_len > RWNX_CMD_E2AMSG_LEN_MAX),
+ "Unexpect E2A msg len %d > %d\n", msg->param_len,
+ RWNX_CMD_E2AMSG_LEN_MAX)) {
+ msg->param_len = RWNX_CMD_E2AMSG_LEN_MAX;
+ }
+
+ if (cmd->e2a_msg && msg->param_len)
+ memcpy(cmd->e2a_msg, &msg->param, msg->param_len);
+
+ if (RWNX_CMD_WAIT_COMPLETE(cmd->flags))
+ cmd_complete(cmd_mgr, cmd);
+
+ break;
+ }
+ }
+ }
+ spin_unlock(&cmd_mgr->lock);
+
+ if (!found)
+ cmd_mgr_run_callback(cmd_mgr, NULL, msg, cb);
+
+ return 0;
+}
+
+static void cmd_mgr_print(struct rwnx_cmd_mgr *cmd_mgr)
+{
+ struct rwnx_cmd *cur;
+
+ spin_lock_bh(&cmd_mgr->lock);
+ printk("q_sz/max: %2d / %2d - next tkn: %d\n",
+ cmd_mgr->queue_sz, cmd_mgr->max_queue_sz,
+ cmd_mgr->next_tkn);
+ list_for_each_entry(cur, &cmd_mgr->cmds, list) {
+ cmd_dump(cur);
+ }
+ spin_unlock_bh(&cmd_mgr->lock);
+}
+
+static void cmd_mgr_drain(struct rwnx_cmd_mgr *cmd_mgr)
+{
+ struct rwnx_cmd *cur, *nxt;
+
+ spin_lock_bh(&cmd_mgr->lock);
+ list_for_each_entry_safe(cur, nxt, &cmd_mgr->cmds, list) {
+ list_del(&cur->list);
+ cmd_mgr->queue_sz--;
+ if (!(cur->flags & RWNX_CMD_FLAG_NONBLOCK))
+ complete(&cur->complete);
+ }
+ spin_unlock_bh(&cmd_mgr->lock);
+}
+
+void rwnx_cmd_mgr_init(struct rwnx_cmd_mgr *cmd_mgr)
+{
+ cmd_mgr->max_queue_sz = RWNX_CMD_MAX_QUEUED;
+ INIT_LIST_HEAD(&cmd_mgr->cmds);
+ cmd_mgr->state = RWNX_CMD_MGR_STATE_INITED;
+ spin_lock_init(&cmd_mgr->lock);
+ spin_lock_init(&cmd_mgr->cb_lock);
+ cmd_mgr->queue = &cmd_mgr_queue;
+ cmd_mgr->print = &cmd_mgr_print;
+ cmd_mgr->drain = &cmd_mgr_drain;
+ cmd_mgr->llind = NULL;//&cmd_mgr_llind;
+ cmd_mgr->msgind = &cmd_mgr_msgind;
+
+ #if 0
+ INIT_WORK(&cmd_mgr->cmdWork, cmd_mgr_task_process);
+ cmd_mgr->cmd_wq = create_singlethread_workqueue("cmd_wq");
+ if (!cmd_mgr->cmd_wq) {
+ txrx_err("insufficient memory to create cmd workqueue.\n");
+ return;
+ }
+ #endif
+}
+
+void rwnx_cmd_mgr_deinit(struct rwnx_cmd_mgr *cmd_mgr)
+{
+ cmd_mgr->print(cmd_mgr);
+ cmd_mgr->drain(cmd_mgr);
+ cmd_mgr->print(cmd_mgr);
+ memset(cmd_mgr, 0, sizeof(*cmd_mgr));
+}
+
+void aicwf_set_cmd_tx(void *dev, struct lmac_msg *msg, uint len)
+{
+ struct aic_usb_dev *usbdev = (struct aic_usb_dev *)dev;
+ struct aicwf_bus *bus = usbdev->bus_if;
+ u8 *buffer = bus->cmd_buf;
+ u16 index = 0;
+
+ memset(buffer, 0, CMD_BUF_MAX);
+ buffer[0] = (len+4) & 0x00ff;
+ buffer[1] = ((len+4) >> 8) &0x0f;
+ buffer[2] = 0x11;
+ buffer[3] = 0x0;
+ index += 4;
+ //there is a dummy word
+ index += 4;
+
+ //make sure little endian
+ put_u16(&buffer[index], msg->id);
+ index += 2;
+ put_u16(&buffer[index], msg->dest_id);
+ index += 2;
+ put_u16(&buffer[index], msg->src_id);
+ index += 2;
+ put_u16(&buffer[index], msg->param_len);
+ index += 2;
+ memcpy(&buffer[index], (u8 *)msg->param, msg->param_len);
+
+ aicwf_bus_txmsg(bus, buffer, len + 8);
+}
+
+static inline void *rwnx_msg_zalloc(lmac_msg_id_t const id,
+ lmac_task_id_t const dest_id,
+ lmac_task_id_t const src_id,
+ uint16_t const param_len)
+{
+ struct lmac_msg *msg;
+ gfp_t flags;
+
+ if (in_softirq())
+ flags = GFP_ATOMIC;
+ else
+ flags = GFP_KERNEL;
+
+ msg = (struct lmac_msg *)kzalloc(sizeof(struct lmac_msg) + param_len,
+ flags);
+ if (msg == NULL) {
+ printk(KERN_CRIT "%s: msg allocation failed\n", __func__);
+ return NULL;
+ }
+ msg->id = id;
+ msg->dest_id = dest_id;
+ msg->src_id = src_id;
+ msg->param_len = param_len;
+
+ return msg->param;
+}
+
+static void rwnx_msg_free(struct lmac_msg *msg, const void *msg_params)
+{
+ kfree(msg);
+}
+
+
+static int rwnx_send_msg(struct aic_usb_dev *usbdev, const void *msg_params,
+ int reqcfm, lmac_msg_id_t reqid, void *cfm)
+{
+ struct lmac_msg *msg;
+ struct rwnx_cmd *cmd;
+ bool nonblock;
+ int ret = 0;
+
+ msg = container_of((void *)msg_params, struct lmac_msg, param);
+ if(usbdev->bus_if->state == BUS_DOWN_ST) {
+ rwnx_msg_free(msg, msg_params);
+ printk("bus is down\n");
+ return 0;
+ }
+
+ nonblock = 0;
+ cmd = kzalloc(sizeof(struct rwnx_cmd), nonblock ? GFP_ATOMIC : GFP_KERNEL);
+ cmd->result = -EINTR;
+ cmd->id = msg->id;
+ cmd->reqid = reqid;
+ cmd->a2e_msg = msg;
+ cmd->e2a_msg = cfm;
+ if (nonblock)
+ cmd->flags = RWNX_CMD_FLAG_NONBLOCK;
+ if (reqcfm)
+ cmd->flags |= RWNX_CMD_FLAG_REQ_CFM;
+
+ if(reqcfm) {
+ cmd->flags &= ~RWNX_CMD_FLAG_WAIT_ACK; // we don't need ack any more
+ ret = usbdev->cmd_mgr.queue(&usbdev->cmd_mgr,cmd);
+ } else {
+ aicwf_set_cmd_tx((void *)(usbdev), cmd->a2e_msg, sizeof(struct lmac_msg) + cmd->a2e_msg->param_len);
+ }
+
+ if(!reqcfm)
+ kfree(cmd);
+
+ return ret;
+}
+
+int rwnx_send_dbg_mem_mask_write_req(struct aic_usb_dev *usbdev, u32 mem_addr,
+ u32 mem_mask, u32 mem_data)
+{
+ struct dbg_mem_mask_write_req *mem_mask_write_req;
+
+ /* Build the DBG_MEM_MASK_WRITE_REQ message */
+ mem_mask_write_req = rwnx_msg_zalloc(DBG_MEM_MASK_WRITE_REQ, TASK_DBG, DRV_TASK_ID,
+ sizeof(struct dbg_mem_mask_write_req));
+ if (!mem_mask_write_req)
+ return -ENOMEM;
+
+ /* Set parameters for the DBG_MEM_MASK_WRITE_REQ message */
+ mem_mask_write_req->memaddr = mem_addr;
+ mem_mask_write_req->memmask = mem_mask;
+ mem_mask_write_req->memdata = mem_data;
+
+ /* Send the DBG_MEM_MASK_WRITE_REQ message to LMAC FW */
+ return rwnx_send_msg(usbdev, mem_mask_write_req, 1, DBG_MEM_MASK_WRITE_CFM, NULL);
+}
+
+
+
+int rwnx_send_dbg_mem_block_write_req(struct aic_usb_dev *usbdev, u32 mem_addr,
+ u32 mem_size, u32 *mem_data)
+{
+ struct dbg_mem_block_write_req *mem_blk_write_req;
+
+ /* Build the DBG_MEM_BLOCK_WRITE_REQ message */
+ mem_blk_write_req = rwnx_msg_zalloc(DBG_MEM_BLOCK_WRITE_REQ, TASK_DBG, DRV_TASK_ID,
+ sizeof(struct dbg_mem_block_write_req));
+ if (!mem_blk_write_req)
+ return -ENOMEM;
+
+ /* Set parameters for the DBG_MEM_BLOCK_WRITE_REQ message */
+ mem_blk_write_req->memaddr = mem_addr;
+ mem_blk_write_req->memsize = mem_size;
+ memcpy(mem_blk_write_req->memdata, mem_data, mem_size);
+
+ /* Send the DBG_MEM_BLOCK_WRITE_REQ message to LMAC FW */
+ return rwnx_send_msg(usbdev, mem_blk_write_req, 1, DBG_MEM_BLOCK_WRITE_CFM, NULL);
+}
+
+
+int rwnx_send_dbg_mem_read_req(struct aic_usb_dev *usbdev, u32 mem_addr,
+ struct dbg_mem_read_cfm *cfm)
+{
+ struct dbg_mem_read_req *mem_read_req;
+
+
+ /* Build the DBG_MEM_READ_REQ message */
+ mem_read_req = rwnx_msg_zalloc(DBG_MEM_READ_REQ, TASK_DBG, DRV_TASK_ID,
+ sizeof(struct dbg_mem_read_req));
+ if (!mem_read_req)
+ return -ENOMEM;
+
+ /* Set parameters for the DBG_MEM_READ_REQ message */
+ mem_read_req->memaddr = mem_addr;
+
+ /* Send the DBG_MEM_READ_REQ message to LMAC FW */
+ return rwnx_send_msg(usbdev, mem_read_req, 1, DBG_MEM_READ_CFM, cfm);
+}
+
+
+int rwnx_send_dbg_mem_write_req(struct aic_usb_dev *usbdev, u32 mem_addr, u32 mem_data)
+{
+ struct dbg_mem_write_req *mem_write_req;
+
+ //printk("%s mem_addr:%x mem_data:%x\r\n", __func__, mem_addr, mem_data);
+
+ /* Build the DBG_MEM_WRITE_REQ message */
+ mem_write_req = rwnx_msg_zalloc(DBG_MEM_WRITE_REQ, TASK_DBG, DRV_TASK_ID,
+ sizeof(struct dbg_mem_write_req));
+ if (!mem_write_req)
+ return -ENOMEM;
+
+ /* Set parameters for the DBG_MEM_WRITE_REQ message */
+ mem_write_req->memaddr = mem_addr;
+ mem_write_req->memdata = mem_data;
+
+ /* Send the DBG_MEM_WRITE_REQ message to LMAC FW */
+ return rwnx_send_msg(usbdev, mem_write_req, 1, DBG_MEM_WRITE_CFM, NULL);
+}
+
+int rwnx_send_dbg_start_app_req(struct aic_usb_dev *usbdev, u32 boot_addr,
+ u32 boot_type)
+{
+ struct dbg_start_app_req *start_app_req;
+
+
+ /* Build the DBG_START_APP_REQ message */
+ start_app_req = rwnx_msg_zalloc(DBG_START_APP_REQ, TASK_DBG, DRV_TASK_ID,
+ sizeof(struct dbg_start_app_req));
+ if (!start_app_req)
+ return -ENOMEM;
+
+ /* Set parameters for the DBG_START_APP_REQ message */
+ start_app_req->bootaddr = boot_addr;
+ start_app_req->boottype = boot_type;
+
+ /* Send the DBG_START_APP_REQ message to LMAC FW */
+ return rwnx_send_msg(usbdev, start_app_req, 0, 0, NULL);
+}
+
+static msg_cb_fct dbg_hdlrs[MSG_I(DBG_MAX)] = {
+};
+
+static msg_cb_fct *msg_hdlrs[] = {
+ [TASK_DBG] = dbg_hdlrs,
+};
+
+void rwnx_rx_handle_msg(struct aic_usb_dev *usbdev, struct ipc_e2a_msg *msg)
+{
+ usbdev->cmd_mgr.msgind(&usbdev->cmd_mgr, msg,
+ msg_hdlrs[MSG_T(msg->id)][MSG_I(msg->id)]);
+}
+
+
+int rwnx_send_reboot(struct aic_usb_dev *usbdev)
+{
+ int ret = 0;
+ u32 delay = 2 *1000; //1s
+
+ printk("%s enter \r\n", __func__);
+
+ ret = rwnx_send_dbg_start_app_req(usbdev, delay, HOST_START_APP_REBOOT);
+ return ret;
+}
+
+
+
diff --git a/drivers/net/wireless/aic8800/aic_load_fw/aicbluetooth_cmds.h b/drivers/net/wireless/aic8800/aic_load_fw/aicbluetooth_cmds.h
new file mode 100644
index 000000000000..585f40fc0969
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic_load_fw/aicbluetooth_cmds.h
@@ -0,0 +1,298 @@
+/**
+ ******************************************************************************
+ *
+ * rwnx_cmds.h
+ *
+ * Copyright (C) RivieraWaves 2014-2019
+ *
+ ******************************************************************************
+ */
+
+#ifndef _AICBLUETOOTH_CMDS_H_
+#define _AICBLUETOOTH_CMDS_H_
+
+#include <linux/spinlock.h>
+#include <linux/completion.h>
+#include <linux/module.h>
+
+#define RWNX_80211_CMD_TIMEOUT_MS 2000//500//300
+
+#define RWNX_CMD_FLAG_NONBLOCK BIT(0)
+#define RWNX_CMD_FLAG_REQ_CFM BIT(1)
+#define RWNX_CMD_FLAG_WAIT_PUSH BIT(2)
+#define RWNX_CMD_FLAG_WAIT_ACK BIT(3)
+#define RWNX_CMD_FLAG_WAIT_CFM BIT(4)
+#define RWNX_CMD_FLAG_DONE BIT(5)
+/* ATM IPC design makes it possible to get the CFM before the ACK,
+ * otherwise this could have simply been a state enum */
+#define RWNX_CMD_WAIT_COMPLETE(flags) \
+ (!(flags & (RWNX_CMD_FLAG_WAIT_ACK | RWNX_CMD_FLAG_WAIT_CFM)))
+
+#define RWNX_CMD_MAX_QUEUED 8
+
+//#include "ipc_shared.h"
+
+#define IPC_E2A_MSG_PARAM_SIZE 256
+
+/// Message structure for MSGs from Emb to App
+struct ipc_e2a_msg
+{
+ u16 id; ///< Message id.
+ u16 dummy_dest_id;
+ u16 dummy_src_id;
+ u16 param_len; ///< Parameter embedded struct length.
+ u32 pattern; ///< Used to stamp a valid MSG buffer
+ u32 param[IPC_E2A_MSG_PARAM_SIZE]; ///< Parameter embedded struct. Must be word-aligned.
+};
+
+typedef u16 lmac_msg_id_t;
+typedef u16 lmac_task_id_t;
+
+struct lmac_msg
+{
+ lmac_msg_id_t id; ///< Message id.
+ lmac_task_id_t dest_id; ///< Destination kernel identifier.
+ lmac_task_id_t src_id; ///< Source kernel identifier.
+ u16 param_len; ///< Parameter embedded struct length.
+ u32 param[]; ///< Parameter embedded struct. Must be word-aligned.
+};
+
+#define rwnx_cmd_e2amsg ipc_e2a_msg
+#define rwnx_cmd_a2emsg lmac_msg
+#define RWNX_CMD_A2EMSG_LEN(m) (sizeof(struct lmac_msg) + m->param_len)
+#define RWNX_CMD_E2AMSG_LEN_MAX (IPC_E2A_MSG_PARAM_SIZE * 4)
+
+static inline void put_u16(u8 *buf, u16 data)
+{
+ buf[0] = (u8)(data&0x00ff);
+ buf[1] = (u8)((data >> 8)&0x00ff);
+}
+
+enum rwnx_cmd_mgr_state {
+ RWNX_CMD_MGR_STATE_DEINIT,
+ RWNX_CMD_MGR_STATE_INITED,
+ RWNX_CMD_MGR_STATE_CRASHED,
+};
+
+struct rwnx_cmd {
+ struct list_head list;
+ lmac_msg_id_t id;
+ lmac_msg_id_t reqid;
+ struct rwnx_cmd_a2emsg *a2e_msg;
+ char *e2a_msg;
+ u32 tkn;
+ u16 flags;
+ struct completion complete;
+ u32 result;
+};
+
+struct aic_sdio_dev;
+struct aic_usb_dev;
+struct rwnx_cmd;
+typedef int (*msg_cb_fct)(struct rwnx_cmd *cmd, struct rwnx_cmd_e2amsg *msg);
+
+struct rwnx_cmd_mgr {
+ enum rwnx_cmd_mgr_state state;
+ spinlock_t lock;
+ u32 next_tkn;
+ u32 queue_sz;
+ u32 max_queue_sz;
+ spinlock_t cb_lock;
+ #if 0
+ void *sdiodev;
+ #else
+ void *usbdev;
+ #endif
+ struct list_head cmds;
+
+ int (*queue)(struct rwnx_cmd_mgr *, struct rwnx_cmd *);
+ int (*llind)(struct rwnx_cmd_mgr *, struct rwnx_cmd *);
+ int (*msgind)(struct rwnx_cmd_mgr *, struct rwnx_cmd_e2amsg *, msg_cb_fct);
+ void (*print)(struct rwnx_cmd_mgr *);
+ void (*drain)(struct rwnx_cmd_mgr *);
+
+ struct work_struct cmdWork;
+ struct workqueue_struct *cmd_wq;
+};
+
+
+#if 0
+#define WAKE_CMD_WORK(cmd_mgr) \
+ do { \
+ queue_work((cmd_mgr)->cmd_wq, &cmd_mgr->cmdWork); \
+ } while (0)
+#endif
+void rwnx_cmd_mgr_init(struct rwnx_cmd_mgr *cmd_mgr);
+void rwnx_cmd_mgr_deinit(struct rwnx_cmd_mgr *cmd_mgr);
+int cmd_mgr_queue_force_defer(struct rwnx_cmd_mgr *cmd_mgr, struct rwnx_cmd *cmd);
+void aicwf_set_cmd_tx(void *dev, struct lmac_msg *msg, uint len);
+
+enum
+{
+ TASK_NONE = (u8) -1,
+
+ // MAC Management task.
+ TASK_MM = 0,
+ // DEBUG task
+ TASK_DBG,
+ /// SCAN task
+ TASK_SCAN,
+ /// TDLS task
+ TASK_TDLS,
+ /// SCANU task
+ TASK_SCANU,
+ /// ME task
+ TASK_ME,
+ /// SM task
+ TASK_SM,
+ /// APM task
+ TASK_APM,
+ /// BAM task
+ TASK_BAM,
+ /// MESH task
+ TASK_MESH,
+ /// RXU task
+ TASK_RXU,
+ // This is used to define the last task that is running on the EMB processor
+ TASK_LAST_EMB = TASK_RXU,
+
+ // nX API task
+ TASK_API,
+ TASK_MAX,
+};
+
+#define LMAC_FIRST_MSG(task) ((lmac_msg_id_t)((task) << 10))
+#define DRV_TASK_ID 100
+#define MSG_I(msg) ((msg) & ((1<<10)-1))
+#define MSG_T(msg) ((lmac_task_id_t)((msg) >> 10))
+
+
+enum dbg_msg_tag
+{
+ /// Memory read request
+ DBG_MEM_READ_REQ = LMAC_FIRST_MSG(TASK_DBG),
+ /// Memory read confirm
+ DBG_MEM_READ_CFM,
+ /// Memory write request
+ DBG_MEM_WRITE_REQ,
+ /// Memory write confirm
+ DBG_MEM_WRITE_CFM,
+ /// Module filter request
+ DBG_SET_MOD_FILTER_REQ,
+ /// Module filter confirm
+ DBG_SET_MOD_FILTER_CFM,
+ /// Severity filter request
+ DBG_SET_SEV_FILTER_REQ,
+ /// Severity filter confirm
+ DBG_SET_SEV_FILTER_CFM,
+ /// LMAC/MAC HW fatal error indication
+ DBG_ERROR_IND,
+ /// Request to get system statistics
+ DBG_GET_SYS_STAT_REQ,
+ /// COnfirmation of system statistics
+ DBG_GET_SYS_STAT_CFM,
+ /// Memory block write request
+ DBG_MEM_BLOCK_WRITE_REQ,
+ /// Memory block write confirm
+ DBG_MEM_BLOCK_WRITE_CFM,
+ /// Start app request
+ DBG_START_APP_REQ,
+ /// Start app confirm
+ DBG_START_APP_CFM,
+ /// Start npc request
+ DBG_START_NPC_REQ,
+ /// Start npc confirm
+ DBG_START_NPC_CFM,
+ /// Memory mask write request
+ DBG_MEM_MASK_WRITE_REQ,
+ /// Memory mask write confirm
+ DBG_MEM_MASK_WRITE_CFM,
+ /// Max number of Debug messages
+ DBG_MAX,
+};
+
+struct dbg_mem_block_write_req
+{
+ u32 memaddr;
+ u32 memsize;
+ u32 memdata[1024 / sizeof(u32)];
+};
+
+/// Structure containing the parameters of the @ref DBG_MEM_MASK_WRITE_REQ message.
+struct dbg_mem_mask_write_req
+{
+ u32 memaddr;
+ u32 memmask;
+ u32 memdata;
+};
+
+
+/// Structure containing the parameters of the @ref DBG_MEM_BLOCK_WRITE_CFM message.
+struct dbg_mem_block_write_cfm
+{
+ u32 wstatus;
+};
+
+/// Structure containing the parameters of the @ref DBG_MEM_READ_REQ message.
+struct dbg_mem_read_req
+{
+ u32 memaddr;
+};
+
+/// Structure containing the parameters of the @ref DBG_MEM_READ_CFM message.
+struct dbg_mem_read_cfm
+{
+ u32 memaddr;
+ u32 memdata;
+};
+
+/// Structure containing the parameters of the @ref DBG_MEM_WRITE_REQ message.
+struct dbg_mem_write_req
+{
+ u32 memaddr;
+ u32 memdata;
+};
+
+/// Structure containing the parameters of the @ref DBG_MEM_WRITE_CFM message.
+struct dbg_mem_write_cfm
+{
+ u32 memaddr;
+ u32 memdata;
+};
+
+struct dbg_start_app_req
+{
+ u32 bootaddr;
+ u32 boottype;
+};
+
+/// Structure containing the parameters of the @ref DBG_START_APP_CFM message.
+struct dbg_start_app_cfm
+{
+ u32 bootstatus;
+};
+
+enum {
+ HOST_START_APP_AUTO = 1,
+ HOST_START_APP_CUSTOM,
+ HOST_START_APP_REBOOT,
+};
+
+int rwnx_send_dbg_mem_mask_write_req(struct aic_usb_dev *usbdev, u32 mem_addr,
+ u32 mem_mask, u32 mem_data);
+
+
+int rwnx_send_dbg_mem_block_write_req(struct aic_usb_dev *usbdev, u32 mem_addr,
+ u32 mem_size, u32 *mem_data);
+
+int rwnx_send_dbg_mem_write_req(struct aic_usb_dev *usbdev, u32 mem_addr, u32 mem_data);
+int rwnx_send_dbg_mem_read_req(struct aic_usb_dev *usbdev, u32 mem_addr, struct dbg_mem_read_cfm *cfm);
+
+void rwnx_rx_handle_msg(struct aic_usb_dev *usbdev, struct ipc_e2a_msg *msg);
+
+int rwnx_send_dbg_start_app_req(struct aic_usb_dev *usbdev, u32 boot_addr,
+ u32 boot_type);
+
+int rwnx_send_reboot(struct aic_usb_dev *usbdev);
+
+#endif
diff --git a/drivers/net/wireless/aic8800/aic_load_fw/aicwf_debug.h b/drivers/net/wireless/aic8800/aic_load_fw/aicwf_debug.h
new file mode 100644
index 000000000000..12fdca688b18
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic_load_fw/aicwf_debug.h
@@ -0,0 +1,52 @@
+
+
+#define RWNX_FN_ENTRY_STR ">>> %s()\n", __func__
+
+
+
+/* message levels */
+#define LOGERROR 0x0001
+#define LOGINFO 0x0002
+#define LOGTRACE 0x0004
+#define LOGDEBUG 0x0008
+#define LOGDATA 0x0010
+
+extern int aicwf_dbg_level;
+void rwnx_data_dump(char* tag, void* data, unsigned long len);
+
+#define AICWF_LOG "AICWFDBG("
+
+#define AICWFDBG(level, args, arg...) \
+do { \
+ if (aicwf_dbg_level & level) { \
+ printk(AICWF_LOG#level")\t" args, ##arg); \
+ } \
+} while (0)
+
+#define RWNX_DBG(fmt, ...) \
+do { \
+ if (aicwf_dbg_level & LOGTRACE) { \
+ printk(AICWF_LOG"LOGTRACE)\t"fmt , ##__VA_ARGS__); \
+ } \
+} while (0)
+
+
+
+#if 0
+#define RWNX_DBG(fmt, ...) \
+ do { \
+ if (aicwf_dbg_level & LOGTRACE) { \
+ printk(AICWF_LOG"LOGTRACE"")\t" fmt, ##__VA_ARGS__); \
+ } \
+ } while (0)
+#define AICWFDBG(args, level) \
+do { \
+ if (aicwf_dbg_level & level) { \
+ printk(AICWF_LOG"(%s)\t" ,#level); \
+ printf args; \
+ } \
+} while (0)
+#endif
+
+
+
diff --git a/drivers/net/wireless/aic8800/aic_load_fw/aicwf_rx_prealloc.c b/drivers/net/wireless/aic8800/aic_load_fw/aicwf_rx_prealloc.c
new file mode 100644
index 000000000000..886d34a8023d
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic_load_fw/aicwf_rx_prealloc.c
@@ -0,0 +1,120 @@
+#include <linux/version.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include "aicwf_rx_prealloc.h"
+#include "aicwf_debug.h"
+
+#ifdef CONFIG_PREALLOC_RX_SKB
+struct aicwf_rx_buff_list aic_rx_buff_list;
+
+int aic_rxbuff_num_max = 1000;
+
+int aic_rxbuff_size = (4 * 512);
+
+int rx_buff_list_ava = 0;
+
+module_param(rx_buff_list_ava, int, 0660);
+
+int aicwf_rxbuff_size_get(void)
+{
+ return aic_rxbuff_size;
+}
+
+struct rx_buff *aicwf_prealloc_rxbuff_alloc(spinlock_t *lock)
+{
+ unsigned long flags;
+ struct rx_buff *rxbuff = NULL;
+
+ spin_lock_irqsave(lock, flags);
+ rx_buff_list_ava = atomic_read(&aic_rx_buff_list.rxbuff_list_len);
+ if(rx_buff_list_ava < 10){
+ AICWFDBG(LOGERROR, "%s WARNING rxbuff is running out %d\r\n", __func__,
+ rx_buff_list_ava);
+ }
+
+ if (list_empty(&aic_rx_buff_list.rxbuff_list)) {
+ spin_unlock_irqrestore(lock, flags);
+ AICWFDBG(LOGERROR, "%s %d, rxbuff list is empty\r\n", __func__, __LINE__);
+ return NULL;
+ } else {
+ rxbuff = list_first_entry(&aic_rx_buff_list.rxbuff_list,
+ struct rx_buff, queue);
+ list_del_init(&rxbuff->queue);
+ atomic_dec(&aic_rx_buff_list.rxbuff_list_len);
+ }
+ spin_unlock_irqrestore(lock, flags);
+ //printk("len:%d\n", aic_rx_buff_list.rxbuff_list_len);
+ memset(rxbuff->data, 0, aic_rxbuff_size);
+ rxbuff->len = 0;
+ rxbuff->start = NULL;
+ rxbuff->read = NULL;
+ rxbuff->end = NULL;
+
+ return rxbuff;
+}
+
+void aicwf_prealloc_rxbuff_free(struct rx_buff *rxbuff, spinlock_t *lock)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(lock, flags);
+ list_add_tail(&rxbuff->queue, &aic_rx_buff_list.rxbuff_list);
+ atomic_inc(&aic_rx_buff_list.rxbuff_list_len);
+ rx_buff_list_ava = atomic_read(&aic_rx_buff_list.rxbuff_list_len);
+ spin_unlock_irqrestore(lock, flags);
+}
+
+int aicwf_prealloc_init()
+{
+ struct rx_buff *rxbuff;
+ int i = 0;
+
+ AICWFDBG(LOGINFO, "%s enter\n", __func__);
+ INIT_LIST_HEAD(&aic_rx_buff_list.rxbuff_list);
+
+ for (i = 0 ; i < aic_rxbuff_num_max ; i++) {
+ rxbuff = kzalloc(sizeof(struct rx_buff), GFP_KERNEL);
+ if (rxbuff) {
+ rxbuff->data = kzalloc(aic_rxbuff_size, GFP_KERNEL);
+ if (rxbuff->data == NULL) {
+ AICWFDBG(LOGERROR, "failed to alloc rxbuff data\n");
+ kfree(rxbuff);
+ continue;
+ }
+ rxbuff->len = 0;
+ rxbuff->start = NULL;
+ rxbuff->read = NULL;
+ rxbuff->end = NULL;
+ list_add_tail(&rxbuff->queue, &aic_rx_buff_list.rxbuff_list);
+ atomic_inc(&aic_rx_buff_list.rxbuff_list_len);
+ rx_buff_list_ava = atomic_read(&aic_rx_buff_list.rxbuff_list_len);
+ }
+ }
+
+ AICWFDBG(LOGINFO, "pre alloc rxbuff list len: %d\n", (int)atomic_read(&aic_rx_buff_list.rxbuff_list_len));
+ return 0;
+}
+
+void aicwf_prealloc_exit()
+{
+ struct rx_buff *rxbuff;
+ struct rx_buff *pos;
+
+ AICWFDBG(LOGINFO, "%s enter\n", __func__);
+
+ AICWFDBG(LOGINFO, "free pre alloc rxbuff list %d\n", (int)atomic_read(&aic_rx_buff_list.rxbuff_list_len));
+ list_for_each_entry_safe(rxbuff, pos, &aic_rx_buff_list.rxbuff_list, queue) {
+ list_del_init(&rxbuff->queue);
+ kfree(rxbuff->data);
+ kfree(rxbuff);
+ }
+}
+
+EXPORT_SYMBOL(aicwf_rxbuff_size_get);
+EXPORT_SYMBOL(aicwf_prealloc_rxbuff_alloc);
+EXPORT_SYMBOL(aicwf_prealloc_rxbuff_free);
+
+#endif
+
diff --git a/drivers/net/wireless/aic8800/aic_load_fw/aicwf_rx_prealloc.h b/drivers/net/wireless/aic8800/aic_load_fw/aicwf_rx_prealloc.h
new file mode 100644
index 000000000000..b2fafc1ff790
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic_load_fw/aicwf_rx_prealloc.h
@@ -0,0 +1,26 @@
+#ifndef _AICWF_RX_PREALLOC_H_
+#define _AICWF_RX_PREALLOC_H_
+
+#ifdef CONFIG_PREALLOC_RX_SKB
+
+struct rx_buff {
+ struct list_head queue;
+ unsigned char *data;
+ u32 len;
+ uint8_t *start;
+ uint8_t *end;
+ uint8_t *read;
+};
+
+struct aicwf_rx_buff_list {
+ struct list_head rxbuff_list;
+ atomic_t rxbuff_list_len;
+};
+
+struct rx_buff *aicwf_prealloc_rxbuff_alloc(spinlock_t *lock);
+void aicwf_prealloc_rxbuff_free(struct rx_buff *rxbuff, spinlock_t *lock);
+int aicwf_prealloc_init(void);
+void aicwf_prealloc_exit(void);
+int aicwf_rxbuff_size_get(void);
+#endif
+#endif /* _AICWF_RX_PREALLOC_H_ */ \ No newline at end of file
diff --git a/drivers/net/wireless/aic8800/aic_load_fw/aicwf_txq_prealloc.c b/drivers/net/wireless/aic8800/aic_load_fw/aicwf_txq_prealloc.c
new file mode 100644
index 000000000000..585d48c11c20
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic_load_fw/aicwf_txq_prealloc.c
@@ -0,0 +1,61 @@
+#include <linux/slab.h>
+#include "aicwf_debug.h"
+
+struct prealloc_txq{
+ int prealloced;
+ void *txq;
+ size_t size;
+};
+
+struct prealloc_txq prealloc_txq;
+#define MAX_TXQ_SIZE 30 * 1024
+
+void *aicwf_prealloc_txq_alloc(size_t size)
+{
+
+ BUG_ON(size > MAX_TXQ_SIZE);
+
+ //check prealloc_txq.size
+ if((int)prealloc_txq.size != (int)size)
+ {
+ AICWFDBG(LOGINFO, "%s size is diff will to be kzalloc \r\n", __func__);
+
+ if(prealloc_txq.txq != NULL)
+ {
+ AICWFDBG(LOGINFO, "%s txq to kfree \r\n", __func__);
+ kfree(prealloc_txq.txq);
+ prealloc_txq.txq = NULL;
+ }
+
+ prealloc_txq.size = size;
+ prealloc_txq.prealloced = 0;
+ }
+
+ //check prealloc or not
+ if(!prealloc_txq.prealloced)
+ {
+ prealloc_txq.txq = kzalloc(size, GFP_KERNEL);
+ if(!prealloc_txq.txq){
+ AICWFDBG(LOGERROR, "%s txq kzalloc fail \r\n", __func__);
+ }else{
+ AICWFDBG(LOGINFO, "%s txq kzalloc successful \r\n", __func__);
+ prealloc_txq.prealloced = 1;
+ }
+ }else{
+ AICWFDBG(LOGINFO, "%s txq not need to kzalloc \r\n", __func__);
+ }
+
+ return prealloc_txq.txq;
+}
+void aicwf_prealloc_txq_free(void)
+{
+ if(prealloc_txq.txq != NULL)
+ {
+ AICWFDBG(LOGINFO, "%s txq to kfree \r\n", __func__);
+ kfree(prealloc_txq.txq);
+ prealloc_txq.txq = NULL;
+ }
+}
+
+EXPORT_SYMBOL(aicwf_prealloc_txq_alloc);
+
diff --git a/drivers/net/wireless/aic8800/aic_load_fw/aicwf_txq_prealloc.h b/drivers/net/wireless/aic8800/aic_load_fw/aicwf_txq_prealloc.h
new file mode 100644
index 000000000000..ce4ee07473b3
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic_load_fw/aicwf_txq_prealloc.h
@@ -0,0 +1,4 @@
+
+
+void aicwf_prealloc_txq_free(void);
+
diff --git a/drivers/net/wireless/aic8800/aic_load_fw/aicwf_usb.c b/drivers/net/wireless/aic8800/aic_load_fw/aicwf_usb.c
new file mode 100644
index 000000000000..52eaa7abcb6a
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic_load_fw/aicwf_usb.c
@@ -0,0 +1,1716 @@
+/**
+ * aicwf_usb.c
+ *
+ * USB function declarations
+ *
+ * Copyright (C) AICSemi 2018-2020
+ */
+#include <linux/kthread.h>
+#include <linux/netdevice.h>
+#include <linux/printk.h>
+#include <linux/interrupt.h>
+#include <linux/sched.h>
+#include <linux/completion.h>
+#include <linux/semaphore.h>
+#include <linux/debugfs.h>
+#include <linux/atomic.h>
+#include <linux/vmalloc.h>
+#include <linux/signal.h>
+
+#include <linux/usb.h>
+#include <linux/kthread.h>
+#include <linux/version.h>
+#include "aic_txrxif.h"
+#include "aicwf_usb.h"
+#include "aicbluetooth.h"
+#include "aicwf_debug.h"
+#include "aic_compat_8800d80.h"
+
+#define JUMP_TABLE_BASE 0x161928
+#define JUMP_TABLE_OFFSET(i) ((u32)(JUMP_TABLE_BASE+(i)*4))
+extern int adap_test;
+extern int testmode;
+extern unsigned char paringid[100];
+extern int ble_scan_wakeup_reboot_time;
+extern uint32_t ad_data_filter_mask;
+u8 chip_id = 0;
+u8 chip_sub_id = 0;
+int fw_loaded = 0;
+
+void aicwf_usb_tx_flowctrl(struct aic_usb_dev *usb_dev, bool state)
+{
+}
+
+static struct aicwf_usb_buf *aicwf_usb_tx_dequeue(struct aic_usb_dev *usb_dev,
+ struct list_head *q, int *counter, spinlock_t *qlock)
+{
+ unsigned long flags;
+ struct aicwf_usb_buf *usb_buf;
+
+ spin_lock_irqsave(qlock, flags);
+ if (list_empty(q)) {
+ usb_buf = NULL;
+ } else {
+ usb_buf = list_first_entry(q, struct aicwf_usb_buf, list);
+ list_del_init(&usb_buf->list);
+ if (counter)
+ (*counter)--;
+ }
+ spin_unlock_irqrestore(qlock, flags);
+ return usb_buf;
+}
+
+static void aicwf_usb_tx_queue(struct aic_usb_dev *usb_dev,
+ struct list_head *q, struct aicwf_usb_buf *usb_buf, int *counter,
+ spinlock_t *qlock)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(qlock, flags);
+ list_add_tail(&usb_buf->list, q);
+ (*counter)++;
+ spin_unlock_irqrestore(qlock, flags);
+}
+
+static struct aicwf_usb_buf *aicwf_usb_rx_buf_get(struct aic_usb_dev *usb_dev)
+{
+ unsigned long flags;
+ struct aicwf_usb_buf *usb_buf;
+
+ spin_lock_irqsave(&usb_dev->rx_free_lock, flags);
+ if (list_empty(&usb_dev->rx_free_list)) {
+ usb_buf = NULL;
+ } else {
+ usb_buf = list_first_entry(&usb_dev->rx_free_list, struct aicwf_usb_buf, list);
+ list_del_init(&usb_buf->list);
+ }
+ spin_unlock_irqrestore(&usb_dev->rx_free_lock, flags);
+ return usb_buf;
+}
+
+static void aicwf_usb_rx_buf_put(struct aic_usb_dev *usb_dev, struct aicwf_usb_buf *usb_buf)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&usb_dev->rx_free_lock, flags);
+ list_add_tail(&usb_buf->list, &usb_dev->rx_free_list);
+ spin_unlock_irqrestore(&usb_dev->rx_free_lock, flags);
+}
+
+static void aicwf_usb_tx_complete(struct urb *urb)
+{
+ unsigned long flags;
+ struct aicwf_usb_buf *usb_buf = (struct aicwf_usb_buf *) urb->context;
+ struct aic_usb_dev *usb_dev = usb_buf->usbdev;
+ struct sk_buff *skb;
+
+ if (usb_buf->cfm == false) {
+ skb = usb_buf->skb;
+ dev_kfree_skb_any(skb);
+ }
+ #if !defined CONFIG_USB_NO_TRANS_DMA_MAP
+ else {
+ u8 *buf;
+ buf = (u8 *)usb_buf->skb;
+ kfree(buf);
+ }
+ #endif
+ usb_buf->skb = NULL;
+
+ aicwf_usb_tx_queue(usb_dev, &usb_dev->tx_free_list, usb_buf,
+ &usb_dev->tx_free_count, &usb_dev->tx_free_lock);
+
+ spin_lock_irqsave(&usb_dev->tx_flow_lock, flags);
+ if (usb_dev->tx_free_count > AICWF_USB_TX_HIGH_WATER) {
+ if (usb_dev->tbusy) {
+ usb_dev->tbusy = false;
+ aicwf_usb_tx_flowctrl(usb_dev, false);
+ }
+ }
+ spin_unlock_irqrestore(&usb_dev->tx_flow_lock, flags);
+ }
+
+static void aicwf_usb_rx_complete(struct urb *urb)
+{
+ struct aicwf_usb_buf *usb_buf = (struct aicwf_usb_buf *) urb->context;
+ struct aic_usb_dev *usb_dev = usb_buf->usbdev;
+ struct aicwf_rx_priv* rx_priv = usb_dev->rx_priv;
+ struct sk_buff *skb = NULL;
+ unsigned long flags = 0;
+
+ skb = usb_buf->skb;
+ usb_buf->skb = NULL;
+
+ if (urb->actual_length > urb->transfer_buffer_length) {
+ aicwf_dev_skb_free(skb);
+ aicwf_usb_rx_buf_put(usb_dev, usb_buf);
+ schedule_work(&usb_dev->rx_urb_work);
+ return;
+ }
+
+ if (urb->status != 0 || !urb->actual_length) {
+ aicwf_dev_skb_free(skb);
+ aicwf_usb_rx_buf_put(usb_dev, usb_buf);
+ schedule_work(&usb_dev->rx_urb_work);
+ return;
+ }
+
+ if (usb_dev->state == USB_UP_ST) {
+ skb_put(skb, urb->actual_length);
+
+ spin_lock_irqsave(&rx_priv->rxqlock, flags);
+ if(!aicwf_rxframe_enqueue(usb_dev->dev, &rx_priv->rxq, skb)){
+ spin_unlock_irqrestore(&rx_priv->rxqlock, flags);
+ usb_err("rx_priv->rxq is over flow!!!\n");
+ aicwf_dev_skb_free(skb);
+ return;
+ }
+ spin_unlock_irqrestore(&rx_priv->rxqlock, flags);
+ atomic_inc(&rx_priv->rx_cnt);
+ complete(&rx_priv->usbdev->bus_if->busrx_trgg);
+ aicwf_usb_rx_buf_put(usb_dev, usb_buf);
+
+ schedule_work(&usb_dev->rx_urb_work);
+ } else {
+ aicwf_dev_skb_free(skb);
+ aicwf_usb_rx_buf_put(usb_dev, usb_buf);
+ }
+}
+
+static int aicwf_usb_submit_rx_urb(struct aic_usb_dev *usb_dev,
+ struct aicwf_usb_buf *usb_buf)
+{
+ struct sk_buff *skb;
+ int ret;
+
+ if (!usb_buf || !usb_dev)
+ return -1;
+
+ if (usb_dev->state != USB_UP_ST) {
+ usb_err("usb state is not up!\n");
+ aicwf_usb_rx_buf_put(usb_dev, usb_buf);
+ return -1;
+ }
+
+ skb = __dev_alloc_skb(AICWF_USB_MAX_PKT_SIZE, GFP_KERNEL);
+ if (!skb) {
+ aicwf_usb_rx_buf_put(usb_dev, usb_buf);
+ return -1;
+ }
+
+ usb_buf->skb = skb;
+
+ usb_fill_bulk_urb(usb_buf->urb,
+ usb_dev->udev,
+ usb_dev->bulk_in_pipe,
+ skb->data, skb_tailroom(skb), aicwf_usb_rx_complete, usb_buf);
+
+ usb_buf->usbdev = usb_dev;
+
+ usb_anchor_urb(usb_buf->urb, &usb_dev->rx_submitted);
+
+ ret = usb_submit_urb(usb_buf->urb, GFP_ATOMIC);
+ if (ret) {
+ usb_err("usb submit rx urb fail:%d\n", ret);
+ usb_unanchor_urb(usb_buf->urb);
+ aicwf_dev_skb_free(usb_buf->skb);
+ usb_buf->skb = NULL;
+ aicwf_usb_rx_buf_put(usb_dev, usb_buf);
+ msleep(100);
+ return ret;
+ }
+ return 0;
+}
+
+static void aicwf_usb_rx_submit_all_urb(struct aic_usb_dev *usb_dev)
+{
+ struct aicwf_usb_buf *usb_buf;
+
+ if (usb_dev->state != USB_UP_ST) {
+ usb_err("bus is not up=%d\n", usb_dev->state);
+ return;
+ }
+ if (usb_dev->app_cmp) {
+ printk("app_cmp\r\n");
+ return;
+}
+
+ while((usb_buf = aicwf_usb_rx_buf_get(usb_dev)) != NULL) {
+ if (aicwf_usb_submit_rx_urb(usb_dev, usb_buf)) {
+ usb_err("usb rx refill fail\n");
+ if (usb_dev->state != USB_UP_ST){
+ return;
+ }
+ break;
+ }
+ }
+}
+
+static void aicwf_usb_rx_prepare(struct aic_usb_dev *usb_dev)
+{
+ aicwf_usb_rx_submit_all_urb(usb_dev);
+}
+
+static void aicwf_usb_tx_prepare(struct aic_usb_dev *usb_dev)
+{
+ struct aicwf_usb_buf *usb_buf;
+
+ while(!list_empty(&usb_dev->tx_post_list)){
+ usb_buf = aicwf_usb_tx_dequeue(usb_dev, &usb_dev->tx_post_list,
+ &usb_dev->tx_post_count, &usb_dev->tx_post_lock);
+ if(usb_buf->skb) {
+ dev_kfree_skb(usb_buf->skb);
+ usb_buf->skb = NULL;
+ }
+ aicwf_usb_tx_queue(usb_dev, &usb_dev->tx_free_list, usb_buf,
+ &usb_dev->tx_free_count, &usb_dev->tx_free_lock);
+ }
+}
+static void aicwf_usb_tx_process(struct aic_usb_dev *usb_dev)
+{
+ struct aicwf_usb_buf *usb_buf;
+ int ret = 0;
+ u8* data = NULL;
+
+ while(!list_empty(&usb_dev->tx_post_list)) {
+ if (usb_dev->state != USB_UP_ST) {
+ usb_err("usb state is not up!\n");
+ return;
+ }
+
+ usb_buf = aicwf_usb_tx_dequeue(usb_dev, &usb_dev->tx_post_list,
+ &usb_dev->tx_post_count, &usb_dev->tx_post_lock);
+ if(!usb_buf) {
+ usb_err("can not get usb_buf from tx_post_list!\n");
+ return;
+ }
+ data = usb_buf->skb->data;
+
+ ret = usb_submit_urb(usb_buf->urb, GFP_ATOMIC);
+ if (ret) {
+ usb_err("aicwf_usb_bus_tx usb_submit_urb FAILED\n");
+ goto fail;
+ }
+
+ continue;
+fail:
+ dev_kfree_skb(usb_buf->skb);
+ usb_buf->skb = NULL;
+ aicwf_usb_tx_queue(usb_dev, &usb_dev->tx_free_list, usb_buf,
+ &usb_dev->tx_free_count, &usb_dev->tx_free_lock);
+ }
+}
+
+static inline void aic_thread_wait_stop(void)
+{
+#if 1// PLATFORM_LINUX
+ #if 0
+ while (!kthread_should_stop())
+ rtw_msleep_os(10);
+ #else
+ set_current_state(TASK_INTERRUPTIBLE);
+ while (!kthread_should_stop()) {
+ schedule();
+ set_current_state(TASK_INTERRUPTIBLE);
+ }
+ __set_current_state(TASK_RUNNING);
+ #endif
+#endif
+}
+
+int usb_bustx_thread(void *data)
+{
+ struct aicwf_bus *bus = (struct aicwf_bus *)data;
+ struct aic_usb_dev *usbdev = bus->bus_priv.usb;
+
+ while (1) {
+ /*
+ if(kthread_should_stop()) {
+ usb_err("usb bustx thread stop\n");
+ break;
+ }
+ */
+
+ if (!wait_for_completion_interruptible(&bus->bustx_trgg)) {
+ if (usbdev->app_cmp == false){
+ printk("usb bustx thread will to stop\n");
+ //mdelay(100);
+ break;
+ //continue;
+ }
+ if (usbdev->tx_post_count > 0)
+ aicwf_usb_tx_process(usbdev);
+ }
+ }
+
+ aic_thread_wait_stop();
+ printk("usb bustx thread stop\n");
+
+ return 0;
+}
+
+int usb_busrx_thread(void *data)
+{
+ struct aicwf_rx_priv *rx_priv = (struct aicwf_rx_priv *)data;
+ struct aicwf_bus *bus_if = rx_priv->usbdev->bus_if;
+
+ allow_signal(SIGTERM);
+ while (1) {
+ #if 0
+ if(kthread_should_stop()) {
+ usb_err("usb busrx thread stop\n");
+ break;
+ }
+ #endif
+ if (!wait_for_completion_interruptible(&bus_if->busrx_trgg)) {
+ aicwf_process_rxframes(rx_priv);
+ } else {
+ break;
+ }
+ if (bus_if->state == BUS_DOWN_ST) {
+ printk("usb busrx thread will to stop\n");
+ break;
+ }
+
+ }
+ aic_thread_wait_stop();
+ printk("usb busrx thread stop\n");
+
+ return 0;
+}
+
+static void aicwf_usb_send_msg_complete(struct urb *urb)
+{
+ struct aic_usb_dev *usb_dev = (struct aic_usb_dev *) urb->context;
+
+ usb_dev->msg_finished = true;
+ if (waitqueue_active(&usb_dev->msg_wait))
+ wake_up(&usb_dev->msg_wait);
+}
+
+static int aicwf_usb_bus_txmsg(struct device *dev, u8 *buf, u32 len)
+{
+ int ret = 0;
+ struct aicwf_bus *bus_if = dev_get_drvdata(dev);
+ struct aic_usb_dev *usb_dev = bus_if->bus_priv.usb;
+
+ if (usb_dev->state != USB_UP_ST)
+ return -EIO;
+
+ if (buf == NULL || len == 0 || usb_dev->msg_out_urb == NULL)
+ return -EINVAL;
+
+ if (test_and_set_bit(0, &usb_dev->msg_busy)) {
+ usb_err("In a control frame option, can't tx!\n");
+ return -EIO;
+ }
+
+ usb_dev->msg_finished = false;
+
+#ifdef CONFIG_USB_MSG_EP
+ if (usb_dev->msg_out_pipe && usb_dev->use_msg_ep == 1) {
+ usb_fill_bulk_urb(usb_dev->msg_out_urb,
+ usb_dev->udev,
+ usb_dev->msg_out_pipe,
+ buf, len, (usb_complete_t) aicwf_usb_send_msg_complete, usb_dev);
+ } else {
+ usb_fill_bulk_urb(usb_dev->msg_out_urb,
+ usb_dev->udev,
+ usb_dev->bulk_out_pipe,
+ buf, len, (usb_complete_t) aicwf_usb_send_msg_complete, usb_dev);
+ }
+#else
+ usb_fill_bulk_urb(usb_dev->msg_out_urb,
+ usb_dev->udev,
+ usb_dev->bulk_out_pipe,
+ buf, len, (usb_complete_t) aicwf_usb_send_msg_complete, usb_dev);
+#endif
+ #if defined CONFIG_USB_NO_TRANS_DMA_MAP
+ usb_dev->msg_out_urb->transfer_dma = usb_dev->cmd_dma_trans_addr;
+ usb_dev->msg_out_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+ #endif
+ usb_dev->msg_out_urb->transfer_flags |= URB_ZERO_PACKET;
+
+ ret = usb_submit_urb(usb_dev->msg_out_urb, GFP_ATOMIC);
+ if (ret) {
+ usb_err("usb_submit_urb failed %d\n", ret);
+ goto exit;
+ }
+
+ ret = wait_event_timeout(usb_dev->msg_wait,
+ usb_dev->msg_finished, msecs_to_jiffies(CMD_TX_TIMEOUT));
+ if (!ret) {
+ if (usb_dev->msg_out_urb)
+ usb_kill_urb(usb_dev->msg_out_urb);
+ usb_err("Txmsg wait timed out\n");
+ ret = -EIO;
+ goto exit;
+ }
+
+ if (usb_dev->msg_finished == false) {
+ usb_err("Txmsg timed out\n");
+ ret = -ETIMEDOUT;
+ goto exit;
+ }
+exit:
+ clear_bit(0, &usb_dev->msg_busy);
+ return ret;
+}
+
+
+static void aicwf_usb_free_urb(struct list_head *q, spinlock_t *qlock)
+{
+ struct aicwf_usb_buf *usb_buf, *tmp;
+ unsigned long flags;
+
+ spin_lock_irqsave(qlock, flags);
+ list_for_each_entry_safe(usb_buf, tmp, q, list) {
+ spin_unlock_irqrestore(qlock, flags);
+ if (!usb_buf->urb) {
+ usb_err("bad usb_buf\n");
+ spin_lock_irqsave(qlock, flags);
+ break;
+ }
+ usb_free_urb(usb_buf->urb);
+ list_del_init(&usb_buf->list);
+ spin_lock_irqsave(qlock, flags);
+ }
+ spin_unlock_irqrestore(qlock, flags);
+}
+
+static int aicwf_usb_alloc_rx_urb(struct aic_usb_dev *usb_dev)
+{
+ int i;
+
+ for (i = 0; i < AICWF_USB_RX_URBS; i++) {
+ struct aicwf_usb_buf *usb_buf = &usb_dev->usb_rx_buf[i];
+
+ usb_buf->usbdev = usb_dev;
+ usb_buf->urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!usb_buf->urb) {
+ usb_err("could not allocate rx data urb\n");
+ goto err;
+ }
+ #if defined CONFIG_USB_NO_TRANS_DMA_MAP
+ // dma buf unused
+ usb_buf->data_buf = NULL;
+ usb_buf->data_dma_trans_addr = 0x0;
+ #endif
+ list_add_tail(&usb_buf->list, &usb_dev->rx_free_list);
+ }
+ return 0;
+
+err:
+ aicwf_usb_free_urb(&usb_dev->rx_free_list, &usb_dev->rx_free_lock);
+ return -ENOMEM;
+}
+
+static int aicwf_usb_alloc_tx_urb(struct aic_usb_dev *usb_dev)
+{
+ int i;
+
+ for (i = 0; i < AICWF_USB_TX_URBS; i++) {
+ struct aicwf_usb_buf *usb_buf = &usb_dev->usb_tx_buf[i];
+
+ usb_buf->usbdev = usb_dev;
+ usb_buf->urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!usb_buf->urb) {
+ usb_err("could not allocate tx data urb\n");
+ goto err;
+ }
+ #if defined CONFIG_USB_NO_TRANS_DMA_MAP
+ // dma buf unused
+ usb_buf->data_buf = NULL;
+ usb_buf->data_dma_trans_addr = 0x0;
+ #endif
+ list_add_tail(&usb_buf->list, &usb_dev->tx_free_list);
+ (usb_dev->tx_free_count)++;
+ }
+ return 0;
+
+err:
+ aicwf_usb_free_urb(&usb_dev->tx_free_list, &usb_dev->tx_free_lock);
+ return -ENOMEM;
+}
+
+
+static void aicwf_usb_state_change(struct aic_usb_dev *usb_dev, int state)
+{
+ int old_state;
+
+ if (usb_dev->state == state)
+ return;
+
+ old_state = usb_dev->state;
+ usb_dev->state = state;
+
+ if (state == USB_DOWN_ST) {
+ usb_dev->bus_if->state = BUS_DOWN_ST;
+ }
+ if (state == USB_UP_ST) {
+ usb_dev->bus_if->state = BUS_UP_ST;
+ }
+}
+
+static int aicwf_usb_bus_txdata(struct device *dev, struct sk_buff *skb)
+{
+ u8 *buf = NULL;
+ u16 buf_len = 0;
+ struct aicwf_usb_buf *usb_buf;
+ int ret = 0;
+ unsigned long flags;
+ struct aicwf_bus *bus_if = dev_get_drvdata(dev);
+ struct aic_usb_dev *usb_dev = bus_if->bus_priv.usb;
+ bool need_cfm = false;
+
+ if (usb_dev->state != USB_UP_ST) {
+ usb_err("usb state is not up!\n");
+ dev_kfree_skb_any(skb);
+ return -EIO;
+ }
+
+ usb_buf = aicwf_usb_tx_dequeue(usb_dev, &usb_dev->tx_free_list,
+ &usb_dev->tx_free_count, &usb_dev->tx_free_lock);
+ if (!usb_buf) {
+ usb_err("free:%d, post:%d\n", usb_dev->tx_free_count, usb_dev->tx_post_count);
+ dev_kfree_skb_any(skb);
+ ret = -ENOMEM;
+ goto flow_ctrl;
+ }
+
+ usb_buf->usbdev = usb_dev;
+ if (need_cfm)
+ usb_buf->cfm = true;
+ else
+ usb_buf->cfm = false;
+ usb_fill_bulk_urb(usb_buf->urb, usb_dev->udev, usb_dev->bulk_out_pipe,
+ buf, buf_len, aicwf_usb_tx_complete, usb_buf);
+ #if defined CONFIG_USB_NO_TRANS_DMA_MAP
+ usb_buf->urb->transfer_dma = usb_buf->data_dma_trans_addr;
+ usb_buf->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+ #endif
+ usb_buf->urb->transfer_flags |= URB_ZERO_PACKET;
+
+ aicwf_usb_tx_queue(usb_dev, &usb_dev->tx_post_list, usb_buf,
+ &usb_dev->tx_post_count, &usb_dev->tx_post_lock);
+ complete(&bus_if->bustx_trgg);
+ ret = 0;
+
+ flow_ctrl:
+ spin_lock_irqsave(&usb_dev->tx_flow_lock, flags);
+ if (usb_dev->tx_free_count < AICWF_USB_TX_LOW_WATER) {
+ usb_dev->tbusy = true;
+ aicwf_usb_tx_flowctrl(usb_dev, true);
+ }
+ spin_unlock_irqrestore(&usb_dev->tx_flow_lock, flags);
+
+ return ret;
+}
+
+static int aicwf_usb_bus_start(struct device *dev)
+{
+ struct aicwf_bus *bus_if = dev_get_drvdata(dev);
+ struct aic_usb_dev *usb_dev = bus_if->bus_priv.usb;
+
+ if (usb_dev->state == USB_UP_ST)
+ return 0;
+
+ aicwf_usb_state_change(usb_dev, USB_UP_ST);
+ aicwf_usb_rx_prepare(usb_dev);
+ aicwf_usb_tx_prepare(usb_dev);
+ return 0;
+}
+
+static void aicwf_usb_cancel_all_urbs(struct aic_usb_dev *usb_dev)
+{
+ struct aicwf_usb_buf *usb_buf, *tmp;
+ unsigned long flags;
+
+ if (usb_dev->msg_out_urb)
+ usb_kill_urb(usb_dev->msg_out_urb);
+
+ spin_lock_irqsave(&usb_dev->tx_post_lock, flags);
+ list_for_each_entry_safe(usb_buf, tmp, &usb_dev->tx_post_list, list) {
+ spin_unlock_irqrestore(&usb_dev->tx_post_lock, flags);
+ if (!usb_buf->urb) {
+ usb_err("bad usb_buf\n");
+ spin_lock_irqsave(&usb_dev->tx_post_lock, flags);
+ break;
+ }
+ usb_kill_urb(usb_buf->urb);
+ spin_lock_irqsave(&usb_dev->tx_post_lock, flags);
+ }
+ spin_unlock_irqrestore(&usb_dev->tx_post_lock, flags);
+
+ usb_kill_anchored_urbs(&usb_dev->rx_submitted);
+}
+
+static void aicwf_usb_bus_stop(struct device *dev)
+{
+ struct aicwf_bus *bus_if = dev_get_drvdata(dev);
+ struct aic_usb_dev *usb_dev = bus_if->bus_priv.usb;
+
+ if (usb_dev == NULL)
+ return;
+
+ if (usb_dev->state == USB_DOWN_ST)
+ return;
+
+ aicwf_usb_state_change(usb_dev, USB_DOWN_ST);
+ aicwf_usb_cancel_all_urbs(usb_dev);
+}
+
+static void aicwf_usb_deinit(struct aic_usb_dev *usbdev)
+{
+ cancel_work_sync(&usbdev->rx_urb_work);
+ aicwf_usb_free_urb(&usbdev->rx_free_list, &usbdev->rx_free_lock);
+ aicwf_usb_free_urb(&usbdev->tx_free_list, &usbdev->tx_free_lock);
+ usb_free_urb(usbdev->msg_out_urb);
+}
+
+static void aicwf_usb_rx_urb_work(struct work_struct *work)
+{
+ struct aic_usb_dev *usb_dev = container_of(work, struct aic_usb_dev, rx_urb_work);
+
+ aicwf_usb_rx_submit_all_urb(usb_dev);
+}
+
+static int aicwf_usb_init(struct aic_usb_dev *usb_dev)
+{
+ int ret = 0;
+
+ usb_dev->tbusy = false;
+ usb_dev->app_cmp = false;
+ usb_dev->state = USB_DOWN_ST;
+
+ init_waitqueue_head(&usb_dev->msg_wait);
+ init_usb_anchor(&usb_dev->rx_submitted);
+
+ spin_lock_init(&usb_dev->tx_free_lock);
+ spin_lock_init(&usb_dev->tx_post_lock);
+ spin_lock_init(&usb_dev->rx_free_lock);
+ spin_lock_init(&usb_dev->tx_flow_lock);
+
+ INIT_LIST_HEAD(&usb_dev->rx_free_list);
+ INIT_LIST_HEAD(&usb_dev->tx_free_list);
+ INIT_LIST_HEAD(&usb_dev->tx_post_list);
+
+ usb_dev->tx_free_count = 0;
+ usb_dev->tx_post_count = 0;
+
+ ret = aicwf_usb_alloc_rx_urb(usb_dev);
+ if (ret) {
+ goto error;
+ }
+ ret = aicwf_usb_alloc_tx_urb(usb_dev);
+ if (ret) {
+ goto error;
+ }
+
+
+ usb_dev->msg_out_urb = usb_alloc_urb(0, GFP_ATOMIC);
+ if (!usb_dev->msg_out_urb) {
+ usb_err("usb_alloc_urb (msg out) failed\n");
+ ret = ENOMEM;
+ goto error;
+ }
+
+ INIT_WORK(&usb_dev->rx_urb_work, aicwf_usb_rx_urb_work);
+
+ return ret;
+ error:
+ usb_err("failed!\n");
+ aicwf_usb_deinit(usb_dev);
+ return ret;
+}
+
+static int aicwf_parse_usb(struct aic_usb_dev *usb_dev, struct usb_interface *interface, int pid)
+{
+ struct usb_interface_descriptor *interface_desc;
+ struct usb_host_interface *host_interface;
+ struct usb_endpoint_descriptor *endpoint;
+ struct usb_device *usb = usb_dev->udev;
+ int i, endpoints;
+ u8 endpoint_num;
+ int ret = 0;
+
+ usb_dev->bulk_in_pipe = 0;
+ usb_dev->bulk_out_pipe = 0;
+#ifdef CONFIG_USB_MSG_EP
+ usb_dev->msg_out_pipe = 0;
+#endif
+
+
+ host_interface = &interface->altsetting[0];
+ interface_desc = &host_interface->desc;
+ endpoints = interface_desc->bNumEndpoints;
+
+ /* Check device configuration */
+ if (usb->descriptor.bNumConfigurations != 1) {
+ usb_err("Number of configurations: %d not supported\n",
+ usb->descriptor.bNumConfigurations);
+ ret = -ENODEV;
+ goto exit;
+ }
+
+ /* Check deviceclass */
+ if (usb->descriptor.bDeviceClass != 0x00 && usb->descriptor.bDeviceClass != 239) {
+ usb_err("DeviceClass %d not supported\n",
+ usb->descriptor.bDeviceClass);
+ ret = -ENODEV;
+ goto exit;
+ }
+
+ /* Check interface number */
+ if (usb->actconfig->desc.bNumInterfaces != 1 && usb->actconfig->desc.bNumInterfaces != 3) {
+ usb_err("Number of interfaces: %d not supported\n",
+ usb->actconfig->desc.bNumInterfaces);
+ ret = -ENODEV;
+ goto exit;
+ }
+
+ if ((interface_desc->bInterfaceClass != USB_CLASS_VENDOR_SPEC) ||
+ (interface_desc->bInterfaceSubClass != 0xff) ||
+ (interface_desc->bInterfaceProtocol != 0xff)) {
+ usb_err("non WLAN interface %d: 0x%x:0x%x:0x%x\n",
+ interface_desc->bInterfaceNumber, interface_desc->bInterfaceClass,
+ interface_desc->bInterfaceSubClass, interface_desc->bInterfaceProtocol);
+ ret = -ENODEV;
+ goto exit;
+ }
+
+ for (i = 0; i < endpoints; i++) {
+ endpoint = &host_interface->endpoint[i].desc;
+ endpoint_num = usb_endpoint_num(endpoint);
+
+ if (usb_endpoint_dir_in(endpoint) &&
+ usb_endpoint_xfer_bulk(endpoint)) {
+ if (!usb_dev->bulk_in_pipe) {
+ usb_dev->bulk_in_pipe = usb_rcvbulkpipe(usb, endpoint_num);
+ }
+ }
+
+ if (usb_endpoint_dir_out(endpoint) &&
+ usb_endpoint_xfer_bulk(endpoint)) {
+ if (!usb_dev->bulk_out_pipe)
+ {
+ usb_dev->bulk_out_pipe = usb_sndbulkpipe(usb, endpoint_num);
+ }
+#ifdef CONFIG_USB_MSG_EP
+ else if (!usb_dev->msg_out_pipe) {
+ usb_dev->msg_out_pipe = usb_sndbulkpipe(usb, endpoint_num);
+ }
+#endif
+ }
+ }
+
+ if (usb_dev->bulk_in_pipe == 0) {
+ usb_err("No RX (in) Bulk EP found\n");
+ ret = -ENODEV;
+ goto exit;
+ }
+ if (usb_dev->bulk_out_pipe == 0) {
+ usb_err("No TX (out) Bulk EP found\n");
+ ret = -ENODEV;
+ goto exit;
+ }
+
+#ifdef CONFIG_USB_MSG_EP
+ if ( usb_dev->msg_out_pipe != 0 &&
+ (usb_dev->chipid == PRODUCT_ID_AIC8801 ||
+ usb_dev->chipid == PRODUCT_ID_AIC8800D81)){
+ printk("TX Msg Bulk EP found\n");
+ usb_dev->use_msg_ep = 1;
+ }else{
+ usb_dev->use_msg_ep = 0;
+ }
+
+#if 0
+ if (usb_dev->msg_out_pipe == 0) {
+ usb_err("No TX Msg (out) Bulk EP found\n");
+ usb_dev->use_msg_ep = 0;
+ }else{
+ usb_dev->use_msg_ep = 1;
+ }
+#endif
+#endif
+
+
+ if (usb->speed == USB_SPEED_HIGH) {
+ printk("Aic high speed USB device detected\n");
+ } else {
+ printk("Aic full speed USB device detected\n");
+ }
+ exit:
+ return ret;
+}
+
+
+
+static struct aicwf_bus_ops aicwf_usb_bus_ops = {
+ .start = aicwf_usb_bus_start,
+ .stop = aicwf_usb_bus_stop,
+ .txdata = aicwf_usb_bus_txdata,
+ .txmsg = aicwf_usb_bus_txmsg,
+};
+
+#if 0
+u32 patch_tbl[][2] =
+{
+#if defined(CONFIG_RFTEST)
+ {JUMP_TABLE_OFFSET(28), 0x16b4c5}, // 161998
+ {JUMP_TABLE_OFFSET(130), 0x16b379}, // 161B30
+ {JUMP_TABLE_OFFSET(126), 0x16b155}, // 161B20
+ {JUMP_TABLE_OFFSET(132), 0x16b38d}, // 161b38
+ {JUMP_TABLE_OFFSET(34), 0x16b515}, // 1619b0
+#elif defined(CONFIG_PLATFORM_UBUNTU)
+ {JUMP_TABLE_OFFSET(28), 0x16b5a5}, // 161998
+ {JUMP_TABLE_OFFSET(130), 0x16b459}, // 161B30
+ {JUMP_TABLE_OFFSET(126), 0x16b235}, // 161B20
+ {JUMP_TABLE_OFFSET(132), 0x16b46d}, // 161b38
+ {JUMP_TABLE_OFFSET(34), 0x16b5f5}, // 1619b0
+#else
+ {JUMP_TABLE_OFFSET(28), 0x16b231}, // 161998
+ {JUMP_TABLE_OFFSET(130), 0x16b0e5}, // 161B30
+ {JUMP_TABLE_OFFSET(126), 0x16aec1}, // 161B20
+ {JUMP_TABLE_OFFSET(132), 0x16b0f9}, // 161b38
+ {JUMP_TABLE_OFFSET(34), 0x16b281}, // 1619b0
+#endif
+};
+#endif
+
+#if 0
+u32 patch_tbl_rf[][2] =
+{
+ {JUMP_TABLE_OFFSET(28), 0x16b4c5}, // 161998
+ {JUMP_TABLE_OFFSET(130), 0x16b379}, // 161B30
+ {JUMP_TABLE_OFFSET(126), 0x16b155}, // 161B20
+ {JUMP_TABLE_OFFSET(132), 0x16b38d}, // 161b38
+ {JUMP_TABLE_OFFSET(34), 0x16b515}, // 1619b0
+};
+#endif
+#if 0
+u32 patch_tbl[18][2] =
+{
+ {0x0044, 0x00000002}, //hosttype
+ {0x0048, 0x00000060},
+#ifdef CONFIG_USB_BT
+ {0x004c, 0x00000040},
+#else
+ {0x004c, 0x00000046},
+#endif
+ {0x0050, 0x00000000}, //ipc base
+ {0x0054, 0x001a0000}, //buf base
+ {0x0058, 0x001a0140}, //desc base
+ {0x005c, 0x00000ee0}, //desc size
+ {0x0060, 0x001a1020}, //pkt base
+#ifdef CONFIG_USB_BT
+ {0x0064, 0x0001EFE0}, //pkt size
+#else
+ {0x0064, 0x000207e0}, //pkt size
+#endif
+ {0x0068, 0x00000008},
+ {0x006c, 0x00000040},
+ {0x0070, 0x00000040},
+ {0x0074, 0x00000020},
+ {0x0078, 0x00000000},
+ {0x007c, 0x00000040},
+ {0x0080, 0x00190000},
+ {0x0084, 0x0000fc00},//63kB
+ {0x0088, 0x0019fc00}
+};
+#endif
+
+u32 adaptivity_patch_tbl[][2] = {
+ {0x0004, 0x0000320A}, //linkloss_thd
+ {0x0094, 0x00000000}, //ac_param_conf
+ {0x00F8, 0x00010138}, //tx_adaptivity_en
+};
+
+u32 patch_tbl[][2] ={
+{0x0044, 0x00000002}, //hosttype
+{0x0048, 0x00000060},
+#if 1//def CONFIG_USB_BT
+#ifdef CONFIG_USB_MSG_EP
+{0x004c, 0x00040050},
+#else
+{0x004c, 0x00000052},
+#endif
+#else
+{0x004c, 0x00000046},
+#endif
+{0x0050, 0x00000000}, //ipc base
+{0x0054, 0x00190000}, //buf base
+{0x0058, 0x00190140}, //desc base
+{0x005c, 0x00000ee0}, //desc size
+{0x0060, 0x00191020}, //pkt base
+#if 1//def CONFIG_USB_BT
+{0x0064, 0x0002EFE0}, //pkt size
+#else
+{0x0064, 0x000207e0}, //pkt size
+#endif
+{0x0068, 0x00000008}, //tx BK A-MPDU 8
+{0x006c, 0x00000040}, //tx BE A-MPDU 40
+{0x0070, 0x00000040}, //tx VI A-MPDU 40
+{0x0074, 0x00000020}, //tx VO A-MPDU 20
+{0x0078, 0x00000000}, //bcn queue
+{0x007c, 0x00000020}, //rx A-MPDU 20
+{0x0080, 0x001d0000},
+{0x0084, 0x0000fc00},//63kB
+{0x0088, 0x001dfc00},
+{0x00A8, 0x8D080103},//dm 8D08
+{0x00D0, 0x00010103},//aon sram
+#ifdef CONFIG_USB_MSG_EP
+{0x00D4, 0x0404087C},
+#else
+{0x00D4, 0x0000087C},
+#endif
+{0x00D8, 0x001C0000},//bt base
+{0x00DC, 0x00008000},//bt size
+{0x00E0, 0x04020A08},
+{0x00E4, 0x00000001},
+#ifdef CONFIG_USB_MSG_EP
+{0x00FC, 0x00000302},//rx msg fc pkt cnt
+#endif
+{0x0100, 0x0000000F}, //usb_reboot_additional_delay
+#if !defined(CONFIG_LINK_DET_5G)
+{0x0104, 0x00000000}, //link_det_5g
+#endif
+};
+
+
+#if 0
+u32 patch_tbl[][2] =
+{
+#ifdef CONFIG_PLATFORM_UBUNTU
+ {JUMP_TABLE_OFFSET(28), 0x16b5a5}, // 161998
+ {JUMP_TABLE_OFFSET(130), 0x16b459}, // 161B30
+ {JUMP_TABLE_OFFSET(126), 0x16b235}, // 161B20
+ {JUMP_TABLE_OFFSET(132), 0x16b46d}, // 161b38
+ {JUMP_TABLE_OFFSET(34), 0x16b5f5}, // 1619b0
+#else
+ {JUMP_TABLE_OFFSET(28), 0x16b231}, // 161998
+ {JUMP_TABLE_OFFSET(130), 0x16b0e5}, // 161B30
+ {JUMP_TABLE_OFFSET(126), 0x16aec1}, // 161B20
+ {JUMP_TABLE_OFFSET(132), 0x16b0f9}, // 161b38
+ {JUMP_TABLE_OFFSET(34), 0x16b281}, // 1619b0
+#endif
+};
+#endif
+
+u32 syscfg_tbl_pmic_u02[][2] = {
+ {0x40040000, 0x00001AC8}, // 1) fix panic
+ {0x40040084, 0x00011580},
+ {0x40040080, 0x00000001},
+ {0x40100058, 0x00000000},
+};
+
+u32 syscfg_tbl_u04[][2] = {
+ {0x40040000, 0x0000042C}, // protect usb replenish rxq / flush rxq, skip flush rxq before start_app
+ {0x40040004, 0x0000DD44},
+ {0x40040008, 0x00000448},
+ {0x4004000C, 0x0000044C},
+ {0x0019B800, 0xB9F0F19B},
+ {0x0019B804, 0x0019B81D},
+ {0x0019B808, 0xBF00FA79},
+ {0x0019B80C, 0xF007BF00},
+ {0x0019B810, 0x4605B672}, // code
+ {0x0019B814, 0x21E0F04F},
+ {0x0019B818, 0xBE0BF664},
+ {0x0019B81C, 0xF665B510},
+ {0x0019B820, 0x4804FC9D},
+ {0x0019B824, 0xFA9EF66C},
+ {0x0019B828, 0xFCA8F665},
+ {0x0019B82C, 0x4010E8BD},
+ {0x0019B830, 0xBAC6F66C},
+ {0x0019B834, 0x0019A0C4},
+ {0x40040084, 0x0019B800}, // out base
+ {0x40040080, 0x0000000F},
+ {0x40100058, 0x00000000},
+};
+
+u32 syscfg_tbl[][2] = {
+ {0x40500014, 0x00000101}, // 1)
+ {0x40500018, 0x0000010d}, // 2)//bt only:10d ,bt combo and bt only sw:109
+ {0x40500004, 0x00000010}, // 3) the order should not be changed
+ #if 1//CONFIG_PMIC_SETTING
+ {0x50000000, 0x03220204}, // 2) pmic interface init
+ {0x50019150, 0x00000002},
+ {0x50017008, 0x00000000}, // 4) stop wdg
+ #endif /* CONFIG_PMIC_SETTING */
+};
+
+u32 sys_reboot_tbl[][2] = {
+ {0x50017000, 0x0001ffff},
+ {0x50017008, 0x00000002},
+};
+
+#if 0
+u32 bt_config_tbl[][2] =
+{
+ {0x0016f000, 0xe0250793},
+ {0x40080000, 0x0008bd64},
+ #if 0
+ {0x0016f004, 0xe0052b00},
+ {0x40080004, 0x000b5570},
+ {0x0016f008, 0xe7b12b00},
+ {0x40080008, 0x000b5748},
+ {0x0016f00c, 0x001ad00e},
+ {0x4008000c, 0x000ac8f8},
+ #endif
+ {0x40080084, 0x0016f000}, // out
+ {0x40080080, 0x00000001}, // en
+ {0x40100058, 0x00000000}, // bypass
+};
+#endif
+
+u32 rf_tbl_masked[][3] = {
+ {0x40344058, 0x00800000, 0x00000000},// pll trx
+};
+
+static int system_config_8800(struct aic_usb_dev *usb_dev){
+ int syscfg_num;
+ int ret, cnt;
+ const u32 mem_addr = 0x40500000;
+ struct dbg_mem_read_cfm rd_mem_addr_cfm;
+ ret = rwnx_send_dbg_mem_read_req(usb_dev, mem_addr, &rd_mem_addr_cfm);
+ if (ret) {
+ printk("%x rd fail: %d\n", mem_addr, ret);
+ return ret;
+ }
+ chip_id = (u8)(rd_mem_addr_cfm.memdata >> 16);
+ //printk("%x=%x\n", rd_mem_addr_cfm.memaddr, rd_mem_addr_cfm.memdata);
+ ret = rwnx_send_dbg_mem_read_req(usb_dev, 0x00000004, &rd_mem_addr_cfm);
+ if (ret) {
+ printk("[0x00000004] rd fail: %d\n", ret);
+ return ret;
+ }
+ chip_sub_id = (u8)(rd_mem_addr_cfm.memdata >> 4);
+ //printk("%x=%x\n", rd_mem_addr_cfm.memaddr, rd_mem_addr_cfm.memdata);
+ printk("chip_id=%x, chip_sub_id=%x\n", chip_id, chip_sub_id);
+ if (chip_id == CHIP_REV_U02) {
+ syscfg_num = sizeof(syscfg_tbl_pmic_u02) / sizeof(u32) / 2;
+ for (cnt = 0; cnt < syscfg_num; cnt++) {
+ ret = rwnx_send_dbg_mem_write_req(usb_dev, syscfg_tbl_pmic_u02[cnt][0], syscfg_tbl_pmic_u02[cnt][1]);
+ if (ret) {
+ printk("%x write fail: %d\n", syscfg_tbl_pmic_u02[cnt][0], ret);
+ return ret;
+ }
+ }
+ }
+ if ((chip_id == CHIP_REV_U03) && (chip_sub_id == CHIP_SUB_REV_U04)) {
+ syscfg_num = sizeof(syscfg_tbl_u04) / sizeof(u32) / 2;
+ printk("cfg u04\n");
+ for (cnt = 0; cnt < syscfg_num; cnt++) {
+ ret = rwnx_send_dbg_mem_write_req(usb_dev, syscfg_tbl_u04[cnt][0], syscfg_tbl_u04[cnt][1]);
+ if (ret) {
+ printk("%x write fail: %d\n", syscfg_tbl_u04[cnt][0], ret);
+ return ret;
+ }
+ }
+ }
+ syscfg_num = sizeof(syscfg_tbl) / sizeof(u32) / 2;
+ for (cnt = 0; cnt < syscfg_num; cnt++) {
+ ret = rwnx_send_dbg_mem_write_req(usb_dev, syscfg_tbl[cnt][0], syscfg_tbl[cnt][1]);
+ if (ret) {
+ printk("%x write fail: %d\n", syscfg_tbl[cnt][0], ret);
+ return ret;
+ }
+ }
+ return 0;
+
+}
+
+static int system_config(struct aic_usb_dev *usb_dev)
+{
+ if(usb_dev->chipid == PRODUCT_ID_AIC8800){
+ return system_config_8800(usb_dev);
+ }else if(usb_dev->chipid == PRODUCT_ID_AIC8800D80){
+ return system_config_8800d80(usb_dev);
+ }else{
+ return -1;
+ }
+}
+
+
+static int system_reboot(struct aic_usb_dev *usb_dev)
+{
+ int syscfg_num;
+ int ret, cnt;
+
+ syscfg_num = sizeof(sys_reboot_tbl) / sizeof(u32) / 2;
+ for (cnt = 0; cnt < syscfg_num; cnt++) {
+ ret = rwnx_send_dbg_mem_write_req(usb_dev, sys_reboot_tbl[cnt][0], sys_reboot_tbl[cnt][1]);
+ if (ret) {
+ printk("%x write fail: %d\n", sys_reboot_tbl[cnt][0], ret);
+ return ret;
+ }
+ }
+ return 0;
+}
+
+static int rf_config(struct aic_usb_dev *usb_dev)
+{
+ int ret;
+ ret = rwnx_send_dbg_mem_mask_write_req(usb_dev,
+ rf_tbl_masked[0][0], rf_tbl_masked[0][1], rf_tbl_masked[0][2]);
+ if (ret) {
+ printk("rf config %x write fail: %d\n", rf_tbl_masked[0][0], ret);
+ }
+
+ return ret;
+}
+
+static int patch_config(struct aic_usb_dev *usb_dev)
+{
+
+ int ret = 0;
+ int cnt = 0;
+ int patch_num = 0;
+ uint32_t start_addr = 0x1e6000;
+ u32 patch_addr = start_addr;
+ u32 config_base;
+ struct dbg_mem_read_cfm rd_patch_addr_cfm;
+ const u32 rd_patch_addr = RAM_FW_ADDR + 0x0180;
+ int tmp_cnt = 0;
+ int adap_patch_num = 0;
+
+ printk("%s enter \r\n", __func__);
+
+ //if (testmode) {
+ patch_num = sizeof(patch_tbl)/4;
+
+ printk("Read FW mem: %08x\n", rd_patch_addr);
+ if ((ret = rwnx_send_dbg_mem_read_req(usb_dev, rd_patch_addr, &rd_patch_addr_cfm))) {
+ printk("patch rd fail\n");
+ }
+
+ printk("%x=%x\n", rd_patch_addr_cfm.memaddr, rd_patch_addr_cfm.memdata);
+ config_base = rd_patch_addr_cfm.memdata;
+
+ //if (testmode == FW_NORMAL_MODE) {
+ if((ret = rwnx_send_dbg_mem_write_req(usb_dev, 0x1e5318, patch_addr))) {
+ printk("%x write fail\n", 0x1e5318);
+ }
+
+ if(adap_test){
+ printk("%s for adaptivity test \r\n", __func__);
+ adap_patch_num = sizeof(adaptivity_patch_tbl)/4;
+ if((ret = rwnx_send_dbg_mem_write_req(usb_dev, 0x1e531c, patch_num + adap_patch_num))) {
+ printk("%x write fail\n", 0x1e531c);
+ }
+ }else{
+ if((ret = rwnx_send_dbg_mem_write_req(usb_dev, 0x1e531c, patch_num))) {
+ printk("%x write fail\n", 0x1e531c);
+ }
+ }
+ //}else if(testmode == FW_TEST_MODE){//for old rf fw
+ // if((ret = rwnx_send_dbg_mem_write_req(usb_dev, 0x1e4d78, patch_addr))) {
+ // printk("%x write fail\n", 0x1e4d80);
+ // }
+ // if((ret = rwnx_send_dbg_mem_write_req(usb_dev, 0x1e4d7C, patch_num))) {
+ // printk("%x write fail\n", 0x1e4d84);
+ // }
+ //}
+
+ for(cnt = 0; cnt < patch_num/2; cnt+=1)
+ {
+ if((ret = rwnx_send_dbg_mem_write_req(usb_dev, start_addr+8*cnt, patch_tbl[cnt][0]+config_base))) {
+ printk("%x write fail\n", start_addr+8*cnt);
+ }
+
+ if((ret = rwnx_send_dbg_mem_write_req(usb_dev, start_addr+8*cnt+4, patch_tbl[cnt][1]))) {
+ printk("%x write fail\n", start_addr+8*cnt+4);
+ }
+
+ }
+
+ tmp_cnt = cnt;
+
+ if(adap_test){
+ for(cnt = 0; cnt < adap_patch_num/2; cnt+=1)
+ {
+ if((ret = rwnx_send_dbg_mem_write_req(usb_dev, start_addr+8*(cnt+tmp_cnt), adaptivity_patch_tbl[cnt][0]+config_base))) {
+ printk("%x write fail\n", start_addr+8*cnt);
+ }
+
+ if((ret = rwnx_send_dbg_mem_write_req(usb_dev, start_addr+8*(cnt+tmp_cnt)+4, adaptivity_patch_tbl[cnt][1]))) {
+ printk("%x write fail\n", start_addr+8*cnt+4);
+ }
+ }
+ }
+
+ return ret;
+#if 0
+ } else {
+ patch_num = sizeof(patch_tbl) / sizeof(u32) / 2;
+
+ for(cnt = 0; cnt < patch_num; cnt++) {
+ ret = rwnx_send_dbg_mem_write_req(usb_dev, patch_tbl[cnt][0], patch_tbl[cnt][1]);
+ if(ret) {
+ printk("%x write fail: %d\n", patch_tbl[cnt][0], ret);
+ break;
+ }
+ }
+ return ret;
+ }
+#endif
+}
+
+#if 0
+static int bt_config(struct aic_usb_dev *usb_dev)
+{
+ int trap_num = sizeof(bt_config_tbl) / sizeof(u32) / 2;
+ int ret, cnt;
+
+ printk("%s enter \r\n", __func__);
+ for(cnt = 0; cnt < trap_num; cnt++) {
+ ret = rwnx_send_dbg_mem_write_req(usb_dev, bt_config_tbl[cnt][0], bt_config_tbl[cnt][1]);
+ if(ret) {
+ printk("%x write fail: %d\n", bt_config_tbl[cnt][0], ret);
+ break;
+ }
+ }
+ return ret;
+}
+#endif
+
+static int get_paring_ids(char* c_paringids, int* i_paringids){
+ int i = 0;
+ int len = strlen(c_paringids);
+ int paring_id_num = 0;
+ char temp_paring_id[5];
+
+//get paring_id_num
+ for(i = 4; i < len; i += 5){
+ if(c_paringids[i] == ','){
+ paring_id_num++;
+ }else if(c_paringids[i] == 0x00){
+ break;
+ }else{
+ paring_id_num = 0;
+ break;
+ }
+ }
+
+ if(paring_id_num == 0 && len == 0){
+ printk("%s paring_id_num:%d \r\n", __func__, paring_id_num);
+ return 0;
+ }else{
+ paring_id_num++;
+ }
+
+//parsing paring_id
+ for(i = 0; i < paring_id_num; i++){
+ memset(temp_paring_id, 0, 5);
+ temp_paring_id[0] = c_paringids[(i * 5)];
+ temp_paring_id[1] = c_paringids[(i * 5) + 1];
+ temp_paring_id[2] = c_paringids[(i * 5) + 2];
+ temp_paring_id[3] = c_paringids[(i * 5) + 3];
+ i_paringids[i] = rwnx_atoli(temp_paring_id);
+ }
+
+ return paring_id_num;
+}
+
+static int aicloadfw_chipmatch(struct aic_usb_dev *usb_dev, u16 vid, u16 pid){
+
+ if(pid == USB_DEVICE_ID_AIC){
+ usb_dev->chipid = PRODUCT_ID_AIC8800;
+ AICWFDBG(LOGINFO, "%s USE AIC8800\r\n", __func__);
+ return 0;
+ }else if(pid == USB_DEVICE_ID_AIC_8801){
+ usb_dev->chipid = PRODUCT_ID_AIC8801;
+ AICWFDBG(LOGINFO, "%s USE AIC8801\r\n", __func__);
+ return 0;
+ }else if(pid == USB_DEVICE_ID_AIC_8800D80){
+ usb_dev->chipid = PRODUCT_ID_AIC8800D80;
+ AICWFDBG(LOGINFO, "%s USE AIC8800D80\r\n", __func__);
+ return 0;
+ }else if(pid == USB_DEVICE_ID_AIC_8800D81){
+ usb_dev->chipid = PRODUCT_ID_AIC8800D81;
+ AICWFDBG(LOGINFO, "%s USE AIC8800D81\r\n", __func__);
+ return 0;
+ }else{
+ return -1;
+ }
+}
+
+int aicfw_download_fw_8800(struct aic_usb_dev *usb_dev){
+ //uint32_t paring_id = 0;
+ uint32_t* paring_ids;
+ int paring_id_num = 0;
+ int i = 0;
+ const u32 fw_addr = RAM_FW_ADDR;
+
+#ifdef CONFIG_M2D_OTA_AUTO_SUPPORT
+ if(testmode == FW_M2D_OTA_MODE){
+ rwnx_plat_m2d_flash_ota_android(usb_dev,FW_M2D_OTA_NAME);
+ }else if(testmode == FW_NORMAL_MODE) {
+ rwnx_plat_m2d_flash_ota_check(usb_dev,FW_M2D_OTA_NAME);
+ }
+#endif
+ if(testmode == FW_TEST_MODE){
+ if (rwnx_plat_bin_fw_upload_android(usb_dev, RAM_FW_ADDR, FW_RF_BASE_NAME)) {
+ return -1;
+ }
+
+ if (chip_id == CHIP_REV_U03) {
+ if(rwnx_plat_bin_fw_upload_android(usb_dev, FW_RAM_ADID_BASE_ADDR, FW_ADID_BASE_NAME_U03)) {
+ return -1;;
+ }
+
+ if(rwnx_plat_bin_fw_upload_android(usb_dev, FW_RAM_PATCH_BASE_ADDR_U03, FW_PATCH_BASE_NAME_U03)) {
+ return -1;;
+ }
+ } else {
+ if(rwnx_plat_bin_fw_upload_android(usb_dev, FW_RAM_ADID_BASE_ADDR, FW_ADID_BASE_NAME)) {
+ return -1;;
+ }
+
+ if(rwnx_plat_bin_fw_upload_android(usb_dev, FW_RAM_PATCH_BASE_ADDR, FW_PATCH_BASE_NAME)) {
+ return -1;;
+ }
+ }
+
+#if 0
+ if(rwnx_plat_bin_fw_upload_android(usb_dev, FW_RAM_ADID_BASE_ADDR, FW_RF_ADID_BASE_NAME)) {
+ goto out_free_bus;
+ }
+
+ if(rwnx_plat_bin_fw_upload_android(usb_dev, FW_RAM_PATCH_BASE_ADDR, FW_RF_PATCH_BASE_NAME)) {
+ goto out_free_bus;
+ }
+#endif
+ } else if(testmode == FW_BLE_SCAN_WAKEUP_MODE){
+#if 0
+ paring_id = rwnx_atoli(paringid);
+ rwnx_plat_bin_fw_upload_android(usb_dev, RAM_FW_BLE_SCAN_WAKEUP_ADDR, FW_BLE_SCAN_WAKEUP_NAME);
+ rwnx_send_dbg_mem_write_req(usb_dev, 0x15FF00, 0x53454C42);//magic_num
+ rwnx_send_dbg_mem_write_req(usb_dev, 0x15FF04, ble_scan_wakeup_reboot_time);//reboot time
+ rwnx_send_dbg_mem_write_req(usb_dev, 0x15FF08, paring_id);
+ rwnx_send_dbg_mem_write_req(usb_dev, 0x15FF0c, paring_id);
+ rwnx_send_dbg_start_app_req(usb_dev, RAM_FW_BLE_SCAN_WAKEUP_ADDR, HOST_START_APP_AUTO);
+#endif
+#if 1
+ paring_ids = (uint32_t*)kmalloc(sizeof(uint32_t) * 8, GFP_KERNEL);
+ rwnx_plat_bin_fw_upload_android(usb_dev, RAM_FW_BLE_SCAN_WAKEUP_ADDR, FW_BLE_SCAN_WAKEUP_NAME);
+ rwnx_send_dbg_mem_write_req(usb_dev, 0x15FF00, 0x53454C42);//magic_num
+ rwnx_send_dbg_mem_write_req(usb_dev, 0x15FF04, ble_scan_wakeup_reboot_time);//reboot time
+ paring_id_num = get_paring_ids(paringid, paring_ids);
+ for(i = 0; i < paring_id_num; i++){
+ printk("paring_ids[%d]:0x%X \r\n", i, paring_ids[i]);
+ rwnx_send_dbg_mem_write_req(usb_dev, 0x15FF08 + (4 * i), paring_ids[i]);
+ }
+ rwnx_send_dbg_start_app_req(usb_dev, RAM_FW_BLE_SCAN_WAKEUP_ADDR, HOST_START_APP_AUTO);
+ kfree(paring_ids);
+#endif
+ return -1;
+
+ } else if(testmode == FW_BLE_SCAN_AD_FILTER_MODE){
+
+/*
+ data and ad_data_filter_mask instructions for use
+ ex.
+ data[18] = {0x46,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0x30,0xff,0xff,0xff,0x43,0x52,0x45,0x4c,0x42};
+ mask = 1100 0000 0111 1111 1100 0000 0000 0000 = 0xc07fc000
+
+ data = 0x46,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0x30,0xff,0xff,0xff,0x43,0x52,0x45,0x4c,0x42
+ mask = 1 1 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 0 0...... fill 0
+
+ data & mask = "0x46 0x00" 0x00 0x00 0x00 0x00 0x00 0x00 0x00 "0x30 0xff 0xff 0x43 0x52 0x45 0x4c 0x42"
+ using data & mask value condition to wakeup host_wake_bt gpio
+*/
+ const uint8_t data[18] = {0x46,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0x30,0xff,0xff,0xff,0x43,0x52,0x45,0x4c,0x42};
+ struct ad_data_filter* ad_data = (struct ad_data_filter*)kmalloc(sizeof(struct ad_data_filter), GFP_KERNEL);
+ uint32_t *write_blocks = (uint32_t *)ad_data;
+
+ memset(ad_data, 0, sizeof(struct ad_data_filter));
+ rwnx_plat_bin_fw_upload_android(usb_dev, RAM_FW_BLE_SCAN_WAKEUP_ADDR, FW_BLE_SCAN_AD_FILTER_NAME);
+ rwnx_send_dbg_mem_write_req(usb_dev, 0x15FF00, 0x53454C42);//magic_num
+ rwnx_send_dbg_mem_write_req(usb_dev, 0x15FF04, ble_scan_wakeup_reboot_time);//reboot time
+ ad_data->ad_len = 0x13;
+ ad_data->ad_type = 0xff;
+ memcpy(ad_data->ad_data, data,ad_data->ad_len-1);// 1100 0000 0111 1111 1100 0000 0000 0000 //0xc07fc000
+ ad_data_filter_mask = 0xc07fc000;
+ rwnx_send_dbg_mem_write_req(usb_dev, 0x15FF08, ad_data_filter_mask);//reboot time
+ for(i = 0; i < 9; i++){
+ printk("write_blocks[%d]:0x%08X \r\n", i, write_blocks[i]);
+ rwnx_send_dbg_mem_write_req(usb_dev, 0x15FF0c + (4 * i), write_blocks[i]);
+ }
+ rwnx_send_dbg_start_app_req(usb_dev, RAM_FW_BLE_SCAN_WAKEUP_ADDR, HOST_START_APP_AUTO);
+ kfree(ad_data);
+ return -1;
+ } else {
+ if (rwnx_plat_bin_fw_upload_android(usb_dev, RAM_FW_ADDR, FW_BASE_NAME)) {
+ return -1;;
+ }
+ if (chip_id == CHIP_REV_U03) {
+ if(rwnx_plat_bin_fw_upload_android(usb_dev, FW_RAM_ADID_BASE_ADDR, FW_ADID_BASE_NAME_U03)) {
+ return -1;;
+ }
+
+ if(rwnx_plat_bin_fw_upload_android(usb_dev, FW_RAM_PATCH_BASE_ADDR_U03, FW_PATCH_BASE_NAME_U03)) {
+ return -1;;
+ }
+ } else {
+ if(rwnx_plat_bin_fw_upload_android(usb_dev, FW_RAM_ADID_BASE_ADDR, FW_ADID_BASE_NAME)) {
+ return -1;;
+ }
+
+ if(rwnx_plat_bin_fw_upload_android(usb_dev, FW_RAM_PATCH_BASE_ADDR, FW_PATCH_BASE_NAME)) {
+ return -1;;
+ }
+ }
+ }
+ if (chip_id == CHIP_REV_U03) {
+ if (rwnx_plat_bin_fw_patch_table_upload_android(usb_dev, FW_PATCH_TABLE_NAME_U03)) {
+ return -1;;
+ }
+ } else {
+ if (rwnx_plat_bin_fw_patch_table_upload_android(usb_dev, FW_PATCH_TABLE_NAME)) {
+ return -1;;
+ }
+ }
+
+#if 0
+ if(testmode == FW_TEST_MODE){
+ if(rwnx_plat_bin_fw_upload_android(usb_dev, FW_PATCH_TEST_BASE_ADDR, FW_PATCH_TEST_BASE_NAME)) {
+ goto out_free_bus;
+ }
+ }
+#endif
+
+ if (rwnx_plat_userconfig_upload_android(usb_dev, FW_USERCONFIG_NAME)){
+ return -1;
+ }
+
+ if (patch_config(usb_dev)) {
+ return -1;;
+ }
+
+ if (rf_config(usb_dev)){
+ return -1;;
+ }
+ if (rwnx_send_dbg_start_app_req(usb_dev, fw_addr, HOST_START_APP_AUTO)) {
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int aicfw_download_fw(struct aic_usb_dev *usb_dev)
+{
+ if(usb_dev->chipid == PRODUCT_ID_AIC8800){
+ return aicfw_download_fw_8800(usb_dev);
+ }else if(usb_dev->chipid == PRODUCT_ID_AIC8800D80){
+ return aicfw_download_fw_8800d80(usb_dev);
+ }else{
+ return -1;
+ }
+}
+
+
+static int aicwf_usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+ int ret = 0;
+ struct usb_device *usb = interface_to_usbdev(intf);
+ struct aicwf_bus *bus_if ;
+ struct device *dev = NULL;
+ struct aicwf_rx_priv* rx_priv = NULL;
+ struct aic_usb_dev *usb_dev = NULL;
+
+
+ AICWFDBG(LOGINFO, "%s vid:0x%X pid:0x%X icl:0x%X isc:0x%X ipr:0x%X \r\n", __func__,
+ id->idVendor,
+ id->idProduct,
+ id->bInterfaceClass,
+ id->bInterfaceSubClass,
+ id->bInterfaceProtocol);
+
+ if(fw_loaded == 1 &&
+ (id->idProduct == USB_DEVICE_ID_AIC_8801 ||
+ id->idProduct == USB_DEVICE_ID_AIC_8800D81)){
+ return -1;
+ }
+
+ usb_dev = kzalloc(sizeof(struct aic_usb_dev), GFP_ATOMIC);
+ if (!usb_dev) {
+ return -ENOMEM;
+ }
+
+ usb_dev->udev = usb;
+ usb_dev->dev = &usb->dev;
+ ret = aicloadfw_chipmatch(usb_dev, id->idVendor, id->idProduct);
+ if (ret) {
+ AICWFDBG(LOGERROR, "%s chip unsupport.\r\n", __func__);
+ goto out_free;
+ }
+
+ usb_set_intfdata(intf, usb_dev);
+
+ ret = aicwf_parse_usb(usb_dev, intf, id->idProduct);
+ if (ret) {
+ usb_err("aicwf_parse_usb err %d\n", ret);
+ goto out_free;
+ }
+
+ ret = aicwf_usb_init(usb_dev);
+ if (ret) {
+ usb_err("aicwf_usb_init err %d\n", ret);
+ goto out_free;
+ }
+
+ bus_if = kzalloc(sizeof(struct aicwf_bus), GFP_ATOMIC);
+ if (!bus_if) {
+ ret = -ENOMEM;
+ goto out_free_usb;
+ }
+
+ dev = usb_dev->dev;
+ bus_if->dev = dev;
+ usb_dev->bus_if = bus_if;
+ bus_if->bus_priv.usb = usb_dev;
+ dev_set_drvdata(dev, bus_if);
+
+ bus_if->ops = &aicwf_usb_bus_ops;
+
+ rx_priv = aicwf_rx_init(usb_dev);
+ if(!rx_priv) {
+ txrx_err("rx init failed\n");
+ ret = -1;
+ goto out_free_bus;
+ }
+ usb_dev->rx_priv = rx_priv;
+
+ ret = aicwf_bus_init(0, dev);
+ if (ret < 0) {
+ usb_err("aicwf_bus_init err %d\n", ret);
+ goto out_free_bus;
+ }
+
+ ret = aicwf_bus_start(bus_if);
+ if (ret < 0) {
+ usb_err("aicwf_bus_start err %d\n", ret);
+ goto out_free_bus;
+ }
+
+ aic_bt_platform_init(usb_dev);
+
+ if (usb->speed != USB_SPEED_HIGH) {
+ printk("Aic full speed USB device detected\n");
+ system_reboot(usb_dev);
+ goto out_free_bus;
+ }
+
+ if(fw_loaded == 0 &&
+ (usb_dev->chipid == PRODUCT_ID_AIC8801 ||
+ usb_dev->chipid == PRODUCT_ID_AIC8800D81)){
+ rwnx_send_reboot(usb_dev);
+ goto out_free_bus;
+ }
+
+ if (system_config(usb_dev)) {
+ goto out_free_bus;
+ }
+
+ if (aicfw_download_fw(usb_dev)){
+ goto out_free_bus;
+ }
+
+ usb_dev->app_cmp = true;
+ fw_loaded = 1;
+
+ return 0;
+
+out_free_bus:
+ aicwf_bus_deinit(dev);
+ kfree(bus_if);
+out_free_usb:
+ aicwf_usb_deinit(usb_dev);
+out_free:
+ usb_err("failed with errno %d\n", ret);
+ kfree(usb_dev);
+ usb_set_intfdata(intf, NULL);
+ return ret;
+}
+
+static void aicwf_usb_disconnect(struct usb_interface *intf)
+{
+ struct aic_usb_dev *usb_dev =
+ (struct aic_usb_dev *) usb_get_intfdata(intf);
+
+ printk("%s enter \r\n", __func__);
+
+ if (!usb_dev)
+ return;
+
+ aicwf_bus_deinit(usb_dev->dev);
+ aicwf_usb_deinit(usb_dev);
+ if (usb_dev->rx_priv)
+ aicwf_rx_deinit(usb_dev->rx_priv);
+ kfree(usb_dev->bus_if);
+ kfree(usb_dev);
+}
+#if 0
+static int aicwf_usb_suspend(struct usb_interface *intf, pm_message_t state)
+{
+ struct aic_usb_dev *usb_dev =
+ (struct aic_usb_dev *) usb_get_intfdata(intf);
+
+ aicwf_usb_state_change(usb_dev, USB_SLEEP_ST);
+ aicwf_bus_stop(usb_dev->bus_if);
+ return 0;
+}
+
+static int aicwf_usb_resume(struct usb_interface *intf)
+{
+ struct aic_usb_dev *usb_dev =
+ (struct aic_usb_dev *) usb_get_intfdata(intf);
+
+ if (usb_dev->state == USB_UP_ST)
+ return 0;
+
+ aicwf_bus_start(usb_dev->bus_if);
+ return 0;
+}
+
+static int aicwf_usb_reset_resume(struct usb_interface *intf)
+{
+ return aicwf_usb_resume(intf);
+}
+#endif
+static struct usb_device_id aicwf_usb_id_table[] = {
+ {USB_DEVICE(USB_VENDOR_ID_AIC, USB_DEVICE_ID_AIC)},
+ {USB_DEVICE(USB_VENDOR_ID_AIC, USB_DEVICE_ID_AIC_8801)},
+ {USB_DEVICE(USB_VENDOR_ID_AIC, USB_DEVICE_ID_AIC_8800D80)},
+ {USB_DEVICE(USB_VENDOR_ID_AIC, USB_DEVICE_ID_AIC_8800D81)},
+ {}
+};
+
+MODULE_DEVICE_TABLE(usb, aicwf_usb_id_table);
+
+static struct usb_driver aicwf_usbdrvr = {
+ .name = KBUILD_MODNAME,
+ .probe = aicwf_usb_probe,
+ .disconnect = aicwf_usb_disconnect,
+ .id_table = aicwf_usb_id_table,
+ //.suspend = aicwf_usb_suspend,
+ //.resume = aicwf_usb_resume,
+ //.reset_resume = aicwf_usb_reset_resume,
+ .supports_autosuspend = 0,
+ .disable_hub_initiated_lpm = 1,
+};
+
+void aicwf_usb_register(void)
+{
+ if (usb_register(&aicwf_usbdrvr) < 0) {
+ usb_err("usb_register failed\n");
+ }
+}
+
+void aicwf_usb_exit(void)
+{
+ usb_deregister(&aicwf_usbdrvr);
+}
diff --git a/drivers/net/wireless/aic8800/aic_load_fw/aicwf_usb.h b/drivers/net/wireless/aic8800/aic_load_fw/aicwf_usb.h
new file mode 100644
index 000000000000..e86d28468d5e
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic_load_fw/aicwf_usb.h
@@ -0,0 +1,187 @@
+/**
+ * aicwf_usb.h
+ *
+ * USB function declarations
+ *
+ * Copyright (C) AICSemi 2018-2020
+ */
+
+#ifndef _AICWF_USB_H_
+#define _AICWF_USB_H_
+
+#include <linux/usb.h>
+#include <linux/skbuff.h>
+#include <linux/if_ether.h>
+#include <linux/ieee80211.h>
+#include <linux/semaphore.h>
+
+#include "aicbluetooth_cmds.h"
+
+#ifdef AICWF_USB_SUPPORT
+
+/* USB Device ID */
+#define USB_VENDOR_ID_AIC 0xA69C
+#define USB_DEVICE_ID_AIC 0x8800
+#define USB_DEVICE_ID_AIC_8801 0x8801
+
+#define CHIP_REV_U01 0x1
+#define CHIP_REV_U02 0x3
+#define CHIP_REV_U03 0x7
+#define CHIP_SUB_REV_U04 0x20
+
+enum AICWF_IC{
+ PRODUCT_ID_AIC8800 = 0,
+ PRODUCT_ID_AIC8801,
+ PRODUCT_ID_AIC8800DC,
+ PRODUCT_ID_AIC8800DW,
+ PRODUCT_ID_AIC8800D80,
+ PRODUCT_ID_AIC8800D81,
+};
+
+
+#define AICWF_USB_RX_URBS (20)
+#define AICWF_USB_TX_URBS (100)
+#define AICWF_USB_TX_LOW_WATER (AICWF_USB_TX_URBS/4)
+#define AICWF_USB_TX_HIGH_WATER (AICWF_USB_TX_LOW_WATER*3)
+#define AICWF_USB_MAX_PKT_SIZE (2048)
+
+#ifdef CONFIG_RFTEST
+#define FW_RF_PATCH_BASE_NAME "fw_patch.bin"
+#define FW_RF_ADID_BASE_NAME "fw_adid.bin"
+#define FW_RF_BASE_NAME "fmacfw_rf.bin"
+#define FW_PATCH_BASE_NAME "fw_patch.bin"
+#define FW_ADID_BASE_NAME "fw_adid.bin"
+#define FW_BASE_NAME "fmacfw.bin"
+#define FW_BLE_SCAN_WAKEUP_NAME "fw_ble_scan.bin"
+#define FW_BLE_SCAN_AD_FILTER_NAME "fw_ble_scan_ad_filter.bin"
+
+#define FW_PATCH_BASE_NAME_PC "fw_patch.bin"
+#define FW_ADID_BASE_NAME_PC "fw_adid.bin"
+#define FW_BASE_NAME_PC "fmacfw.bin"
+#define FW_RF_PATCH_BASE_NAME_PC "fw_patch.bin"
+#define FW_RF_ADID_BASE_NAME_PC "fw_adid.bin"
+#define FW_RF_BASE_NAME_PC "fmacfw_rf.bin"
+#define FW_PATCH_TEST_BASE_NAME "fw_patch_test.bin"
+#define FW_PATCH_BASE_NAME_U03 "fw_patch_u03.bin"
+#define FW_ADID_BASE_NAME_U03 "fw_adid_u03.bin"
+
+#else
+#define FW_PATCH_BASE_NAME "fw_patch.bin"
+#define FW_ADID_BASE_NAME "fw_adid.bin"
+#define FW_BASE_NAME "fmacfw.bin"
+#endif
+
+#define FW_PATCH_TABLE_NAME "fw_patch_table.bin"
+#define FW_PATCH_TABLE_NAME_U03 "fw_patch_table_u03.bin"
+#define FW_USERCONFIG_NAME "aic_userconfig.txt"
+#define FW_M2D_OTA_NAME "m2d_ota.bin"
+
+#define RAM_FW_BLE_SCAN_WAKEUP_ADDR 0x00100000
+#define RAM_FW_ADDR 0x00110000
+#define FW_RAM_ADID_BASE_ADDR 0x00161928
+#define FW_RAM_PATCH_BASE_ADDR 0x00100000
+#define FW_RAM_PATCH_BASE_ADDR_U03 0x00100000
+#define FW_PATCH_TEST_BASE_ADDR 0x00100000
+
+enum {
+ FW_NORMAL_MODE,
+ FW_TEST_MODE,
+ FW_BLE_SCAN_WAKEUP_MODE,
+ FW_M2D_OTA_MODE,
+ FW_DPDCALIB_MODE,
+ FW_BLE_SCAN_AD_FILTER_MODE,
+};
+
+
+#if 0
+#define FW_NAME2 FW_BASE_NAME".bin"
+#define FW_PATCH_BIN_NAME FW_PATCH_BASE_NAME".bin"
+#define FW_ADID_BIN_NAME FW_ADID_BASE_NAME".bin"
+#endif
+
+typedef enum {
+ USB_TYPE_DATA = 0X00,
+ USB_TYPE_CFG = 0X10,
+ USB_TYPE_CFG_CMD_RSP = 0X11,
+ USB_TYPE_CFG_DATA_CFM = 0X12
+} usb_type;
+
+enum aicwf_usb_state {
+ USB_DOWN_ST,
+ USB_UP_ST,
+ USB_SLEEP_ST
+};
+
+struct ad_data_filter {
+ uint8_t ad_len;
+ uint8_t ad_type;
+ uint8_t ad_data[31];
+};
+
+struct aicwf_usb_buf {
+ struct list_head list;
+ struct aic_usb_dev *usbdev;
+ struct urb *urb;
+ struct sk_buff *skb;
+ #ifdef CONFIG_USB_NO_TRANS_DMA_MAP
+ u8 *data_buf;
+ dma_addr_t data_dma_trans_addr;
+ #endif
+ bool cfm;
+};
+
+struct aic_usb_dev {
+ struct rwnx_cmd_mgr cmd_mgr;
+ struct aicwf_bus *bus_if;
+ struct usb_device *udev;
+ struct device *dev;
+ struct aicwf_rx_priv* rx_priv;
+ enum aicwf_usb_state state;
+
+ struct usb_anchor rx_submitted;
+ struct work_struct rx_urb_work;
+
+ spinlock_t rx_free_lock;
+ spinlock_t tx_free_lock;
+ spinlock_t tx_post_lock;
+ spinlock_t tx_flow_lock;
+
+ struct list_head rx_free_list;
+ struct list_head tx_free_list;
+ struct list_head tx_post_list;
+
+ uint bulk_in_pipe;
+ uint bulk_out_pipe;
+#ifdef CONFIG_USB_MSG_EP
+ uint msg_out_pipe;
+ uint use_msg_ep;
+#endif
+
+ int tx_free_count;
+ int tx_post_count;
+
+ struct aicwf_usb_buf usb_tx_buf[AICWF_USB_TX_URBS];
+ struct aicwf_usb_buf usb_rx_buf[AICWF_USB_RX_URBS];
+
+ int msg_finished;
+ wait_queue_head_t msg_wait;
+ ulong msg_busy;
+ struct urb *msg_out_urb;
+ #ifdef CONFIG_USB_NO_TRANS_DMA_MAP
+ dma_addr_t cmd_dma_trans_addr;
+ #endif
+
+ u16 chipid;
+ bool tbusy;
+ bool app_cmp;
+};
+
+extern void aicwf_usb_exit(void);
+extern void aicwf_usb_register(void);
+extern void aicwf_usb_tx_flowctrl(struct aic_usb_dev *usb_dev, bool state);
+int usb_bustx_thread(void *data);
+int usb_busrx_thread(void *data);
+int aicwf_process_rxframes(struct aicwf_rx_priv *rx_priv);
+
+#endif /* AICWF_USB_SUPPORT */
+#endif /* _AICWF_USB_H_ */
diff --git a/drivers/net/wireless/aic8800/aic_load_fw/md5.c b/drivers/net/wireless/aic8800/aic_load_fw/md5.c
new file mode 100644
index 000000000000..3d7b65375adb
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic_load_fw/md5.c
@@ -0,0 +1,161 @@
+#include <linux/memory.h>
+#include "md5.h"
+
+unsigned char PADDING[]={0x80,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};
+
+void MD5Init(MD5_CTX *context)
+{
+ context->count[0] = 0;
+ context->count[1] = 0;
+ context->state[0] = 0x67452301;
+ context->state[1] = 0xEFCDAB89;
+ context->state[2] = 0x98BADCFE;
+ context->state[3] = 0x10325476;
+}
+void MD5Update(MD5_CTX *context,unsigned char *input,unsigned int inputlen)
+{
+ unsigned int i = 0,index = 0,partlen = 0;
+ index = (context->count[0] >> 3) & 0x3F;
+ partlen = 64 - index;
+ context->count[0] += inputlen << 3;
+ if(context->count[0] < (inputlen << 3))
+ context->count[1]++;
+ context->count[1] += inputlen >> 29;
+
+ if(inputlen >= partlen)
+ {
+ memcpy(&context->buffer[index],input,partlen);
+ MD5Transform(context->state,context->buffer);
+ for(i = partlen;i+64 <= inputlen;i+=64)
+ MD5Transform(context->state,&input[i]);
+ index = 0;
+ }
+ else
+ {
+ i = 0;
+ }
+ memcpy(&context->buffer[index],&input[i],inputlen-i);
+}
+void MD5Final(MD5_CTX *context,unsigned char digest[16])
+{
+ unsigned int index = 0,padlen = 0;
+ unsigned char bits[8];
+ index = (context->count[0] >> 3) & 0x3F;
+ padlen = (index < 56)?(56-index):(120-index);
+ MD5Encode(bits,context->count,8);
+ MD5Update(context,PADDING,padlen);
+ MD5Update(context,bits,8);
+ MD5Encode(digest,context->state,16);
+}
+void MD5Encode(unsigned char *output,unsigned int *input,unsigned int len)
+{
+ unsigned int i = 0,j = 0;
+ while(j < len)
+ {
+ output[j] = input[i] & 0xFF;
+ output[j+1] = (input[i] >> 8) & 0xFF;
+ output[j+2] = (input[i] >> 16) & 0xFF;
+ output[j+3] = (input[i] >> 24) & 0xFF;
+ i++;
+ j+=4;
+ }
+}
+void MD5Decode(unsigned int *output,unsigned char *input,unsigned int len)
+{
+ unsigned int i = 0,j = 0;
+ while(j < len)
+ {
+ output[i] = (input[j]) |
+ (input[j+1] << 8) |
+ (input[j+2] << 16) |
+ (input[j+3] << 24);
+ i++;
+ j+=4;
+ }
+}
+void MD5Transform(unsigned int state[4],unsigned char block[64])
+{
+ unsigned int a = state[0];
+ unsigned int b = state[1];
+ unsigned int c = state[2];
+ unsigned int d = state[3];
+ unsigned int x[64];
+ MD5Decode(x,block,64);
+ FF(a, b, c, d, x[ 0], 7, 0xd76aa478); /* 1 */
+ FF(d, a, b, c, x[ 1], 12, 0xe8c7b756); /* 2 */
+ FF(c, d, a, b, x[ 2], 17, 0x242070db); /* 3 */
+ FF(b, c, d, a, x[ 3], 22, 0xc1bdceee); /* 4 */
+ FF(a, b, c, d, x[ 4], 7, 0xf57c0faf); /* 5 */
+ FF(d, a, b, c, x[ 5], 12, 0x4787c62a); /* 6 */
+ FF(c, d, a, b, x[ 6], 17, 0xa8304613); /* 7 */
+ FF(b, c, d, a, x[ 7], 22, 0xfd469501); /* 8 */
+ FF(a, b, c, d, x[ 8], 7, 0x698098d8); /* 9 */
+ FF(d, a, b, c, x[ 9], 12, 0x8b44f7af); /* 10 */
+ FF(c, d, a, b, x[10], 17, 0xffff5bb1); /* 11 */
+ FF(b, c, d, a, x[11], 22, 0x895cd7be); /* 12 */
+ FF(a, b, c, d, x[12], 7, 0x6b901122); /* 13 */
+ FF(d, a, b, c, x[13], 12, 0xfd987193); /* 14 */
+ FF(c, d, a, b, x[14], 17, 0xa679438e); /* 15 */
+ FF(b, c, d, a, x[15], 22, 0x49b40821); /* 16 */
+
+ /* Round 2 */
+ GG(a, b, c, d, x[ 1], 5, 0xf61e2562); /* 17 */
+ GG(d, a, b, c, x[ 6], 9, 0xc040b340); /* 18 */
+ GG(c, d, a, b, x[11], 14, 0x265e5a51); /* 19 */
+ GG(b, c, d, a, x[ 0], 20, 0xe9b6c7aa); /* 20 */
+ GG(a, b, c, d, x[ 5], 5, 0xd62f105d); /* 21 */
+ GG(d, a, b, c, x[10], 9, 0x2441453); /* 22 */
+ GG(c, d, a, b, x[15], 14, 0xd8a1e681); /* 23 */
+ GG(b, c, d, a, x[ 4], 20, 0xe7d3fbc8); /* 24 */
+ GG(a, b, c, d, x[ 9], 5, 0x21e1cde6); /* 25 */
+ GG(d, a, b, c, x[14], 9, 0xc33707d6); /* 26 */
+ GG(c, d, a, b, x[ 3], 14, 0xf4d50d87); /* 27 */
+ GG(b, c, d, a, x[ 8], 20, 0x455a14ed); /* 28 */
+ GG(a, b, c, d, x[13], 5, 0xa9e3e905); /* 29 */
+ GG(d, a, b, c, x[ 2], 9, 0xfcefa3f8); /* 30 */
+ GG(c, d, a, b, x[ 7], 14, 0x676f02d9); /* 31 */
+ GG(b, c, d, a, x[12], 20, 0x8d2a4c8a); /* 32 */
+
+ /* Round 3 */
+ HH(a, b, c, d, x[ 5], 4, 0xfffa3942); /* 33 */
+ HH(d, a, b, c, x[ 8], 11, 0x8771f681); /* 34 */
+ HH(c, d, a, b, x[11], 16, 0x6d9d6122); /* 35 */
+ HH(b, c, d, a, x[14], 23, 0xfde5380c); /* 36 */
+ HH(a, b, c, d, x[ 1], 4, 0xa4beea44); /* 37 */
+ HH(d, a, b, c, x[ 4], 11, 0x4bdecfa9); /* 38 */
+ HH(c, d, a, b, x[ 7], 16, 0xf6bb4b60); /* 39 */
+ HH(b, c, d, a, x[10], 23, 0xbebfbc70); /* 40 */
+ HH(a, b, c, d, x[13], 4, 0x289b7ec6); /* 41 */
+ HH(d, a, b, c, x[ 0], 11, 0xeaa127fa); /* 42 */
+ HH(c, d, a, b, x[ 3], 16, 0xd4ef3085); /* 43 */
+ HH(b, c, d, a, x[ 6], 23, 0x4881d05); /* 44 */
+ HH(a, b, c, d, x[ 9], 4, 0xd9d4d039); /* 45 */
+ HH(d, a, b, c, x[12], 11, 0xe6db99e5); /* 46 */
+ HH(c, d, a, b, x[15], 16, 0x1fa27cf8); /* 47 */
+ HH(b, c, d, a, x[ 2], 23, 0xc4ac5665); /* 48 */
+
+ /* Round 4 */
+ II(a, b, c, d, x[ 0], 6, 0xf4292244); /* 49 */
+ II(d, a, b, c, x[ 7], 10, 0x432aff97); /* 50 */
+ II(c, d, a, b, x[14], 15, 0xab9423a7); /* 51 */
+ II(b, c, d, a, x[ 5], 21, 0xfc93a039); /* 52 */
+ II(a, b, c, d, x[12], 6, 0x655b59c3); /* 53 */
+ II(d, a, b, c, x[ 3], 10, 0x8f0ccc92); /* 54 */
+ II(c, d, a, b, x[10], 15, 0xffeff47d); /* 55 */
+ II(b, c, d, a, x[ 1], 21, 0x85845dd1); /* 56 */
+ II(a, b, c, d, x[ 8], 6, 0x6fa87e4f); /* 57 */
+ II(d, a, b, c, x[15], 10, 0xfe2ce6e0); /* 58 */
+ II(c, d, a, b, x[ 6], 15, 0xa3014314); /* 59 */
+ II(b, c, d, a, x[13], 21, 0x4e0811a1); /* 60 */
+ II(a, b, c, d, x[ 4], 6, 0xf7537e82); /* 61 */
+ II(d, a, b, c, x[11], 10, 0xbd3af235); /* 62 */
+ II(c, d, a, b, x[ 2], 15, 0x2ad7d2bb); /* 63 */
+ II(b, c, d, a, x[ 9], 21, 0xeb86d391); /* 64 */
+ state[0] += a;
+ state[1] += b;
+ state[2] += c;
+ state[3] += d;
+}
diff --git a/drivers/net/wireless/aic8800/aic_load_fw/md5.h b/drivers/net/wireless/aic8800/aic_load_fw/md5.h
new file mode 100644
index 000000000000..6ed5c0f8e886
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic_load_fw/md5.h
@@ -0,0 +1,48 @@
+#ifndef MD5_H
+#define MD5_H
+
+typedef struct
+{
+ unsigned int count[2];
+ unsigned int state[4];
+ unsigned char buffer[64];
+}MD5_CTX;
+
+
+#define F(x,y,z) ((x & y) | (~x & z))
+#define G(x,y,z) ((x & z) | (y & ~z))
+#define H(x,y,z) (x^y^z)
+#define I(x,y,z) (y ^ (x | ~z))
+#define ROTATE_LEFT(x,n) ((x << n) | (x >> (32-n)))
+#define FF(a,b,c,d,x,s,ac) \
+ { \
+ a += F(b,c,d) + x + ac; \
+ a = ROTATE_LEFT(a,s); \
+ a += b; \
+ }
+#define GG(a,b,c,d,x,s,ac) \
+ { \
+ a += G(b,c,d) + x + ac; \
+ a = ROTATE_LEFT(a,s); \
+ a += b; \
+ }
+#define HH(a,b,c,d,x,s,ac) \
+ { \
+ a += H(b,c,d) + x + ac; \
+ a = ROTATE_LEFT(a,s); \
+ a += b; \
+ }
+#define II(a,b,c,d,x,s,ac) \
+ { \
+ a += I(b,c,d) + x + ac; \
+ a = ROTATE_LEFT(a,s); \
+ a += b; \
+ }
+void MD5Init(MD5_CTX *context);
+void MD5Update(MD5_CTX *context,unsigned char *input,unsigned int inputlen);
+void MD5Final(MD5_CTX *context,unsigned char digest[16]);
+void MD5Transform(unsigned int state[4],unsigned char block[64]);
+void MD5Encode(unsigned char *output,unsigned int *input,unsigned int len);
+void MD5Decode(unsigned int *output,unsigned char *input,unsigned int len);
+
+#endif
diff --git a/drivers/net/wireless/aic8800/aic_load_fw/rwnx_version_gen.h b/drivers/net/wireless/aic8800/aic_load_fw/rwnx_version_gen.h
new file mode 100644
index 000000000000..09010086a0d2
--- /dev/null
+++ b/drivers/net/wireless/aic8800/aic_load_fw/rwnx_version_gen.h
@@ -0,0 +1,4 @@
+#define RWNX_VERS_REV "1a4b0054d2M (master)"
+#define RWNX_VERS_MOD "6.4.3.0"
+#define RWNX_VERS_BANNER "rwnx v6.4.3.0 - 1a4b0054d2M (master)"
+#define RELEASE_DATE "2023_0707_1001"
diff --git a/drivers/net/wireless/eswin/Kconfig b/drivers/net/wireless/eswin/Kconfig
new file mode 100644
index 000000000000..94d445ed420e
--- /dev/null
+++ b/drivers/net/wireless/eswin/Kconfig
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config USB_WIFI_ECR6600U
+ bool "USB WIFI ECR6600U"
+ help
+ usb wifi ECR6600U.
+
diff --git a/drivers/net/wireless/eswin/Makefile b/drivers/net/wireless/eswin/Makefile
new file mode 100644
index 000000000000..26959b97291c
--- /dev/null
+++ b/drivers/net/wireless/eswin/Makefile
@@ -0,0 +1,145 @@
+ECRNX_VERS_NUM=ECR6600U_V1.1.0B04P05
+
+# config_ceva_rtos = y use ceva rtos and add task_cli id
+# config_ceva_rtos = n use freertos and no task_cli id
+
+#export DRIVER_PATH ?= $(shell pwd)
+export DRIVER_PATH = $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
+DRIVER_PATH := $(DRIVER_PATH:/=)
+
+#if build the host driver within linux kernel, the DRIVER_PATH should be defined to absolutly path
+export product=6600u
+ifeq ($(product), 6600u)
+ -include $(DRIVER_PATH)/feature_config/6600u_config
+else
+ -include $(DRIVER_PATH)/feature_config/6600_config
+ ifeq ($(os), )
+ export CONFIG_CEVA_RTOS=y
+ else
+ export CONFIG_CEVA_RTOS=n
+ endif
+endif
+
+###################### Platform Related #######################
+CONFIG_PLATFORM_RTK_RTD2851D = n
+CONFIG_PLATFORM_MTK_MT9255 = n
+CONFIG_PLATFORM_RASPBERRY = y
+CONFIG_PLATFORM_X86 = n
+CONFIG_PLATFORM_AML_T963 = n
+###############################################################
+
+ifeq ($(CONFIG_PLATFORM_RTK_RTD2851D), y)
+export KERNELDIR=/work3/zhanghong/realtek/rtk10/vendor/realtek/tool/kernel/linux/linux-4.14/
+export KBUILDDIR=/work3/zhanghong/realtek/rtk10/vendor/realtek/tool/kernel/linux/linux-4.14/
+endif
+
+ifeq ($(CONFIG_PLATFORM_MTK_MT9255), y)
+export KERNELDIR= $(DRIVER_PATH)/../../../../../../kernel/fusion/4.9
+export KBUILDDIR= $(DRIVER_PATH)/../../../../../../kernel/fusion/4.9
+export CROSS_COMPILE:=$(DRIVER_PATH)/../../../../../../prebuilts/mtk_toolchain/gcc-arm-linux-gnu-5.5.0-ubuntu/x86_64/bin/arm-linux-gnueabi-
+endif
+
+ifeq ($(CONFIG_PLATFORM_RASPBERRY), y)
+export KERNELDIR=/lib/modules/$(shell uname -r)/build
+export KBUILDDIR=/lib/modules/$(shell uname -r)/build
+endif
+
+ifeq ($(CONFIG_PLATFORM_X86), y)
+export KERNELDIR=/lib/modules/$(shell uname -r)/build
+export KBUILDDIR=/lib/modules/$(shell uname -r)/build
+endif
+
+ifeq ($(CONFIG_PLATFORM_AML_T963), y)
+ifeq ($(DRIVER_DIR), )
+export DRIVER_PATH ?= $(shell pwd)
+else
+export DRIVER_PATH ?= $(DRIVER_DIR)
+endif
+export KERNELDIR ?= $(DRIVER_PATH)/../../../../../../out/target/product/T963/obj/KERNEL_OBJ
+export KBUILDDIR ?= $(DRIVER_PATH)/../../../../../../out/target/product/T963/obj/KERNEL_OBJ
+CROSS_COMPILE ?= /opt/gcc-linaro-6.3.1-2017.02-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf-
+ARCH := arm
+endif
+#
+# } // WAITING FOR KCONFIG
+#
+
+subdir-ccflags-$(CONFIG_6600_HAL) += -DCONFIG_6600_HAL
+subdir-ccflags-$(CONFIG_DEBUG_FS) += -DCONFIG_ECRNX_DEBUGFS
+subdir-ccflags-$(CONFIG_DEBUG_FS) += -DCONFIG_ECRNX_UM_HELPER_DFLT=\"$(CONFIG_ECRNX_UM_HELPER_DFLT)\"
+subdir-ccflags-$(CONFIG_ECRNX_P2P_DEBUGFS) += -DCONFIG_ECRNX_P2P_DEBUGFS
+subdir-ccflags-$(CONFIG_CEVA_RTOS) += -DCONFIG_CEVA_RTOS
+subdir-ccflags-$(CONFIG_ECRNX_DBG) += -DCONFIG_ECRNX_DBG
+subdir-ccflags-$(CONFIG_ECRNX_KTHREAD) += -DCONFIG_ECRNX_KTHREAD
+subdir-ccflags-$(CONFIG_ECRNX_WORKQUEUE) += -DCONFIG_ECRNX_WORKQUEUE
+subdir-ccflags-$(CONFIG_ECRNX_TASKLET) += -DCONFIG_ECRNX_TASKLET
+subdir-ccflags-y += -DCONFIG_ECRNX_DBG_LEVEL=$(CONFIG_ECRNX_DBG_LEVEL)
+
+# FW VARS
+subdir-ccflags-y += -DNX_VIRT_DEV_MAX=$(NX_VIRT_DEV_MAX)
+subdir-ccflags-y += -DNX_REMOTE_STA_MAX=$(NX_REMOTE_STA_MAX)
+subdir-ccflags-y += -DNX_MU_GROUP_MAX=$(NX_MU_GROUP_MAX)
+subdir-ccflags-y += -DNX_TXDESC_CNT=$(NX_TXDESC_CNT)
+subdir-ccflags-y += -DNX_TX_MAX_RATES=$(NX_TX_MAX_RATES)
+subdir-ccflags-y += -DNX_CHAN_CTXT_CNT=$(NX_CHAN_CTXT_CNT)
+#subdir-ccflags-y += -DCONFIG_POWERKEY_GPIO=$(CONFIG_POWERKEY_GPIO)
+# FW ARCH:
+subdir-ccflags-$(CONFIG_ECRNX_SDM) += -DCONFIG_ECRNX_SDM
+subdir-ccflags-$(CONFIG_ECRNX_TL4) += -DCONFIG_ECRNX_TL4
+subdir-ccflags-$(CONFIG_ECRNX_OLD_IPC) += -DCONFIG_ECRNX_OLD_IPC
+
+#FW VER INFO
+DRIVER_BUILD_TIME = "$(shell TZ=CST date -u "+%Y-%m-%d %H:%M:%S CST")"
+subdir-ccflags-y += -DECRNX_VERS_MOD=\"$(ECRNX_VERS_NUM)\"
+subdir-ccflags-y += -DECRNX_VERS_BANNER=\"$(ECRNX_MODULE_NAME)-$(ECRNX_VERS_NUM)-build\:$(DRIVER_BUILD_TIME)\"
+
+ifeq ($(CONFIG_ECRNX_FULLMAC), m)
+MAC_SRC = fullmac
+else ifeq ($(CONFIG_ECRNX_SOFTMAC), m)
+MAC_SRC = softmac
+endif
+
+
+obj-m := $(ECRNX_MODULE_NAME).o
+
+ifneq ($(KERNELRELEASE),)
+include $(DRIVER_PATH)/$(MAC_SRC)/Makefile
+
+else
+all: modules
+
+.PHONY: modules clean copy strip
+
+modules:
+ifeq ($(product), 6600u)
+ $(warning "select chip is $(product).")
+else
+ $(warning "select chip is 6600.")
+endif
+ rm -rf *.ko
+ifeq ($(os), )
+ $(warning "select slave is used CEVA RTOS.")
+else
+ $(warning "select slave is not used CEVA_RTOS.")
+endif
+ifeq ($(CONFIG_PLATFORM_MTK_MT9255), y)
+ $(MAKE) -C $(KERNELDIR) CROSS_COMPILE=$(CROSS_COMPILE) O=$(KBUILDDIR) M=$(DRIVER_PATH) $@
+else ifeq ($(CONFIG_PLATFORM_AML_T963), y)
+ $(MAKE) -C $(KERNELDIR) CROSS_COMPILE=$(CROSS_COMPILE) ARCH=$(ARCH) O=$(KBUILDDIR) M=$(DRIVER_PATH) $@
+else
+ $(MAKE) -C $(KERNELDIR) O=$(KBUILDDIR) M=$(DRIVER_PATH) $@
+endif
+ rm -rf *.o
+ rm -rf *.mod *.mod.c
+
+copy:
+ cp -f $(DRIVER_PATH)/fullmac/$(ECRNX_MODULE_NAME).ko $(MODDESTDIR)
+
+strip:
+ $(CROSS_COMPILE)strip --strip-unneeded $(ECRNX_MODULE_NAME).ko
+clean:
+ rm -rf *.o
+ rm -rf *.ko *.mod *.mod.c
+ $(MAKE) -C $(KERNELDIR) O=$(KBUILDDIR) M=$(DRIVER_PATH) $@
+# @$(DRIVER_PATH)/mklink.sh clean
+endif
diff --git a/drivers/net/wireless/eswin/README.md b/drivers/net/wireless/eswin/README.md
new file mode 100644
index 000000000000..745e42481f8d
--- /dev/null
+++ b/drivers/net/wireless/eswin/README.md
@@ -0,0 +1,119 @@
+## **Wifi host driver编译指å—**
+
+## Wifi host driver框架
+
+Wifi host driver文件目录如下所示;
+
+- ├── ble_netconfig
+- ├── compile_test.sh
+- ├── ecrnx_bfmer.c
+- ├── ecrnx_bfmer.h
+- ├── ecrnx_cfgfile.c
+- ├── ecrnx_cfgfile.h
+- ├── ecrnx_cmds.c
+- ├── ecrnx_cmds.h
+- ├── ecrnx_compat.h
+- ├── ecrnx_debugfs.c
+- ├── ecrnx_debugfs.h
+- ├── ecrnx_events.h
+- ├── ecrnx_fw_dump.c
+- ├── ecrnx_fw_trace.c
+- ├── ecrnx_fw_trace.h
+- ├── ecrnx_iwpriv.c
+- ├── ecrnx_mod_params.c
+- ├── ecrnx_mod_params.h
+- ├── ecrnx_msg_rx.c
+- ├── ecrnx_msg_rx.h
+- ├── ecrnx_msg_tx.c
+- ├── ecrnx_msg_tx.h
+- ├── ecrnx_mu_group.c
+- ├── ecrnx_mu_group.h
+- ├── ecrnx_platform.c
+- ├── ecrnx_platform.h
+- ├── ecrnx_prof.h
+- ├── ecrnx_radar.c
+- ├── ecrnx_radar.h
+- ├── ecrnx_strs.c
+- ├── ecrnx_strs.h
+- ├── ecrnx_testmode.c
+- ├── ecrnx_testmode.h
+- ├── ecrnx_txq.c
+- ├── ecrnx_txq.h
+- ├── ecrnx_utils.c
+- ├── ecrnx_utils.h
+- ├── ecrnx_version.h
+- ├── eswin_port
+- ├── feature_config
+- ├── fullmac
+- ├── fw_head_check.c
+- ├── fw_head_check.h
+- ├── hal_desc.c
+- ├── hal_desc.h
+- ├── ipc_compat.h
+- ├── ipc_host.c
+- ├── ipc_host.h
+- ├── ipc_shared.h
+- ├── lmac_mac.h
+- ├── lmac_msg.h
+- ├── lmac_types.h
+- ├── Makefile
+- ├── reg_access.h
+- ├── reg_ipc_app.h
+- ├── sdio
+- ├── softmac
+- └── usb
+
+
+Wifi host driver 主è¦ç›®å½•ç»“构说明如下表所示;
+
+| **文件路径** | **说明** |
+| ------------ | :-------------------------------- |
+| eswin_port | Host Driver相关的适é…æŽ¥å£ |
+| fullmac | fullmac模å¼ä¸‹ç›¸å…³çš„é…置文件和æºç  |
+| softmac | softmac模å¼ä¸‹ç›¸å…³çš„é…置文件和æºç  |
+| sdio | sdio相关的驱动文件 |
+| usb | usb相关的驱动文件 |
+
+## **Wifi host driver 编译å‚数说明**
+
+​ 6600U host driver ç›®å‰æ—¶æœ‰ä¸¤ä¸ªå‚æ•°å¯é€‰ï¼Œæ述如下:
+
+​ product:product å‚数分为 6600 å’Œ 6600u,用æ¥åŒºåˆ†èŠ¯ç‰‡å¹³å°ï¼Œé»˜è®¤ä¸º 6600ï¼›
+
+​ os:os å‚数为 ceva,æ„æ€ä¸ºæ˜¯å¦ä½¿ç”¨ ceva çš„ os,该å‚æ•°åªé’ˆå¯¹ 6600 有效,6600u ä¸éœ€è¦ os å‚数;默认为ä¸ä½¿ç”¨ ceva osï¼›
+
+## **6600(SDIO)é€ä¼ ç‰ˆæœ¬ç¼–译指令**
+
+​ slave 使用 6600 iot 仓库的 6600 é€ä¼ ç‰ˆæœ¬æ—¶ï¼Œhost driver 编译命令如下:
+
+​ sudo make os=true(或者 sudo make product=6600 os=true)
+
+​ slave 使用 6600U åˆä»“仓库的 develop 分支时,host driver 的编译命令如下:
+
+​ sudo make(或者 sudo make product=6600)
+
+​ sdio 版本 host driver ko 加载时,如果需è¦ä¸‹è½½å›ºä»¶çš„è¯ï¼Œåˆ™è¦å¸¦ä¸‹è½½å›ºä»¶å’Œå›ºä»¶å称的å‚数(默认ä¸ä¸‹è½½å›ºä»¶ï¼‰ï¼Œ 如下所示:
+
+​ sudo insmod wlan_ecr6600.ko dl_fw=1 fw_name="transport.bin"
+
+
+
+## **6600U(USB)é€ä¼ ç‰ˆæœ¬ç¼–译指令**
+
+​ 6600U slave 侧统一使用 6600U 仓库下的 6600U é€ä¼ ç‰ˆæœ¬ï¼Œhost driver 编译命令如下:
+
+​ sudo make product=6600u (ä¸éœ€è¦å¸¦ os å‚数,6600U 默认带 CEVA_OS)
+
+​ 6600U host ko 加载命令如下所示:
+
+​ sudo insmod wlan_ecr6600u_usb.ko(6600U host ko 加载时默认会下载固件,默认加载固件å称为:ECR6600U_transport.bin,如果ä¸éœ€è¦å›ºä»¶ä¸‹è½½åˆ™ä½¿ç”¨ dl_fw=0, 如果è¦è‡ªå®šä¹‰ä¸‹è½½å›ºä»¶å称则使用:fw_name="filename.bin" );
+
+## cfg文件使用说明
+
+å°† wifi_ecr6600u.cfg æ‹·è´åˆ° /lib/firmware 路径下,修改 cfg 文件里的å‚数值,å³å¯ä¿®æ”¹ host å’Œ slave 的相关å‚数,修改完æˆåŽé‡æ–°å¸è½½ã€åŠ è½½ ko å³å¯ç”Ÿæ•ˆï¼›ç›®å‰å¯æ”¯æŒçš„å‚数如下:
+
+DRIVER_LOG_LEVEL=3 //host driver log 等级,å–值范围为 0-5
+
+FW_LOG_LEVEL=2 //slave log 等级,å–值范围为 0-4
+
+FW_LOG_TYPE=0 // slave log 输出方å¼ï¼Œ 0 为通过 slave 端串å£è¾“出;1 为通过 host debugfs ä¿å­˜ï¼› 2 为通过 host 侧文件ä¿å­˜ï¼› \ No newline at end of file
diff --git a/drivers/net/wireless/eswin/ble_netconfig/btgatt-server.c b/drivers/net/wireless/eswin/ble_netconfig/btgatt-server.c
new file mode 100644
index 000000000000..c986ee7add05
--- /dev/null
+++ b/drivers/net/wireless/eswin/ble_netconfig/btgatt-server.c
@@ -0,0 +1,1685 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2014 Google Inc.
+ *
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <time.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/hci_lib.h"
+#include "lib/l2cap.h"
+#include "lib/uuid.h"
+
+#include "src/shared/mainloop.h"
+#include "src/shared/util.h"
+#include "src/shared/att.h"
+#include "src/shared/queue.h"
+#include "src/shared/timeout.h"
+#include "src/shared/gatt-db.h"
+#include "src/shared/gatt-server.h"
+
+#define UUID_GAP 0x1800
+#define UUID_GATT 0x1801
+#define UUID_HEART_RATE 0x180d
+#define UUID_HEART_RATE_MSRMT 0x2a37
+#define UUID_HEART_RATE_BODY 0x2a38
+#define UUID_HEART_RATE_CTRL 0x2a39
+
+#define ESWIN_TEST 1
+
+#ifdef ESWIN_TEST
+#define UUID_NETCFG 0x1920
+#define UUID_SSID 0x2b10
+#define UUID_PASSWORD 0x2b11
+#define UUID_NETSTATUS 0x2b12
+#define CFG_ITEM_LEN 40
+#define CFG_FLAG_SSID 0x01
+#define CFG_FLAG_PWD 0x02
+#define CFG_FLAG_ALL 0x03
+#endif
+
+#define ATT_CID 4
+
+#define PRLOG(...) \
+ do { \
+ printf(__VA_ARGS__); \
+ print_prompt(); \
+ } while (0)
+
+#ifndef MIN
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#endif
+
+#define COLOR_OFF "\x1B[0m"
+#define COLOR_RED "\x1B[0;91m"
+#define COLOR_GREEN "\x1B[0;92m"
+#define COLOR_YELLOW "\x1B[0;93m"
+#define COLOR_BLUE "\x1B[0;94m"
+#define COLOR_MAGENTA "\x1B[0;95m"
+#define COLOR_BOLDGRAY "\x1B[1;30m"
+#define COLOR_BOLDWHITE "\x1B[1;37m"
+
+//static const char test_device_name[] = "Very Long Test Device Name For Testing "
+// "ATT Protocol Operations On GATT Server";
+static const char test_device_name[] = "BlueZTest";
+
+static bool verbose = false;
+
+#ifdef ESWIN_TEST
+static uint8_t netcfg_ssid[CFG_ITEM_LEN] = "";
+static uint8_t netcfg_pwd[CFG_ITEM_LEN] = "";
+static uint8_t net_status[CFG_ITEM_LEN] = "";
+static uint8_t ssid_len = 0;
+static uint8_t pwd_len = 0;
+static uint8_t netcfg_flag = 0;
+#endif
+
+
+struct server {
+ int fd;
+ struct bt_att *att;
+ struct gatt_db *db;
+ struct bt_gatt_server *gatt;
+
+ uint8_t *device_name;
+ size_t name_len;
+
+ uint16_t gatt_svc_chngd_handle;
+ bool svc_chngd_enabled;
+
+ uint16_t hr_handle;
+ uint16_t hr_msrmt_handle;
+ uint16_t hr_energy_expended;
+ bool hr_visible;
+ bool hr_msrmt_enabled;
+ int hr_ee_count;
+ unsigned int hr_timeout_id;
+#ifdef ESWIN_TEST
+ uint16_t netstatus_handle;
+ bool netstatus_enabled;
+ unsigned int netstatus_timeout_id;
+#endif
+};
+
+static void print_prompt(void)
+{
+ printf(COLOR_BLUE "[GATT server]" COLOR_OFF "# ");
+ fflush(stdout);
+}
+
+static void att_disconnect_cb(int err, void *user_data)
+{
+ printf("Device disconnected: %s\n", strerror(err));
+
+ mainloop_quit();
+}
+
+static void att_debug_cb(const char *str, void *user_data)
+{
+ const char *prefix = user_data;
+
+ PRLOG(COLOR_BOLDGRAY "%s" COLOR_BOLDWHITE "%s\n" COLOR_OFF, prefix,
+ str);
+}
+
+static void gatt_debug_cb(const char *str, void *user_data)
+{
+ const char *prefix = user_data;
+
+ PRLOG(COLOR_GREEN "%s%s\n" COLOR_OFF, prefix, str);
+}
+
+static void gap_device_name_read_cb(struct gatt_db_attribute *attrib,
+ unsigned int id, uint16_t offset,
+ uint8_t opcode, struct bt_att *att,
+ void *user_data)
+{
+ struct server *server = user_data;
+ uint8_t error = 0;
+ size_t len = 0;
+ const uint8_t *value = NULL;
+
+ PRLOG("GAP Device Name Read called\n");
+
+ len = server->name_len;
+
+ if (offset > len) {
+ error = BT_ATT_ERROR_INVALID_OFFSET;
+ goto done;
+ }
+
+ len -= offset;
+ value = len ? &server->device_name[offset] : NULL;
+
+done:
+ gatt_db_attribute_read_result(attrib, id, error, value, len);
+}
+
+static void gap_device_name_write_cb(struct gatt_db_attribute *attrib,
+ unsigned int id, uint16_t offset,
+ const uint8_t *value, size_t len,
+ uint8_t opcode, struct bt_att *att,
+ void *user_data)
+{
+ struct server *server = user_data;
+ uint8_t error = 0;
+
+ PRLOG("GAP Device Name Write called\n");
+
+ /* If the value is being completely truncated, clean up and return */
+ if (!(offset + len)) {
+ free(server->device_name);
+ server->device_name = NULL;
+ server->name_len = 0;
+ goto done;
+ }
+
+ /* Implement this as a variable length attribute value. */
+ if (offset > server->name_len) {
+ error = BT_ATT_ERROR_INVALID_OFFSET;
+ goto done;
+ }
+
+ if (offset + len != server->name_len) {
+ uint8_t *name;
+
+ name = realloc(server->device_name, offset + len);
+ if (!name) {
+ error = BT_ATT_ERROR_INSUFFICIENT_RESOURCES;
+ goto done;
+ }
+
+ server->device_name = name;
+ server->name_len = offset + len;
+ }
+
+ if (value)
+ memcpy(server->device_name + offset, value, len);
+
+done:
+ gatt_db_attribute_write_result(attrib, id, error);
+}
+
+static void gap_device_name_ext_prop_read_cb(struct gatt_db_attribute *attrib,
+ unsigned int id, uint16_t offset,
+ uint8_t opcode, struct bt_att *att,
+ void *user_data)
+{
+ uint8_t value[2];
+
+ PRLOG("Device Name Extended Properties Read called\n");
+
+ value[0] = BT_GATT_CHRC_EXT_PROP_RELIABLE_WRITE;
+ value[1] = 0;
+
+ gatt_db_attribute_read_result(attrib, id, 0, value, sizeof(value));
+}
+
+static void gatt_service_changed_cb(struct gatt_db_attribute *attrib,
+ unsigned int id, uint16_t offset,
+ uint8_t opcode, struct bt_att *att,
+ void *user_data)
+{
+ PRLOG("Service Changed Read called\n");
+
+ gatt_db_attribute_read_result(attrib, id, 0, NULL, 0);
+}
+
+static void gatt_svc_chngd_ccc_read_cb(struct gatt_db_attribute *attrib,
+ unsigned int id, uint16_t offset,
+ uint8_t opcode, struct bt_att *att,
+ void *user_data)
+{
+ struct server *server = user_data;
+ uint8_t value[2];
+
+ PRLOG("Service Changed CCC Read called\n");
+
+ value[0] = server->svc_chngd_enabled ? 0x02 : 0x00;
+ value[1] = 0x00;
+
+ gatt_db_attribute_read_result(attrib, id, 0, value, sizeof(value));
+}
+
+static void gatt_svc_chngd_ccc_write_cb(struct gatt_db_attribute *attrib,
+ unsigned int id, uint16_t offset,
+ const uint8_t *value, size_t len,
+ uint8_t opcode, struct bt_att *att,
+ void *user_data)
+{
+ struct server *server = user_data;
+ uint8_t ecode = 0;
+
+ PRLOG("Service Changed CCC Write called\n");
+
+ if (!value || len != 2) {
+ ecode = BT_ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LEN;
+ goto done;
+ }
+
+ if (offset) {
+ ecode = BT_ATT_ERROR_INVALID_OFFSET;
+ goto done;
+ }
+
+ if (value[0] == 0x00)
+ server->svc_chngd_enabled = false;
+ else if (value[0] == 0x02)
+ server->svc_chngd_enabled = true;
+ else
+ ecode = 0x80;
+
+ PRLOG("Service Changed Enabled: %s\n",
+ server->svc_chngd_enabled ? "true" : "false");
+
+done:
+ gatt_db_attribute_write_result(attrib, id, ecode);
+}
+
+static void hr_msrmt_ccc_read_cb(struct gatt_db_attribute *attrib,
+ unsigned int id, uint16_t offset,
+ uint8_t opcode, struct bt_att *att,
+ void *user_data)
+{
+ struct server *server = user_data;
+ uint8_t value[2];
+
+ value[0] = server->hr_msrmt_enabled ? 0x01 : 0x00;
+ value[1] = 0x00;
+
+ gatt_db_attribute_read_result(attrib, id, 0, value, 2);
+}
+
+static bool hr_msrmt_cb(void *user_data)
+{
+ struct server *server = user_data;
+ bool expended_present = !(server->hr_ee_count % 10);
+ uint16_t len = 2;
+ uint8_t pdu[4];
+ uint32_t cur_ee;
+
+ pdu[0] = 0x06;
+ pdu[1] = 90 + (rand() % 40);
+
+ if (expended_present) {
+ pdu[0] |= 0x08;
+ put_le16(server->hr_energy_expended, pdu + 2);
+ len += 2;
+ }
+
+ bt_gatt_server_send_notification(server->gatt,
+ server->hr_msrmt_handle,
+ pdu, len, false);
+
+
+ cur_ee = server->hr_energy_expended;
+ server->hr_energy_expended = MIN(UINT16_MAX, cur_ee + 10);
+ server->hr_ee_count++;
+
+ return true;
+}
+
+static void update_hr_msrmt_simulation(struct server *server)
+{
+ if (!server->hr_msrmt_enabled || !server->hr_visible) {
+ timeout_remove(server->hr_timeout_id);
+ return;
+ }
+
+ server->hr_timeout_id = timeout_add(1000, hr_msrmt_cb, server, NULL);
+}
+
+static void hr_msrmt_ccc_write_cb(struct gatt_db_attribute *attrib,
+ unsigned int id, uint16_t offset,
+ const uint8_t *value, size_t len,
+ uint8_t opcode, struct bt_att *att,
+ void *user_data)
+{
+ struct server *server = user_data;
+ uint8_t ecode = 0;
+
+ if (!value || len != 2) {
+ ecode = BT_ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LEN;
+ goto done;
+ }
+
+ if (offset) {
+ ecode = BT_ATT_ERROR_INVALID_OFFSET;
+ goto done;
+ }
+
+ if (value[0] == 0x00)
+ server->hr_msrmt_enabled = false;
+ else if (value[0] == 0x01) {
+ if (server->hr_msrmt_enabled) {
+ PRLOG("HR Measurement Already Enabled\n");
+ goto done;
+ }
+
+ server->hr_msrmt_enabled = true;
+ } else
+ ecode = 0x80;
+
+ PRLOG("HR: Measurement Enabled: %s\n",
+ server->hr_msrmt_enabled ? "true" : "false");
+
+ update_hr_msrmt_simulation(server);
+
+done:
+ gatt_db_attribute_write_result(attrib, id, ecode);
+}
+
+static void hr_control_point_write_cb(struct gatt_db_attribute *attrib,
+ unsigned int id, uint16_t offset,
+ const uint8_t *value, size_t len,
+ uint8_t opcode, struct bt_att *att,
+ void *user_data)
+{
+ struct server *server = user_data;
+ uint8_t ecode = 0;
+
+ if (!value || len != 1) {
+ ecode = BT_ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LEN;
+ goto done;
+ }
+
+ if (offset) {
+ ecode = BT_ATT_ERROR_INVALID_OFFSET;
+ goto done;
+ }
+
+ if (value[0] == 1) {
+ PRLOG("HR: Energy Expended value reset\n");
+ server->hr_energy_expended = 0;
+ }
+
+done:
+ gatt_db_attribute_write_result(attrib, id, ecode);
+}
+
+static void confirm_write(struct gatt_db_attribute *attr, int err,
+ void *user_data)
+{
+ if (!err)
+ return;
+
+ fprintf(stderr, "Error caching attribute %p - err: %d\n", attr, err);
+ exit(1);
+}
+
+#ifdef ESWIN_TEST
+static void modify_wpa_conf(void)
+{
+ FILE *fp;
+ char ssid_str[2*CFG_ITEM_LEN]={0};
+ char pwd_str[2*CFG_ITEM_LEN]={0};
+ PRLOG("enter\n");
+
+ fp = fopen("/etc/wpa_supplicant/wpa_supplicant.conf", "we");
+ if (!fp){
+ PRLOG("fopen fail\n");
+ return;
+ }
+ snprintf(ssid_str, sizeof(ssid_str), "ssid=\"%s\"\n", netcfg_ssid);
+ snprintf(pwd_str, sizeof(pwd_str), "psk=\"%s\"\n", netcfg_pwd);
+
+ fputs("ctrl_interface=DIR=/var/run/wpa_supplicant\n",fp);
+ fputs("update_config=1\n", fp);
+ fputs("network={\n", fp);
+ fputs(ssid_str, fp);
+ fputs(pwd_str, fp);
+ fputs("}\n", fp);
+
+
+ fclose(fp);
+ PRLOG("exit\n");
+}
+
+static int wifi_run_cmd(char *cmd)
+{
+ int ret = 0;
+ ret = system(cmd);
+ if(ret < 0) {
+ PRLOG("cmd=%s\t error:%s\n",cmd, strerror(errno));
+ return -1;
+ }
+ if(WIFEXITED(ret)){
+
+ PRLOG("success,cmd=%s,status=%d\n",cmd,WEXITSTATUS(ret));
+ return WEXITSTATUS(ret);
+ }
+
+ return -1;
+
+}
+static void update_wpa_network(void)
+{
+ char sys_cmd[100] = {0};
+ char temp[CFG_ITEM_LEN] = {0};
+ int result = -1;
+
+ sprintf(sys_cmd, "cd /wpa_supplicant-2.10/wpa_supplicant");
+ result = wifi_run_cmd(sys_cmd);
+ if(result < 0){
+ return;
+ }
+
+ sprintf(sys_cmd, "sudo killall wpa_supplicant");
+ result = wifi_run_cmd(sys_cmd);
+ if(result < 0){
+ return;
+ }
+
+ sprintf(sys_cmd, "sudo /wpa_supplicant-2.10/wpa_supplicant/wpa_supplicant -B -c/etc/wpa_supplicant/wpa_supplicant.conf -iwlan0 -Dnl80211,wext");
+ result = wifi_run_cmd(sys_cmd);
+ if(result < 0){
+ return;
+ }
+
+
+ sprintf(sys_cmd, "sudo /wpa_supplicant-2.10/wpa_supplicant/wpa_cli -i wlan0 remove_network all");
+ result = wifi_run_cmd(sys_cmd);
+ if(result < 0){
+ return;
+ }
+ memset(sys_cmd, 0x00, 100);
+ sprintf(sys_cmd, "sudo /wpa_supplicant-2.10/wpa_supplicant/wpa_cli -i wlan0 add_network");
+ result = wifi_run_cmd(sys_cmd);
+ if(result < 0){
+ return;
+ }
+ memset(sys_cmd, 0x00, 100);
+ sprintf(sys_cmd, "sudo /wpa_supplicant-2.10/wpa_supplicant/wpa_cli -i wlan0 set_network 0 ssid ");
+ snprintf(temp, sizeof(temp),"'\"%s\"'", netcfg_ssid);
+ strcat(sys_cmd,temp);
+ result = wifi_run_cmd(sys_cmd);
+ if(result < 0){
+ return;
+ }
+
+ memset(sys_cmd, 0x00, 100);
+ memset(temp, 0x00, CFG_ITEM_LEN);
+ sprintf(sys_cmd, "sudo /wpa_supplicant-2.10/wpa_supplicant/wpa_cli -i wlan0 set_network 0 psk ");
+ snprintf(temp, sizeof(temp),"'\"%s\"'", netcfg_pwd);
+ strcat(sys_cmd,temp);
+ result = wifi_run_cmd(sys_cmd);
+ if(result < 0){
+ return;
+ }
+
+ memset(sys_cmd, 0x00, 100);
+ sprintf(sys_cmd, "sudo /wpa_supplicant-2.10/wpa_supplicant/wpa_cli -i wlan0 save_config");
+ result = wifi_run_cmd(sys_cmd);
+ if(result < 0){
+ return;
+ }
+
+ memset(sys_cmd, 0x00, 100);
+ sprintf(sys_cmd, "sudo /wpa_supplicant-2.10/wpa_supplicant/wpa_cli -i wlan0 select_network 0");
+ result = wifi_run_cmd(sys_cmd);
+ if(result < 0){
+ return;
+ }
+
+ PRLOG("update_wpa_network finish\n");
+}
+
+static void eswin_netcfg_wpa_conf(int flag)
+{
+ netcfg_flag |= flag;
+
+ if(netcfg_flag == CFG_FLAG_ALL){
+ netcfg_flag = 0;
+ //modify_wpa_conf();
+ update_wpa_network();
+ }
+}
+
+
+
+
+static void eswin_netcfg_ssid_read_cb(struct gatt_db_attribute *attrib,
+ unsigned int id, uint16_t offset,
+ uint8_t opcode, struct bt_att *att,
+ void *user_data)
+{
+ struct server *server = user_data;
+ uint8_t error = 0;
+ size_t len = 0;
+ const uint8_t *value = NULL;
+
+ //PRLOG("ESWIN SSID Read called ssid_len=%d,offset=%d\n",ssid_len,offset);
+
+ if (offset > ssid_len) {
+ error = BT_ATT_ERROR_INVALID_OFFSET;
+ goto done;
+ }
+
+ len = ssid_len - offset;
+ value = len ? &netcfg_ssid[offset] : NULL;
+
+done:
+ gatt_db_attribute_read_result(attrib, id, error, value, len);
+}
+
+
+static void eswin_netcfg_ssid_write_cb(struct gatt_db_attribute *attrib,
+ unsigned int id, uint16_t offset,
+ const uint8_t *value, size_t len,
+ uint8_t opcode, struct bt_att *att,
+ void *user_data)
+{
+ struct server *server = user_data;
+ uint8_t error = 0;
+
+ //PRLOG("ESWIN SSID write called offset=%d len=%d\n",offset, len);
+
+ memset(netcfg_ssid, 0x00, CFG_ITEM_LEN);
+ ssid_len = 0;
+
+ if (offset+len >= CFG_ITEM_LEN) {
+ error = BT_ATT_ERROR_INVALID_OFFSET;
+ goto done;
+ }
+
+ if (value)
+ memcpy(netcfg_ssid + offset, value, len);
+
+ ssid_len +=len;
+ eswin_netcfg_wpa_conf(CFG_FLAG_SSID);
+done:
+ gatt_db_attribute_write_result(attrib, id, error);
+
+}
+
+
+static void eswin_netcfg_pwd_read_cb(struct gatt_db_attribute *attrib,
+ unsigned int id, uint16_t offset,
+ uint8_t opcode, struct bt_att *att,
+ void *user_data)
+{
+ struct server *server = user_data;
+ uint8_t error = 0;
+ size_t len = 0;
+ const uint8_t *value = NULL;
+
+ //PRLOG("ESWIN pwd Read called pwd_len=%d,offset=%d\n",pwd_len,offset);
+
+ if (offset > pwd_len) {
+ error = BT_ATT_ERROR_INVALID_OFFSET;
+ goto done;
+ }
+
+ len = pwd_len - offset;
+ value = len ? &netcfg_pwd[offset] : NULL;
+
+done:
+ gatt_db_attribute_read_result(attrib, id, error, value, len);
+}
+
+
+static void eswin_netcfg_pwd_write_cb(struct gatt_db_attribute *attrib,
+ unsigned int id, uint16_t offset,
+ const uint8_t *value, size_t len,
+ uint8_t opcode, struct bt_att *att,
+ void *user_data)
+{
+ struct server *server = user_data;
+ uint8_t error = 0;
+
+ //PRLOG("ESWIN pwd write called offset=%d len=%d\n",offset, len);
+
+ memset(netcfg_pwd, 0x00, CFG_ITEM_LEN);
+ pwd_len = 0;
+
+ if (offset+len >= CFG_ITEM_LEN) {
+ error = BT_ATT_ERROR_INVALID_OFFSET;
+ goto done;
+ }
+
+ if (value)
+ memcpy(netcfg_pwd + offset, value, len);
+
+ pwd_len +=len;
+ eswin_netcfg_wpa_conf(CFG_FLAG_PWD);
+done:
+ gatt_db_attribute_write_result(attrib, id, error);
+
+}
+
+static void eswin_netstatus_ccc_read_cb(struct gatt_db_attribute *attrib,
+ unsigned int id, uint16_t offset,
+ uint8_t opcode, struct bt_att *att,
+ void *user_data)
+{
+ struct server *server = user_data;
+ uint8_t value[2];
+
+ value[0] = server->netstatus_enabled ? 0x01 : 0x00;
+ value[1] = 0x00;
+
+ gatt_db_attribute_read_result(attrib, id, 0, value, 2);
+}
+
+#define WAP_STATE "wpa_state="
+static bool netstatus_cb(void *user_data)
+{
+ struct server *server = user_data;
+ FILE *fp = NULL;
+ char buff[500] = {0};
+ char *ptr = NULL, *ptr_end = NULL;
+ int head_len = strlen(WAP_STATE);
+ uint8_t cur_status[CFG_ITEM_LEN] = "";
+
+ fp = popen("sudo wpa_cli -i wlan0 status", "r");
+
+ fread(buff, 1, 499, fp);
+ ptr = strstr(buff, WAP_STATE);
+ if (ptr == NULL) {
+ PRLOG("no wpa_state\n");
+ return false;
+ }
+
+ ptr_end = strchr(ptr, '\n');
+ if (ptr_end == NULL) {
+ PRLOG("no wpa_state end\n");
+ return false;
+ }
+ strncpy(cur_status, ptr+head_len, ptr_end-ptr-head_len);
+ PRLOG("%s\n", cur_status);
+
+ if(strcmp(cur_status, net_status) != 0) {
+ memset(net_status, 0x00, CFG_ITEM_LEN);
+ strcpy(net_status, cur_status);
+ PRLOG("bt_gatt_server_send_notification\n");
+
+ bt_gatt_server_send_notification(server->gatt,
+ server->netstatus_handle,
+ net_status, strlen(net_status), false);
+ }
+ return true;
+}
+
+static void update_netstatus_simulation(struct server *server)
+{
+ if (!server->netstatus_enabled) {
+ timeout_remove(server->netstatus_timeout_id);
+ return;
+ }
+
+ server->netstatus_timeout_id = timeout_add(1000, netstatus_cb, server, NULL);
+}
+
+
+static void eswin_netstatus_ccc_write_cb(struct gatt_db_attribute *attrib,
+ unsigned int id, uint16_t offset,
+ const uint8_t *value, size_t len,
+ uint8_t opcode, struct bt_att *att,
+ void *user_data)
+{
+ struct server *server = user_data;
+ uint8_t ecode = 0;
+
+ if (!value || len != 2) {
+ ecode = BT_ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LEN;
+ goto done;
+ }
+
+ if (offset) {
+ ecode = BT_ATT_ERROR_INVALID_OFFSET;
+ goto done;
+ }
+
+ if (value[0] == 0x00)
+ server->netstatus_enabled = false;
+ else if (value[0] == 0x01) {
+ server->netstatus_enabled = true;
+ } else
+ ecode = 0x80;
+
+ PRLOG("netstatus Enabled: %s\n",
+ server->netstatus_enabled ? "true" : "false");
+ update_netstatus_simulation(server);
+
+done:
+ gatt_db_attribute_write_result(attrib, id, ecode);
+}
+
+#endif
+
+static void populate_gap_service(struct server *server)
+{
+ bt_uuid_t uuid;
+ struct gatt_db_attribute *service, *tmp;
+ uint16_t appearance;
+
+ /* Add the GAP service */
+ bt_uuid16_create(&uuid, UUID_GAP);
+ service = gatt_db_add_service(server->db, &uuid, true, 6);
+
+ /*
+ * Device Name characteristic. Make the value dynamically read and
+ * written via callbacks.
+ */
+ bt_uuid16_create(&uuid, GATT_CHARAC_DEVICE_NAME);
+ gatt_db_service_add_characteristic(service, &uuid,
+ BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+ BT_GATT_CHRC_PROP_READ |
+ BT_GATT_CHRC_PROP_EXT_PROP,
+ gap_device_name_read_cb,
+ gap_device_name_write_cb,
+ server);
+
+ bt_uuid16_create(&uuid, GATT_CHARAC_EXT_PROPER_UUID);
+ gatt_db_service_add_descriptor(service, &uuid, BT_ATT_PERM_READ,
+ gap_device_name_ext_prop_read_cb,
+ NULL, server);
+
+ /*
+ * Appearance characteristic. Reads and writes should obtain the value
+ * from the database.
+ */
+ bt_uuid16_create(&uuid, GATT_CHARAC_APPEARANCE);
+ tmp = gatt_db_service_add_characteristic(service, &uuid,
+ BT_ATT_PERM_READ,
+ BT_GATT_CHRC_PROP_READ,
+ NULL, NULL, server);
+
+ /*
+ * Write the appearance value to the database, since we're not using a
+ * callback.
+ */
+ put_le16(128, &appearance);
+ gatt_db_attribute_write(tmp, 0, (void *) &appearance,
+ sizeof(appearance),
+ BT_ATT_OP_WRITE_REQ,
+ NULL, confirm_write,
+ NULL);
+
+ gatt_db_service_set_active(service, true);
+}
+
+static void populate_gatt_service(struct server *server)
+{
+ bt_uuid_t uuid;
+ struct gatt_db_attribute *service, *svc_chngd;
+
+ /* Add the GATT service */
+ bt_uuid16_create(&uuid, UUID_GATT);
+ service = gatt_db_add_service(server->db, &uuid, true, 4);
+
+ bt_uuid16_create(&uuid, GATT_CHARAC_SERVICE_CHANGED);
+ svc_chngd = gatt_db_service_add_characteristic(service, &uuid,
+ BT_ATT_PERM_READ,
+ BT_GATT_CHRC_PROP_READ | BT_GATT_CHRC_PROP_INDICATE,
+ gatt_service_changed_cb,
+ NULL, server);
+ server->gatt_svc_chngd_handle = gatt_db_attribute_get_handle(svc_chngd);
+
+ bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID);
+ gatt_db_service_add_descriptor(service, &uuid,
+ BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+ gatt_svc_chngd_ccc_read_cb,
+ gatt_svc_chngd_ccc_write_cb, server);
+
+ gatt_db_service_set_active(service, true);
+}
+
+static void populate_hr_service(struct server *server)
+{
+ bt_uuid_t uuid;
+ struct gatt_db_attribute *service, *hr_msrmt, *body;
+ uint8_t body_loc = 1; /* "Chest" */
+
+ /* Add Heart Rate Service */
+ bt_uuid16_create(&uuid, UUID_HEART_RATE);
+ service = gatt_db_add_service(server->db, &uuid, true, 8);
+ server->hr_handle = gatt_db_attribute_get_handle(service);
+
+ /* HR Measurement Characteristic */
+ bt_uuid16_create(&uuid, UUID_HEART_RATE_MSRMT);
+ hr_msrmt = gatt_db_service_add_characteristic(service, &uuid,
+ BT_ATT_PERM_NONE,
+ BT_GATT_CHRC_PROP_NOTIFY,
+ NULL, NULL, NULL);
+ server->hr_msrmt_handle = gatt_db_attribute_get_handle(hr_msrmt);
+
+ bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID);
+ gatt_db_service_add_descriptor(service, &uuid,
+ BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+ hr_msrmt_ccc_read_cb,
+ hr_msrmt_ccc_write_cb, server);
+
+ /*
+ * Body Sensor Location Characteristic. Make reads obtain the value from
+ * the database.
+ */
+ bt_uuid16_create(&uuid, UUID_HEART_RATE_BODY);
+ body = gatt_db_service_add_characteristic(service, &uuid,
+ BT_ATT_PERM_READ,
+ BT_GATT_CHRC_PROP_READ,
+ NULL, NULL, server);
+ gatt_db_attribute_write(body, 0, (void *) &body_loc, sizeof(body_loc),
+ BT_ATT_OP_WRITE_REQ,
+ NULL, confirm_write,
+ NULL);
+
+ /* HR Control Point Characteristic */
+ bt_uuid16_create(&uuid, UUID_HEART_RATE_CTRL);
+ gatt_db_service_add_characteristic(service, &uuid,
+ BT_ATT_PERM_WRITE,
+ BT_GATT_CHRC_PROP_WRITE,
+ NULL, hr_control_point_write_cb,
+ server);
+
+ if (server->hr_visible)
+ gatt_db_service_set_active(service, true);
+}
+
+#ifdef ESWIN_TEST
+static void populate_eswin_service(struct server *server)
+{
+ bt_uuid_t uuid;
+ struct gatt_db_attribute *service, *netstatus;
+
+ ssid_len = strlen(netcfg_ssid);
+ pwd_len = strlen(netcfg_pwd);
+ server->netstatus_enabled = true;
+
+ /* Add Net config Service */
+ bt_uuid16_create(&uuid, UUID_NETCFG);
+ service = gatt_db_add_service(server->db, &uuid, true, 10);
+
+ /* SSID Characteristic */
+ bt_uuid16_create(&uuid, UUID_SSID);
+ gatt_db_service_add_characteristic(service, &uuid,
+ BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+ BT_GATT_CHRC_PROP_READ |
+ BT_GATT_CHRC_PROP_WRITE,
+ eswin_netcfg_ssid_read_cb,
+ eswin_netcfg_ssid_write_cb,
+ server);
+
+
+ /*
+ * PASSWORD Characteristic.
+ */
+ bt_uuid16_create(&uuid, UUID_PASSWORD);
+ gatt_db_service_add_characteristic(service, &uuid,
+ BT_ATT_PERM_READ| BT_ATT_PERM_WRITE,
+ BT_GATT_CHRC_PROP_READ |
+ BT_GATT_CHRC_PROP_WRITE,
+ eswin_netcfg_pwd_read_cb,
+ eswin_netcfg_pwd_write_cb,
+ server);
+
+ /*
+ * NETSTATUS Characteristic.
+ */
+ bt_uuid16_create(&uuid, UUID_NETSTATUS);
+ netstatus = gatt_db_service_add_characteristic(service, &uuid,
+ BT_ATT_PERM_NONE,
+ BT_GATT_CHRC_PROP_NOTIFY,
+ NULL, NULL, NULL);
+ server->netstatus_handle = gatt_db_attribute_get_handle(netstatus);
+
+ bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID);
+ gatt_db_service_add_descriptor(service, &uuid,
+ BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+ eswin_netstatus_ccc_read_cb,
+ eswin_netstatus_ccc_write_cb, server);
+
+ gatt_db_service_set_active(service, true);
+}
+
+#endif
+
+static void populate_db(struct server *server)
+{
+ populate_gap_service(server);
+ populate_gatt_service(server);
+ populate_hr_service(server);
+#ifdef ESWIN_TEST
+ populate_eswin_service(server);
+#endif
+}
+
+static struct server *server_create(int fd, uint16_t mtu, bool hr_visible)
+{
+ struct server *server;
+ size_t name_len = strlen(test_device_name);
+
+ server = new0(struct server, 1);
+ if (!server) {
+ fprintf(stderr, "Failed to allocate memory for server\n");
+ return NULL;
+ }
+
+ server->att = bt_att_new(fd, false);
+ if (!server->att) {
+ fprintf(stderr, "Failed to initialze ATT transport layer\n");
+ goto fail;
+ }
+
+ if (!bt_att_set_close_on_unref(server->att, true)) {
+ fprintf(stderr, "Failed to set up ATT transport layer\n");
+ goto fail;
+ }
+
+ if (!bt_att_register_disconnect(server->att, att_disconnect_cb, NULL,
+ NULL)) {
+ fprintf(stderr, "Failed to set ATT disconnect handler\n");
+ goto fail;
+ }
+
+ server->name_len = name_len + 1;
+ server->device_name = malloc(name_len + 1);
+ if (!server->device_name) {
+ fprintf(stderr, "Failed to allocate memory for device name\n");
+ goto fail;
+ }
+
+ memcpy(server->device_name, test_device_name, name_len);
+ server->device_name[name_len] = '\0';
+
+ server->fd = fd;
+ server->db = gatt_db_new();
+ if (!server->db) {
+ fprintf(stderr, "Failed to create GATT database\n");
+ goto fail;
+ }
+
+ server->gatt = bt_gatt_server_new(server->db, server->att, mtu, 0);
+ if (!server->gatt) {
+ fprintf(stderr, "Failed to create GATT server\n");
+ goto fail;
+ }
+
+ server->hr_visible = hr_visible;
+
+ if (verbose) {
+ bt_att_set_debug(server->att, BT_ATT_DEBUG_VERBOSE,
+ att_debug_cb, "att: ", NULL);
+ bt_gatt_server_set_debug(server->gatt, gatt_debug_cb,
+ "server: ", NULL);
+ }
+
+ /* Random seed for generating fake Heart Rate measurements */
+ srand(time(NULL));
+
+ /* bt_gatt_server already holds a reference */
+ populate_db(server);
+
+ return server;
+
+fail:
+ gatt_db_unref(server->db);
+ free(server->device_name);
+ bt_att_unref(server->att);
+ free(server);
+
+ return NULL;
+}
+
+static void server_destroy(struct server *server)
+{
+ timeout_remove(server->hr_timeout_id);
+ bt_gatt_server_unref(server->gatt);
+ gatt_db_unref(server->db);
+}
+
+static void usage(void)
+{
+ printf("btgatt-server\n");
+ printf("Usage:\n\tbtgatt-server [options]\n");
+
+ printf("Options:\n"
+ "\t-i, --index <id>\t\tSpecify adapter index, e.g. hci0\n"
+ "\t-m, --mtu <mtu>\t\t\tThe ATT MTU to use\n"
+ "\t-s, --security-level <sec>\tSet security level (low|"
+ "medium|high)\n"
+ "\t-t, --type [random|public] \t The source address type\n"
+ "\t-v, --verbose\t\t\tEnable extra logging\n"
+ "\t-r, --heart-rate\t\tEnable Heart Rate service\n"
+ "\t-h, --help\t\t\tDisplay help\n");
+}
+
+static struct option main_options[] = {
+ { "index", 1, 0, 'i' },
+ { "mtu", 1, 0, 'm' },
+ { "security-level", 1, 0, 's' },
+ { "type", 1, 0, 't' },
+ { "verbose", 0, 0, 'v' },
+ { "heart-rate", 0, 0, 'r' },
+ { "help", 0, 0, 'h' },
+ { }
+};
+
+static int l2cap_le_att_listen_and_accept(bdaddr_t *src, int sec,
+ uint8_t src_type)
+{
+ int sk, nsk;
+ struct sockaddr_l2 srcaddr, addr;
+ socklen_t optlen;
+ struct bt_security btsec;
+ char ba[18];
+
+ sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
+ if (sk < 0) {
+ perror("Failed to create L2CAP socket");
+ return -1;
+ }
+
+ /* Set up source address */
+ memset(&srcaddr, 0, sizeof(srcaddr));
+ srcaddr.l2_family = AF_BLUETOOTH;
+ srcaddr.l2_cid = htobs(ATT_CID);
+ srcaddr.l2_bdaddr_type = src_type;
+ bacpy(&srcaddr.l2_bdaddr, src);
+
+ if (bind(sk, (struct sockaddr *) &srcaddr, sizeof(srcaddr)) < 0) {
+ perror("Failed to bind L2CAP socket");
+ goto fail;
+ }
+
+ /* Set the security level */
+ memset(&btsec, 0, sizeof(btsec));
+ btsec.level = sec;
+ if (setsockopt(sk, SOL_BLUETOOTH, BT_SECURITY, &btsec,
+ sizeof(btsec)) != 0) {
+ fprintf(stderr, "Failed to set L2CAP security level\n");
+ goto fail;
+ }
+
+ if (listen(sk, 10) < 0) {
+ perror("Listening on socket failed");
+ goto fail;
+ }
+
+ printf("Started listening on ATT channel. Waiting for connections\n");
+
+ memset(&addr, 0, sizeof(addr));
+ optlen = sizeof(addr);
+ nsk = accept(sk, (struct sockaddr *) &addr, &optlen);
+ if (nsk < 0) {
+ perror("Accept failed");
+ goto fail;
+ }
+
+ ba2str(&addr.l2_bdaddr, ba);
+ printf("Connect from %s\n", ba);
+ close(sk);
+
+ return nsk;
+
+fail:
+ close(sk);
+ return -1;
+}
+
+static void notify_usage(void)
+{
+ printf("Usage: notify [options] <value_handle> <value>\n"
+ "Options:\n"
+ "\t -i, --indicate\tSend indication\n"
+ "e.g.:\n"
+ "\tnotify 0x0001 00 01 00\n");
+}
+
+static struct option notify_options[] = {
+ { "indicate", 0, 0, 'i' },
+ { }
+};
+
+static bool parse_args(char *str, int expected_argc, char **argv, int *argc)
+{
+ char **ap;
+
+ for (ap = argv; (*ap = strsep(&str, " \t")) != NULL;) {
+ if (**ap == '\0')
+ continue;
+
+ (*argc)++;
+ ap++;
+
+ if (*argc > expected_argc)
+ return false;
+ }
+
+ return true;
+}
+
+static void conf_cb(void *user_data)
+{
+ PRLOG("Received confirmation\n");
+}
+
+static void cmd_notify(struct server *server, char *cmd_str)
+{
+ int opt, i;
+ char *argvbuf[516];
+ char **argv = argvbuf;
+ int argc = 1;
+ uint16_t handle;
+ char *endptr = NULL;
+ int length;
+ uint8_t *value = NULL;
+ bool indicate = false;
+
+ if (!parse_args(cmd_str, 514, argv + 1, &argc)) {
+ printf("Too many arguments\n");
+ notify_usage();
+ return;
+ }
+
+ optind = 0;
+ argv[0] = "notify";
+ while ((opt = getopt_long(argc, argv, "+i", notify_options,
+ NULL)) != -1) {
+ switch (opt) {
+ case 'i':
+ indicate = true;
+ break;
+ default:
+ notify_usage();
+ return;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1) {
+ notify_usage();
+ return;
+ }
+
+ handle = strtol(argv[0], &endptr, 16);
+ if (!endptr || *endptr != '\0' || !handle) {
+ printf("Invalid handle: %s\n", argv[0]);
+ return;
+ }
+
+ length = argc - 1;
+
+ if (length > 0) {
+ if (length > UINT16_MAX) {
+ printf("Value too long\n");
+ return;
+ }
+
+ value = malloc(length);
+ if (!value) {
+ printf("Failed to construct value\n");
+ return;
+ }
+
+ for (i = 1; i < argc; i++) {
+ if (strlen(argv[i]) != 2) {
+ printf("Invalid value byte: %s\n",
+ argv[i]);
+ goto done;
+ }
+
+ value[i-1] = strtol(argv[i], &endptr, 16);
+ if (endptr == argv[i] || *endptr != '\0'
+ || errno == ERANGE) {
+ printf("Invalid value byte: %s\n",
+ argv[i]);
+ goto done;
+ }
+ }
+ }
+
+ if (indicate) {
+ if (!bt_gatt_server_send_indication(server->gatt, handle,
+ value, length,
+ conf_cb, NULL, NULL))
+ printf("Failed to initiate indication\n");
+ } else if (!bt_gatt_server_send_notification(server->gatt, handle,
+ value, length, false))
+ printf("Failed to initiate notification\n");
+
+done:
+ free(value);
+}
+
+static void heart_rate_usage(void)
+{
+ printf("Usage: heart-rate on|off\n");
+}
+
+static void cmd_heart_rate(struct server *server, char *cmd_str)
+{
+ bool enable;
+ uint8_t pdu[4];
+ struct gatt_db_attribute *attr;
+
+ if (!cmd_str) {
+ heart_rate_usage();
+ return;
+ }
+
+ if (strcmp(cmd_str, "on") == 0)
+ enable = true;
+ else if (strcmp(cmd_str, "off") == 0)
+ enable = false;
+ else {
+ heart_rate_usage();
+ return;
+ }
+
+ if (enable == server->hr_visible) {
+ printf("Heart Rate Service already %s\n",
+ enable ? "visible" : "hidden");
+ return;
+ }
+
+ server->hr_visible = enable;
+ attr = gatt_db_get_attribute(server->db, server->hr_handle);
+ gatt_db_service_set_active(attr, server->hr_visible);
+ update_hr_msrmt_simulation(server);
+
+ if (!server->svc_chngd_enabled)
+ return;
+
+ put_le16(server->hr_handle, pdu);
+ put_le16(server->hr_handle + 7, pdu + 2);
+
+ server->hr_msrmt_enabled = false;
+ update_hr_msrmt_simulation(server);
+
+ bt_gatt_server_send_indication(server->gatt,
+ server->gatt_svc_chngd_handle,
+ pdu, 4, conf_cb, NULL, NULL);
+}
+
+static void print_uuid(const bt_uuid_t *uuid)
+{
+ char uuid_str[MAX_LEN_UUID_STR];
+ bt_uuid_t uuid128;
+
+ bt_uuid_to_uuid128(uuid, &uuid128);
+ bt_uuid_to_string(&uuid128, uuid_str, sizeof(uuid_str));
+
+ printf("%s\n", uuid_str);
+}
+
+static void print_incl(struct gatt_db_attribute *attr, void *user_data)
+{
+ struct server *server = user_data;
+ uint16_t handle, start, end;
+ struct gatt_db_attribute *service;
+ bt_uuid_t uuid;
+
+ if (!gatt_db_attribute_get_incl_data(attr, &handle, &start, &end))
+ return;
+
+ service = gatt_db_get_attribute(server->db, start);
+ if (!service)
+ return;
+
+ gatt_db_attribute_get_service_uuid(service, &uuid);
+
+ printf("\t " COLOR_GREEN "include" COLOR_OFF " - handle: "
+ "0x%04x, - start: 0x%04x, end: 0x%04x,"
+ "uuid: ", handle, start, end);
+ print_uuid(&uuid);
+}
+
+static void print_desc(struct gatt_db_attribute *attr, void *user_data)
+{
+ printf("\t\t " COLOR_MAGENTA "descr" COLOR_OFF
+ " - handle: 0x%04x, uuid: ",
+ gatt_db_attribute_get_handle(attr));
+ print_uuid(gatt_db_attribute_get_type(attr));
+}
+
+static void print_chrc(struct gatt_db_attribute *attr, void *user_data)
+{
+ uint16_t handle, value_handle;
+ uint8_t properties;
+ uint16_t ext_prop;
+ bt_uuid_t uuid;
+
+ if (!gatt_db_attribute_get_char_data(attr, &handle,
+ &value_handle,
+ &properties,
+ &ext_prop,
+ &uuid))
+ return;
+
+ printf("\t " COLOR_YELLOW "charac" COLOR_OFF
+ " - start: 0x%04x, value: 0x%04x, "
+ "props: 0x%02x, ext_prop: 0x%04x, uuid: ",
+ handle, value_handle, properties, ext_prop);
+ print_uuid(&uuid);
+
+ gatt_db_service_foreach_desc(attr, print_desc, NULL);
+}
+
+static void print_service(struct gatt_db_attribute *attr, void *user_data)
+{
+ struct server *server = user_data;
+ uint16_t start, end;
+ bool primary;
+ bt_uuid_t uuid;
+
+ if (!gatt_db_attribute_get_service_data(attr, &start, &end, &primary,
+ &uuid))
+ return;
+
+ printf(COLOR_RED "service" COLOR_OFF " - start: 0x%04x, "
+ "end: 0x%04x, type: %s, uuid: ",
+ start, end, primary ? "primary" : "secondary");
+ print_uuid(&uuid);
+
+ gatt_db_service_foreach_incl(attr, print_incl, server);
+ gatt_db_service_foreach_char(attr, print_chrc, NULL);
+
+ printf("\n");
+}
+
+static void cmd_services(struct server *server, char *cmd_str)
+{
+ gatt_db_foreach_service(server->db, NULL, print_service, server);
+}
+
+static bool convert_sign_key(char *optarg, uint8_t key[16])
+{
+ int i;
+
+ if (strlen(optarg) != 32) {
+ printf("sign-key length is invalid\n");
+ return false;
+ }
+
+ for (i = 0; i < 16; i++) {
+ if (sscanf(optarg + (i * 2), "%2hhx", &key[i]) != 1)
+ return false;
+ }
+
+ return true;
+}
+
+static void set_sign_key_usage(void)
+{
+ printf("Usage: set-sign-key [options]\nOptions:\n"
+ "\t -c, --sign-key <remote csrk>\tRemote CSRK\n"
+ "e.g.:\n"
+ "\tset-sign-key -c D8515948451FEA320DC05A2E88308188\n");
+}
+
+static bool remote_counter(uint32_t *sign_cnt, void *user_data)
+{
+ static uint32_t cnt = 0;
+
+ if (*sign_cnt < cnt)
+ return false;
+
+ cnt = *sign_cnt;
+
+ return true;
+}
+
+static void cmd_set_sign_key(struct server *server, char *cmd_str)
+{
+ char *argv[3];
+ int argc = 0;
+ uint8_t key[16];
+
+ memset(key, 0, 16);
+
+ if (!parse_args(cmd_str, 2, argv, &argc)) {
+ set_sign_key_usage();
+ return;
+ }
+
+ if (argc != 2) {
+ set_sign_key_usage();
+ return;
+ }
+
+ if (!strcmp(argv[0], "-c") || !strcmp(argv[0], "--sign-key")) {
+ if (convert_sign_key(argv[1], key))
+ bt_att_set_remote_key(server->att, key, remote_counter,
+ server);
+ } else
+ set_sign_key_usage();
+}
+
+static void cmd_help(struct server *server, char *cmd_str);
+
+typedef void (*command_func_t)(struct server *server, char *cmd_str);
+
+static struct {
+ char *cmd;
+ command_func_t func;
+ char *doc;
+} command[] = {
+ { "help", cmd_help, "\tDisplay help message" },
+ { "notify", cmd_notify, "\tSend handle-value notification" },
+ { "heart-rate", cmd_heart_rate, "\tHide/Unhide Heart Rate Service" },
+ { "services", cmd_services, "\tEnumerate all services" },
+ { "set-sign-key", cmd_set_sign_key,
+ "\tSet remote signing key for signed write command"},
+ { }
+};
+
+static void cmd_help(struct server *server, char *cmd_str)
+{
+ int i;
+
+ printf("Commands:\n");
+ for (i = 0; command[i].cmd; i++)
+ printf("\t%-15s\t%s\n", command[i].cmd, command[i].doc);
+}
+
+static void prompt_read_cb(int fd, uint32_t events, void *user_data)
+{
+ ssize_t read;
+ size_t len = 0;
+ char *line = NULL;
+ char *cmd = NULL, *args;
+ struct server *server = user_data;
+ int i;
+
+ if (events & (EPOLLRDHUP | EPOLLHUP | EPOLLERR)) {
+ mainloop_quit();
+ return;
+ }
+
+ read = getline(&line, &len, stdin);
+ if (read < 0)
+ return;
+
+ if (read <= 1) {
+ cmd_help(server, NULL);
+ print_prompt();
+ return;
+ }
+
+ line[read-1] = '\0';
+ args = line;
+
+ while ((cmd = strsep(&args, " \t")))
+ if (*cmd != '\0')
+ break;
+
+ if (!cmd)
+ goto failed;
+
+ for (i = 0; command[i].cmd; i++) {
+ if (strcmp(command[i].cmd, cmd) == 0)
+ break;
+ }
+
+ if (command[i].cmd)
+ command[i].func(server, args);
+ else
+ fprintf(stderr, "Unknown command: %s\n", line);
+
+failed:
+ print_prompt();
+
+ free(line);
+}
+
+static void signal_cb(int signum, void *user_data)
+{
+ switch (signum) {
+ case SIGINT:
+ case SIGTERM:
+ mainloop_quit();
+ break;
+ default:
+ break;
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ int opt;
+ bdaddr_t src_addr;
+ int dev_id = -1;
+ int fd;
+ int sec = BT_SECURITY_LOW;
+ uint8_t src_type = BDADDR_LE_PUBLIC;
+ uint16_t mtu = 0;
+ bool hr_visible = false;
+ struct server *server;
+
+ while ((opt = getopt_long(argc, argv, "+hvrs:t:m:i:",
+ main_options, NULL)) != -1) {
+ switch (opt) {
+ case 'h':
+ usage();
+ return EXIT_SUCCESS;
+ case 'v':
+ verbose = true;
+ break;
+ case 'r':
+ hr_visible = true;
+ break;
+ case 's':
+ if (strcmp(optarg, "low") == 0)
+ sec = BT_SECURITY_LOW;
+ else if (strcmp(optarg, "medium") == 0)
+ sec = BT_SECURITY_MEDIUM;
+ else if (strcmp(optarg, "high") == 0)
+ sec = BT_SECURITY_HIGH;
+ else {
+ fprintf(stderr, "Invalid security level\n");
+ return EXIT_FAILURE;
+ }
+ break;
+ case 't':
+ if (strcmp(optarg, "random") == 0)
+ src_type = BDADDR_LE_RANDOM;
+ else if (strcmp(optarg, "public") == 0)
+ src_type = BDADDR_LE_PUBLIC;
+ else {
+ fprintf(stderr,
+ "Allowed types: random, public\n");
+ return EXIT_FAILURE;
+ }
+ break;
+ case 'm': {
+ int arg;
+
+ arg = atoi(optarg);
+ if (arg <= 0) {
+ fprintf(stderr, "Invalid MTU: %d\n", arg);
+ return EXIT_FAILURE;
+ }
+
+ if (arg > UINT16_MAX) {
+ fprintf(stderr, "MTU too large: %d\n", arg);
+ return EXIT_FAILURE;
+ }
+
+ mtu = (uint16_t) arg;
+ break;
+ }
+ case 'i':
+ dev_id = hci_devid(optarg);
+ if (dev_id < 0) {
+ perror("Invalid adapter");
+ return EXIT_FAILURE;
+ }
+
+ break;
+ default:
+ fprintf(stderr, "Invalid option: %c\n", opt);
+ return EXIT_FAILURE;
+ }
+ }
+
+ argc -= optind;
+ argv -= optind;
+ optind = 0;
+
+ if (argc) {
+ usage();
+ return EXIT_SUCCESS;
+ }
+
+ if (dev_id == -1)
+ bacpy(&src_addr, BDADDR_ANY);
+ else if (hci_devba(dev_id, &src_addr) < 0) {
+ perror("Adapter not available");
+ return EXIT_FAILURE;
+ }
+
+ fd = l2cap_le_att_listen_and_accept(&src_addr, sec, src_type);
+ if (fd < 0) {
+ fprintf(stderr, "Failed to accept L2CAP ATT connection\n");
+ return EXIT_FAILURE;
+ }
+
+ mainloop_init();
+
+ server = server_create(fd, mtu, hr_visible);
+ if (!server) {
+ close(fd);
+ return EXIT_FAILURE;
+ }
+
+ if (mainloop_add_fd(fileno(stdin),
+ EPOLLIN | EPOLLRDHUP | EPOLLHUP | EPOLLERR,
+ prompt_read_cb, server, NULL) < 0) {
+ fprintf(stderr, "Failed to initialize console\n");
+ server_destroy(server);
+
+ return EXIT_FAILURE;
+ }
+
+ printf("Running GATT server\n");
+
+ print_prompt();
+
+ mainloop_run_with_signal(signal_cb, NULL);
+
+ printf("\n\nShutting down...\n");
+
+ server_destroy(server);
+
+ return EXIT_SUCCESS;
+}
diff --git a/drivers/net/wireless/eswin/compile_test.sh b/drivers/net/wireless/eswin/compile_test.sh
new file mode 100644
index 000000000000..faa142f88c60
--- /dev/null
+++ b/drivers/net/wireless/eswin/compile_test.sh
@@ -0,0 +1,29 @@
+#!/bin/bash
+
+linux_dir=/net/rwlab-srv1/nx_share/linux
+ARCH=${ARCH:-x86}
+CROSS_COMPILE=${CROSS_COMPILE:-x86_64-poky-linux-}
+error=0
+
+if [ ! -d $linux_dir ]
+then
+ echo "Invalid path: ${linux_dir}" >&2
+ exit 1
+fi
+
+for version in $(find $linux_dir -maxdepth 2 -type d -name cevav7 | sort)
+do
+ echo ""
+ echo "#####################################################"
+ echo "Testing $version"
+ echo "#####################################################"
+ ARCH=$ARCH CROSS_COMPILE=$CROSS_COMPILE KERNELDIR=$version \
+ CONFIG_ECRNX_SOFTMAC=m CONFIG_ECRNX_FULLMAC=m CONFIG_ECRNX_FHOST=m make -j 8
+
+ if [ $? -ne 0 ]
+ then
+ ((error++))
+ fi
+done
+
+exit $error
diff --git a/drivers/net/wireless/eswin/ecrnx_bfmer.c b/drivers/net/wireless/eswin/ecrnx_bfmer.c
new file mode 100644
index 000000000000..467da01f9e4f
--- /dev/null
+++ b/drivers/net/wireless/eswin/ecrnx_bfmer.c
@@ -0,0 +1,105 @@
+/**
+ ******************************************************************************
+ *
+ * @file ecrnx_bfmer.c
+ *
+ * @brief VHT Beamformer function definitions
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+
+/**
+ * INCLUDE FILES
+ ******************************************************************************
+ */
+
+#include <linux/slab.h>
+#include "ecrnx_bfmer.h"
+
+/**
+ * FUNCTION DEFINITIONS
+ ******************************************************************************
+ */
+
+int ecrnx_bfmer_report_add(struct ecrnx_hw *ecrnx_hw, struct ecrnx_sta *ecrnx_sta,
+ unsigned int length)
+{
+ gfp_t flags;
+ struct ecrnx_bfmer_report *bfm_report ;
+
+ if (in_softirq())
+ flags = GFP_ATOMIC;
+ else
+ flags = GFP_KERNEL;
+
+ /* Allocate a structure that will contain the beamforming report */
+ bfm_report = kmalloc(sizeof(*bfm_report) + length, flags);
+
+
+ /* Check report allocation */
+ if (!bfm_report) {
+ /* Do not use beamforming */
+ return -1;
+ }
+
+ /* Store report length */
+ bfm_report->length = length;
+
+ /*
+ * Need to provide a Virtual Address to the MAC so that it can
+ * upload the received Beamforming Report in driver memory
+ */
+ bfm_report->dma_addr = dma_map_single(ecrnx_hw->dev, &bfm_report->report[0],
+ length, DMA_FROM_DEVICE);
+
+ /* Check DMA mapping result */
+ if (dma_mapping_error(ecrnx_hw->dev, bfm_report->dma_addr)) {
+ /* Free allocated report */
+ kfree(bfm_report);
+ /* And leave */
+ return -1;
+ }
+
+ /* Store report structure */
+ ecrnx_sta->bfm_report = bfm_report;
+
+ return 0;
+}
+
+void ecrnx_bfmer_report_del(struct ecrnx_hw *ecrnx_hw, struct ecrnx_sta *ecrnx_sta)
+{
+ /* Verify if a report has been allocated */
+ if (ecrnx_sta->bfm_report) {
+ struct ecrnx_bfmer_report *bfm_report = ecrnx_sta->bfm_report;
+
+ /* Unmap DMA region */
+ dma_unmap_single(ecrnx_hw->dev, bfm_report->dma_addr,
+ bfm_report->length, DMA_BIDIRECTIONAL);
+
+ /* Free allocated report structure and clean the pointer */
+ kfree(bfm_report);
+ ecrnx_sta->bfm_report = NULL;
+ }
+}
+
+#ifdef CONFIG_ECRNX_FULLMAC
+u8 ecrnx_bfmer_get_rx_nss(const struct ieee80211_vht_cap *vht_capa)
+{
+ int i;
+ u8 rx_nss = 0;
+ u16 rx_mcs_map = le16_to_cpu(vht_capa->supp_mcs.rx_mcs_map);
+
+ for (i = 7; i >= 0; i--) {
+ u8 mcs = (rx_mcs_map >> (2 * i)) & 3;
+
+ if (mcs != IEEE80211_VHT_MCS_NOT_SUPPORTED) {
+ rx_nss = i + 1;
+ break;
+ }
+ }
+
+ return rx_nss;
+}
+#endif /* CONFIG_ECRNX_FULLMAC */
diff --git a/drivers/net/wireless/eswin/ecrnx_bfmer.h b/drivers/net/wireless/eswin/ecrnx_bfmer.h
new file mode 100644
index 000000000000..6bb30ef27895
--- /dev/null
+++ b/drivers/net/wireless/eswin/ecrnx_bfmer.h
@@ -0,0 +1,100 @@
+/**
+ ******************************************************************************
+ *
+ * @file ecrnx_bfmer.h
+ *
+ * @brief VHT Beamformer function declarations
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+
+#ifndef _ECRNX_BFMER_H_
+#define _ECRNX_BFMER_H_
+
+/**
+ * INCLUDE FILES
+ ******************************************************************************
+ */
+
+#include "ecrnx_defs.h"
+
+/**
+ * DEFINES
+ ******************************************************************************
+ */
+
+/// Maximal supported report length (in bytes)
+#define ECRNX_BFMER_REPORT_MAX_LEN 2048
+
+/// Size of the allocated report space (twice the maximum report length)
+#define ECRNX_BFMER_REPORT_SPACE_SIZE (ECRNX_BFMER_REPORT_MAX_LEN * 2)
+
+/**
+ * TYPE DEFINITIONS
+ ******************************************************************************
+ */
+
+/*
+ * Structure used to store a beamforming report.
+ */
+struct ecrnx_bfmer_report {
+ dma_addr_t dma_addr; /* Virtual address provided to MAC for
+ DMA transfer of the Beamforming Report */
+ unsigned int length; /* Report Length */
+ u8 report[1]; /* Report to be used for VHT TX Beamforming */
+};
+
+/**
+ * FUNCTION DECLARATIONS
+ ******************************************************************************
+ */
+
+/**
+ ******************************************************************************
+ * @brief Allocate memory aiming to contains the Beamforming Report received
+ * from a Beamformee capable capable.
+ * The providing length shall be large enough to contain the VHT Compressed
+ * Beaforming Report and the MU Exclusive part.
+ * It also perform a DMA Mapping providing an address to be provided to the HW
+ * responsible for the DMA transfer of the report.
+ * If successful a struct ecrnx_bfmer_report object is allocated, it's address
+ * is stored in ecrnx_sta->bfm_report.
+ *
+ * @param[in] ecrnx_hw PHY Information
+ * @param[in] ecrnx_sta Peer STA Information
+ * @param[in] length Memory size to be allocated
+ *
+ * @return 0 if operation is successful, else -1.
+ ******************************************************************************
+ */
+int ecrnx_bfmer_report_add(struct ecrnx_hw *ecrnx_hw, struct ecrnx_sta *ecrnx_sta,
+ unsigned int length);
+
+/**
+ ******************************************************************************
+ * @brief Free a previously allocated memory intended to be used for
+ * Beamforming Reports.
+ *
+ * @param[in] ecrnx_hw PHY Information
+ * @param[in] ecrnx_sta Peer STA Information
+ *
+ ******************************************************************************
+ */
+void ecrnx_bfmer_report_del(struct ecrnx_hw *ecrnx_hw, struct ecrnx_sta *ecrnx_sta);
+
+#ifdef CONFIG_ECRNX_FULLMAC
+/**
+ ******************************************************************************
+ * @brief Parse a Rx VHT-MCS map in order to deduce the maximum number of
+ * Spatial Streams supported by a beamformee.
+ *
+ * @param[in] vht_capa Received VHT Capability field.
+ *
+ ******************************************************************************
+ */
+u8 ecrnx_bfmer_get_rx_nss(const struct ieee80211_vht_cap *vht_capa);
+#endif /* CONFIG_ECRNX_FULLMAC */
+
+#endif /* _ECRNX_BFMER_H_ */
diff --git a/drivers/net/wireless/eswin/ecrnx_cfgfile.c b/drivers/net/wireless/eswin/ecrnx_cfgfile.c
new file mode 100644
index 000000000000..c753677c5749
--- /dev/null
+++ b/drivers/net/wireless/eswin/ecrnx_cfgfile.c
@@ -0,0 +1,288 @@
+/**
+ ****************************************************************************************
+ *
+ * @file ecrnx_configparse.c
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ****************************************************************************************
+ */
+#include <linux/firmware.h>
+#include <linux/if_ether.h>
+
+#include "ecrnx_defs.h"
+#include "ecrnx_cfgfile.h"
+#include "ecrnx_debug.h"
+#include "ecrnx_debugfs_func.h"
+
+/**
+ *
+ */
+static const char *ecrnx_find_tag(const u8 *file_data, unsigned int file_size,
+ const char *tag_name, unsigned int tag_len)
+{
+ unsigned int curr, line_start = 0, line_size;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ /* Walk through all the lines of the configuration file */
+ while (line_start < file_size) {
+ /* Search the end of the current line (or the end of the file) */
+ for (curr = line_start; curr < file_size; curr++)
+ if (file_data[curr] == '\n')
+ break;
+
+ /* Compute the line size */
+ line_size = curr - line_start;
+
+ /* Check if this line contains the expected tag */
+ if ((line_size == (strlen(tag_name) + tag_len)) &&
+ (!strncmp(&file_data[line_start], tag_name, strlen(tag_name))))
+ return (&file_data[line_start + strlen(tag_name)]);
+
+ /* Move to next line */
+ line_start = curr + 1;
+ }
+
+ /* Tag not found */
+ return NULL;
+}
+
+/**
+ * Parse the Config file used at init time
+ */
+int ecrnx_parse_configfile(struct ecrnx_hw *ecrnx_hw, const char *filename)
+{
+ const struct firmware *config_fw;
+ u8 dflt_mac[ETH_ALEN] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x5f };
+ int ret;
+ const u8 *tag_ptr;
+ bool mac_flag = false, dbg_level_flag = false, fw_log_lv_flag = false, fw_log_type_flag = false;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0)
+ ret = firmware_request_nowarn(&config_fw, filename, ecrnx_hw->dev); //avoid the files not exit error
+#else
+ ret = request_firmware(&config_fw, filename, ecrnx_hw->dev);
+#endif
+
+ if (ret == 0) {
+ /* Get MAC Address */
+ tag_ptr = ecrnx_find_tag(config_fw->data, config_fw->size, "MAC_ADDR=", strlen("00:00:00:00:00:00"));
+ if (tag_ptr != NULL) {
+ u8 *addr = ecrnx_hw->conf_param.mac_addr;
+ if (sscanf(tag_ptr, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
+ addr + 0, addr + 1, addr + 2,
+ addr + 3, addr + 4, addr + 5) == ETH_ALEN){
+ mac_flag = true;
+ }
+ }
+
+ tag_ptr = ecrnx_find_tag(config_fw->data, config_fw->size, "DRIVER_LOG_LEVEL=", strlen("0"));
+ if (tag_ptr != NULL){
+ if(sscanf(tag_ptr, "%hhx", &ecrnx_hw->conf_param.host_driver_log_level) == 1){
+ ecrnx_dbg_level = ecrnx_hw->conf_param.host_driver_log_level;
+ dbg_level_flag = true;
+ }
+ }
+
+ tag_ptr = ecrnx_find_tag(config_fw->data, config_fw->size, "FW_LOG_LEVEL=", strlen("0"));
+ if (tag_ptr != NULL){
+ if(sscanf(tag_ptr, "%hhx", &ecrnx_hw->conf_param.fw_log_level) == 1){
+ fw_log_lv_flag = true;
+ }
+ }
+
+ tag_ptr = ecrnx_find_tag(config_fw->data, config_fw->size, "FW_LOG_TYPE=", strlen("0"));
+ if (tag_ptr != NULL){
+ if(sscanf(tag_ptr, "%hhx", &ecrnx_hw->conf_param.fw_log_type) == 1){
+ fw_log_type_flag = true;
+ }
+ }
+
+ /* Release the configuration file */
+ release_firmware(config_fw);
+ }
+
+ if(!mac_flag){
+ memcpy(ecrnx_hw->conf_param.mac_addr, dflt_mac, ETH_ALEN);
+ }
+
+ if(!dbg_level_flag){
+ ecrnx_hw->conf_param.host_driver_log_level = ecrnx_dbg_level;
+ }
+
+ if(!fw_log_lv_flag){
+ ecrnx_hw->conf_param.fw_log_level = log_ctl.level;
+ }
+
+ if(!fw_log_type_flag){
+ ecrnx_hw->conf_param.fw_log_type = log_ctl.dir;
+ }
+
+ ECRNX_PRINT("MAC Address is:%pM\n", ecrnx_hw->conf_param.mac_addr);
+ ECRNX_PRINT("host driver log level is:%d \n", ecrnx_hw->conf_param.host_driver_log_level);
+ ECRNX_PRINT("firmware log level is:%d \n", ecrnx_hw->conf_param.fw_log_level);
+
+ if(0 == ecrnx_hw->conf_param.fw_log_type){
+ ECRNX_PRINT("firmware log level type:%d (print to chip's uart) \n", ecrnx_hw->conf_param.fw_log_type);
+ }else if(1 == ecrnx_hw->conf_param.fw_log_type){
+ ECRNX_PRINT("firmware log level type:%d (print to host debugfs) \n", ecrnx_hw->conf_param.fw_log_type);
+ }else if(2 == ecrnx_hw->conf_param.fw_log_type){
+ ECRNX_PRINT("firmware log level type:%d (print to host kernel) \n", ecrnx_hw->conf_param.fw_log_type);
+ }else{
+ ECRNX_ERR("firmware log level type error;\n");
+ }
+ return 0;
+}
+
+/**
+ * Parse the Config file used at init time
+ */
+int ecrnx_parse_phy_configfile(struct ecrnx_hw *ecrnx_hw, const char *filename,
+ struct ecrnx_phy_conf_file *config, int path)
+{
+ const struct firmware *config_fw;
+ int ret;
+ const u8 *tag_ptr;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ if ((ret = request_firmware(&config_fw, filename, ecrnx_hw->dev))) {
+ ECRNX_ERR(KERN_CRIT "%s: Failed to get %s (%d)\n", __func__, filename, ret);
+ return ret;
+ }
+
+ /* Get Trident path mapping */
+ tag_ptr = ecrnx_find_tag(config_fw->data, config_fw->size,
+ "TRD_PATH_MAPPING=", strlen("00"));
+ if (tag_ptr != NULL) {
+ u8 val;
+ if (sscanf(tag_ptr, "%hhx", &val) == 1)
+ config->trd.path_mapping = val;
+ else
+ config->trd.path_mapping = path;
+ } else
+ config->trd.path_mapping = path;
+
+ ECRNX_DBG("Trident path mapping is: %d\n", config->trd.path_mapping);
+
+ /* Get DC offset compensation */
+ tag_ptr = ecrnx_find_tag(config_fw->data, config_fw->size,
+ "TX_DC_OFF_COMP=", strlen("00000000"));
+ if (tag_ptr != NULL) {
+ if (sscanf(tag_ptr, "%08x", &config->trd.tx_dc_off_comp) != 1)
+ config->trd.tx_dc_off_comp = 0;
+ } else
+ config->trd.tx_dc_off_comp = 0;
+
+ ECRNX_DBG("TX DC offset compensation is: %08X\n", config->trd.tx_dc_off_comp);
+
+ /* Get Karst TX IQ compensation value for path0 on 2.4GHz */
+ tag_ptr = ecrnx_find_tag(config_fw->data, config_fw->size,
+ "KARST_TX_IQ_COMP_2_4G_PATH_0=", strlen("00000000"));
+ if (tag_ptr != NULL) {
+ if (sscanf(tag_ptr, "%08x", &config->karst.tx_iq_comp_2_4G[0]) != 1)
+ config->karst.tx_iq_comp_2_4G[0] = 0x01000000;
+ } else
+ config->karst.tx_iq_comp_2_4G[0] = 0x01000000;
+
+ ECRNX_DBG("Karst TX IQ compensation for path 0 on 2.4GHz is: %08X\n", config->karst.tx_iq_comp_2_4G[0]);
+
+ /* Get Karst TX IQ compensation value for path1 on 2.4GHz */
+ tag_ptr = ecrnx_find_tag(config_fw->data, config_fw->size,
+ "KARST_TX_IQ_COMP_2_4G_PATH_1=", strlen("00000000"));
+ if (tag_ptr != NULL) {
+ if (sscanf(tag_ptr, "%08x", &config->karst.tx_iq_comp_2_4G[1]) != 1)
+ config->karst.tx_iq_comp_2_4G[1] = 0x01000000;
+ } else
+ config->karst.tx_iq_comp_2_4G[1] = 0x01000000;
+
+ ECRNX_DBG("Karst TX IQ compensation for path 1 on 2.4GHz is: %08X\n", config->karst.tx_iq_comp_2_4G[1]);
+
+ /* Get Karst RX IQ compensation value for path0 on 2.4GHz */
+ tag_ptr = ecrnx_find_tag(config_fw->data, config_fw->size,
+ "KARST_RX_IQ_COMP_2_4G_PATH_0=", strlen("00000000"));
+ if (tag_ptr != NULL) {
+ if (sscanf(tag_ptr, "%08x", &config->karst.rx_iq_comp_2_4G[0]) != 1)
+ config->karst.rx_iq_comp_2_4G[0] = 0x01000000;
+ } else
+ config->karst.rx_iq_comp_2_4G[0] = 0x01000000;
+
+ ECRNX_DBG("Karst RX IQ compensation for path 0 on 2.4GHz is: %08X\n", config->karst.rx_iq_comp_2_4G[0]);
+
+ /* Get Karst RX IQ compensation value for path1 on 2.4GHz */
+ tag_ptr = ecrnx_find_tag(config_fw->data, config_fw->size,
+ "KARST_RX_IQ_COMP_2_4G_PATH_1=", strlen("00000000"));
+ if (tag_ptr != NULL) {
+ if (sscanf(tag_ptr, "%08x", &config->karst.rx_iq_comp_2_4G[1]) != 1)
+ config->karst.rx_iq_comp_2_4G[1] = 0x01000000;
+ } else
+ config->karst.rx_iq_comp_2_4G[1] = 0x01000000;
+
+ ECRNX_DBG("Karst RX IQ compensation for path 1 on 2.4GHz is: %08X\n", config->karst.rx_iq_comp_2_4G[1]);
+
+ /* Get Karst TX IQ compensation value for path0 on 5GHz */
+ tag_ptr = ecrnx_find_tag(config_fw->data, config_fw->size,
+ "KARST_TX_IQ_COMP_5G_PATH_0=", strlen("00000000"));
+ if (tag_ptr != NULL) {
+ if (sscanf(tag_ptr, "%08x", &config->karst.tx_iq_comp_5G[0]) != 1)
+ config->karst.tx_iq_comp_5G[0] = 0x01000000;
+ } else
+ config->karst.tx_iq_comp_5G[0] = 0x01000000;
+
+ ECRNX_DBG("Karst TX IQ compensation for path 0 on 5GHz is: %08X\n", config->karst.tx_iq_comp_5G[0]);
+
+ /* Get Karst TX IQ compensation value for path1 on 5GHz */
+ tag_ptr = ecrnx_find_tag(config_fw->data, config_fw->size,
+ "KARST_TX_IQ_COMP_5G_PATH_1=", strlen("00000000"));
+ if (tag_ptr != NULL) {
+ if (sscanf(tag_ptr, "%08x", &config->karst.tx_iq_comp_5G[1]) != 1)
+ config->karst.tx_iq_comp_5G[1] = 0x01000000;
+ } else
+ config->karst.tx_iq_comp_5G[1] = 0x01000000;
+
+ ECRNX_DBG("Karst TX IQ compensation for path 1 on 5GHz is: %08X\n", config->karst.tx_iq_comp_5G[1]);
+
+ /* Get Karst RX IQ compensation value for path0 on 5GHz */
+ tag_ptr = ecrnx_find_tag(config_fw->data, config_fw->size,
+ "KARST_RX_IQ_COMP_5G_PATH_0=", strlen("00000000"));
+ if (tag_ptr != NULL) {
+ if (sscanf(tag_ptr, "%08x", &config->karst.rx_iq_comp_5G[0]) != 1)
+ config->karst.rx_iq_comp_5G[0] = 0x01000000;
+ } else
+ config->karst.rx_iq_comp_5G[0] = 0x01000000;
+
+ ECRNX_DBG("Karst RX IQ compensation for path 0 on 5GHz is: %08X\n", config->karst.rx_iq_comp_5G[0]);
+
+ /* Get Karst RX IQ compensation value for path1 on 5GHz */
+ tag_ptr = ecrnx_find_tag(config_fw->data, config_fw->size,
+ "KARST_RX_IQ_COMP_5G_PATH_1=", strlen("00000000"));
+ if (tag_ptr != NULL) {
+ if (sscanf(tag_ptr, "%08x", &config->karst.rx_iq_comp_5G[1]) != 1)
+ config->karst.rx_iq_comp_5G[1] = 0x01000000;
+ } else
+ config->karst.rx_iq_comp_5G[1] = 0x01000000;
+
+ ECRNX_DBG("Karst RX IQ compensation for path 1 on 5GHz is: %08X\n", config->karst.rx_iq_comp_5G[1]);
+
+ /* Get Karst default path */
+ tag_ptr = ecrnx_find_tag(config_fw->data, config_fw->size,
+ "KARST_DEFAULT_PATH=", strlen("00"));
+ if (tag_ptr != NULL) {
+ u8 val;
+ if (sscanf(tag_ptr, "%hhx", &val) == 1)
+ config->karst.path_used = val;
+ else
+ config->karst.path_used = path;
+ } else
+ config->karst.path_used = path;
+
+ ECRNX_DBG("Karst default path is: %d\n", config->karst.path_used);
+
+ /* Release the configuration file */
+ release_firmware(config_fw);
+
+ return 0;
+}
+
diff --git a/drivers/net/wireless/eswin/ecrnx_cfgfile.h b/drivers/net/wireless/eswin/ecrnx_cfgfile.h
new file mode 100644
index 000000000000..bdf7eaa12eb7
--- /dev/null
+++ b/drivers/net/wireless/eswin/ecrnx_cfgfile.h
@@ -0,0 +1,37 @@
+/**
+ ****************************************************************************************
+ *
+ * @file ecrnx_cfgfile.h
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ****************************************************************************************
+ */
+
+#ifndef _ECRNX_CFGFILE_H_
+#define _ECRNX_CFGFILE_H_
+
+/*
+ * Structure used to retrieve information from the Config file used at Initialization time
+ */
+struct ecrnx_conf_file {
+ u8 mac_addr[ETH_ALEN];
+ u8 host_driver_log_level;
+ u8 fw_log_level;
+ u8 fw_log_type;
+};
+
+/*
+ * Structure used to retrieve information from the PHY Config file used at Initialization time
+ */
+struct ecrnx_phy_conf_file {
+ struct phy_trd_cfg_tag trd;
+ struct phy_karst_cfg_tag karst;
+ struct phy_cataxia_cfg_tag cataxia;
+};
+
+int ecrnx_parse_configfile(struct ecrnx_hw *ecrnx_hw, const char *filename);
+int ecrnx_parse_phy_configfile(struct ecrnx_hw *ecrnx_hw, const char *filename,
+ struct ecrnx_phy_conf_file *config, int path);
+
+#endif /* _ECRNX_CFGFILE_H_ */
diff --git a/drivers/net/wireless/eswin/ecrnx_cmds.c b/drivers/net/wireless/eswin/ecrnx_cmds.c
new file mode 100644
index 000000000000..5e4781af500a
--- /dev/null
+++ b/drivers/net/wireless/eswin/ecrnx_cmds.c
@@ -0,0 +1,325 @@
+/**
+ ******************************************************************************
+ *
+ * ecrnx_cmds.c
+ *
+ * Handles queueing (push to IPC, ack/cfm from IPC) of commands issued to
+ * LMAC FW
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+
+#include <linux/list.h>
+
+#include "ecrnx_cmds.h"
+#include "ecrnx_defs.h"
+#include "ecrnx_strs.h"
+#define CREATE_TRACE_POINTS
+#include "ecrnx_events.h"
+
+/**
+ *
+ */
+static void cmd_dump(const struct ecrnx_cmd *cmd)
+{
+#ifndef CONFIG_ECRNX_FHOST
+ ECRNX_PRINT("tkn[%d] flags:%04x result:%3d cmd:%4d-%-24s - reqcfm(%4d-%-s)\n",
+ cmd->tkn, cmd->flags, cmd->result, cmd->id, ECRNX_ID2STR(cmd->id),
+ cmd->reqid, cmd->reqid != (lmac_msg_id_t)-1 ? ECRNX_ID2STR(cmd->reqid) : "none");
+#endif
+}
+
+/**
+ *
+ */
+static void cmd_complete(struct ecrnx_cmd_mgr *cmd_mgr, struct ecrnx_cmd *cmd)
+{
+ lockdep_assert_held(&cmd_mgr->lock);
+
+ list_del(&cmd->list);
+ cmd_mgr->queue_sz--;
+
+ cmd->flags |= ECRNX_CMD_FLAG_DONE;
+ if (cmd->flags & ECRNX_CMD_FLAG_NONBLOCK) {
+ kfree(cmd);
+ } else {
+ if (ECRNX_CMD_WAIT_COMPLETE(cmd->flags)) {
+ cmd->result = 0;
+ complete(&cmd->complete);
+ }
+ }
+}
+
+/**
+ *
+ */
+static int cmd_mgr_queue(struct ecrnx_cmd_mgr *cmd_mgr, struct ecrnx_cmd *cmd)
+{
+ struct ecrnx_hw *ecrnx_hw = container_of(cmd_mgr, struct ecrnx_hw, cmd_mgr);
+ bool defer_push = false;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+ trace_msg_send(cmd->id);
+
+ spin_lock_bh(&cmd_mgr->lock);
+
+ if (cmd_mgr->state == ECRNX_CMD_MGR_STATE_CRASHED) {
+ ECRNX_PRINT(KERN_CRIT"cmd queue crashed\n");
+ cmd->result = -EPIPE;
+ spin_unlock_bh(&cmd_mgr->lock);
+ return -EPIPE;
+ }
+
+ #ifndef CONFIG_ECRNX_FHOST
+ if (!list_empty(&cmd_mgr->cmds)) {
+ struct ecrnx_cmd *last;
+
+ if (cmd_mgr->queue_sz == cmd_mgr->max_queue_sz) {
+ ECRNX_ERR(KERN_CRIT"Too many cmds (%d) already queued\n",
+ cmd_mgr->max_queue_sz);
+ cmd->result = -ENOMEM;
+ spin_unlock_bh(&cmd_mgr->lock);
+ return -ENOMEM;
+ }
+ last = list_entry(cmd_mgr->cmds.prev, struct ecrnx_cmd, list);
+ if (last->flags & (ECRNX_CMD_FLAG_WAIT_ACK | ECRNX_CMD_FLAG_WAIT_PUSH)) {
+#if 0 // queue even NONBLOCK command.
+ if (cmd->flags & ECRNX_CMD_FLAG_NONBLOCK) {
+ printk(KERN_CRIT"cmd queue busy\n");
+ cmd->result = -EBUSY;
+ spin_unlock_bh(&cmd_mgr->lock);
+ return -EBUSY;
+ }
+#endif
+ cmd->flags |= ECRNX_CMD_FLAG_WAIT_PUSH;
+ defer_push = true;
+ }
+ }
+ #endif
+
+ cmd->flags |= ECRNX_CMD_FLAG_WAIT_ACK;
+ if (cmd->flags & ECRNX_CMD_FLAG_REQ_CFM)
+ cmd->flags |= ECRNX_CMD_FLAG_WAIT_CFM;
+
+ cmd->tkn = cmd_mgr->next_tkn++;
+ cmd->result = -EINTR;
+
+ if (!(cmd->flags & ECRNX_CMD_FLAG_NONBLOCK))
+ init_completion(&cmd->complete);
+
+ list_add_tail(&cmd->list, &cmd_mgr->cmds);
+ cmd_mgr->queue_sz++;
+ spin_unlock_bh(&cmd_mgr->lock);
+
+ if (!defer_push) {
+ ecrnx_ipc_msg_push(ecrnx_hw, cmd, ECRNX_CMD_A2EMSG_LEN(cmd->a2e_msg));
+ kfree(cmd->a2e_msg);
+ }
+
+ if (!(cmd->flags & ECRNX_CMD_FLAG_NONBLOCK)) {
+ #ifdef CONFIG_ECRNX_FHOST
+ if (wait_for_completion_killable(&cmd->complete)) {
+ if (cmd->flags & ECRNX_CMD_FLAG_WAIT_ACK)
+ up(&ecrnx_hw->term.fw_cmd);
+ cmd->result = -EINTR;
+ spin_lock_bh(&cmd_mgr->lock);
+ cmd_complete(cmd_mgr, cmd);
+ spin_unlock_bh(&cmd_mgr->lock);
+ /* TODO: kill the cmd at fw level */
+ } else {
+ if (cmd->flags & ECRNX_CMD_FLAG_WAIT_ACK)
+ up(&ecrnx_hw->term.fw_cmd);
+ }
+ #else
+ unsigned long tout = msecs_to_jiffies(ECRNX_80211_CMD_TIMEOUT_MS * cmd_mgr->queue_sz);
+ if (!wait_for_completion_killable_timeout(&cmd->complete, tout)) {
+ ECRNX_PRINT("cmd timed-out queue_sz:%d\n",cmd_mgr->queue_sz);
+ cmd_dump(cmd);
+ spin_lock_bh(&cmd_mgr->lock);
+ //cmd_mgr->state = ECRNX_CMD_MGR_STATE_CRASHED;
+ if (!(cmd->flags & ECRNX_CMD_FLAG_DONE)) {
+ cmd->result = -ETIMEDOUT;
+ cmd_complete(cmd_mgr, cmd);
+ }
+ spin_unlock_bh(&cmd_mgr->lock);
+ }
+ #endif
+ } else {
+ cmd->result = 0;
+ }
+
+ return 0;
+}
+
+/**
+ *
+ */
+static int cmd_mgr_llind(struct ecrnx_cmd_mgr *cmd_mgr, struct ecrnx_cmd *cmd)
+{
+ struct ecrnx_cmd *cur, *acked = NULL, *next = NULL;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ spin_lock(&cmd_mgr->lock);
+ list_for_each_entry(cur, &cmd_mgr->cmds, list) {
+ if (!acked) {
+ if (cur->tkn == cmd->tkn) {
+ if (WARN_ON_ONCE(cur != cmd)) {
+ cmd_dump(cmd);
+ }
+ acked = cur;
+ continue;
+ }
+ }
+ if (cur->flags & ECRNX_CMD_FLAG_WAIT_PUSH) {
+ next = cur;
+ break;
+ }
+ }
+ if (!acked) {
+ ECRNX_PRINT(KERN_CRIT "Error: acked cmd not found\n");
+ } else {
+ cmd->flags &= ~ECRNX_CMD_FLAG_WAIT_ACK;
+ if (ECRNX_CMD_WAIT_COMPLETE(cmd->flags))
+ cmd_complete(cmd_mgr, cmd);
+ }
+ if (next) {
+ struct ecrnx_hw *ecrnx_hw = container_of(cmd_mgr, struct ecrnx_hw, cmd_mgr);
+ next->flags &= ~ECRNX_CMD_FLAG_WAIT_PUSH;
+ ecrnx_ipc_msg_push(ecrnx_hw, next, ECRNX_CMD_A2EMSG_LEN(next->a2e_msg));
+ kfree(next->a2e_msg);
+ }
+ spin_unlock(&cmd_mgr->lock);
+
+ return 0;
+}
+
+
+
+static int cmd_mgr_run_callback(struct ecrnx_hw *ecrnx_hw, struct ecrnx_cmd *cmd,
+ struct ecrnx_cmd_e2amsg *msg, msg_cb_fct cb)
+{
+ int res;
+
+ if (! cb)
+ return 0;
+
+ spin_lock(&ecrnx_hw->cb_lock);
+ res = cb(ecrnx_hw, cmd, msg);
+ spin_unlock(&ecrnx_hw->cb_lock);
+
+ return res;
+}
+
+/**
+ *
+
+ */
+static int cmd_mgr_msgind(struct ecrnx_cmd_mgr *cmd_mgr, struct ecrnx_cmd_e2amsg *msg,
+ msg_cb_fct cb)
+{
+ struct ecrnx_hw *ecrnx_hw = container_of(cmd_mgr, struct ecrnx_hw, cmd_mgr);
+ struct ecrnx_cmd *cmd;
+ bool found = false;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+ trace_msg_recv(msg->id);
+
+ spin_lock(&cmd_mgr->lock);
+ list_for_each_entry(cmd, &cmd_mgr->cmds, list) {
+ if (cmd->reqid == msg->id &&
+ (cmd->flags & ECRNX_CMD_FLAG_WAIT_CFM)) {
+
+ if (!cmd_mgr_run_callback(ecrnx_hw, cmd, msg, cb)) {
+ found = true;
+ cmd->flags &= ~ECRNX_CMD_FLAG_WAIT_CFM;
+
+ if (WARN((msg->param_len > ECRNX_CMD_E2AMSG_LEN_MAX),
+ "Unexpect E2A msg len %d > %d\n", msg->param_len,
+ ECRNX_CMD_E2AMSG_LEN_MAX)) {
+ msg->param_len = ECRNX_CMD_E2AMSG_LEN_MAX;
+ }
+
+ if (cmd->e2a_msg && msg->param_len)
+ memcpy(cmd->e2a_msg, &msg->param, msg->param_len);
+
+ if (ECRNX_CMD_WAIT_COMPLETE(cmd->flags))
+ cmd_complete(cmd_mgr, cmd);
+
+ break;
+ }
+ }
+ }
+ spin_unlock(&cmd_mgr->lock);
+
+ if (!found)
+ cmd_mgr_run_callback(ecrnx_hw, NULL, msg, cb);
+
+ ECRNX_DBG("%s exit!! \n", __func__);
+ return 0;
+}
+
+/**
+ *
+ */
+static void cmd_mgr_print(struct ecrnx_cmd_mgr *cmd_mgr)
+{
+ struct ecrnx_cmd *cur;
+
+ spin_lock_bh(&cmd_mgr->lock);
+ ECRNX_PRINT("q_sz/max: %2d / %2d - next tkn: %d\n",
+ cmd_mgr->queue_sz, cmd_mgr->max_queue_sz,
+ cmd_mgr->next_tkn);
+ list_for_each_entry(cur, &cmd_mgr->cmds, list) {
+ cmd_dump(cur);
+ }
+ spin_unlock_bh(&cmd_mgr->lock);
+}
+
+/**
+ *
+ */
+static void cmd_mgr_drain(struct ecrnx_cmd_mgr *cmd_mgr)
+{
+ struct ecrnx_cmd *cur, *nxt;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ spin_lock_bh(&cmd_mgr->lock);
+ list_for_each_entry_safe(cur, nxt, &cmd_mgr->cmds, list) {
+ list_del(&cur->list);
+ cmd_mgr->queue_sz--;
+ if (!(cur->flags & ECRNX_CMD_FLAG_NONBLOCK))
+ complete(&cur->complete);
+ }
+ spin_unlock_bh(&cmd_mgr->lock);
+}
+
+/**
+ *
+ */
+void ecrnx_cmd_mgr_init(struct ecrnx_cmd_mgr *cmd_mgr)
+{
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ INIT_LIST_HEAD(&cmd_mgr->cmds);
+ spin_lock_init(&cmd_mgr->lock);
+ cmd_mgr->max_queue_sz = ECRNX_CMD_MAX_QUEUED;
+ cmd_mgr->queue = &cmd_mgr_queue;
+ cmd_mgr->print = &cmd_mgr_print;
+ cmd_mgr->drain = &cmd_mgr_drain;
+ cmd_mgr->llind = &cmd_mgr_llind;
+ cmd_mgr->msgind = &cmd_mgr_msgind;
+}
+
+/**
+ *
+ */
+void ecrnx_cmd_mgr_deinit(struct ecrnx_cmd_mgr *cmd_mgr)
+{
+ cmd_mgr->print(cmd_mgr);
+ cmd_mgr->drain(cmd_mgr);
+ memset(cmd_mgr, 0, sizeof(*cmd_mgr));
+}
diff --git a/drivers/net/wireless/eswin/ecrnx_cmds.h b/drivers/net/wireless/eswin/ecrnx_cmds.h
new file mode 100644
index 000000000000..58533fc08d40
--- /dev/null
+++ b/drivers/net/wireless/eswin/ecrnx_cmds.h
@@ -0,0 +1,102 @@
+/**
+ ******************************************************************************
+ *
+ * ecrnx_cmds.h
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+
+#ifndef _ECRNX_CMDS_H_
+#define _ECRNX_CMDS_H_
+
+#include <linux/spinlock.h>
+#include <linux/completion.h>
+#include "lmac_msg.h"
+
+#ifdef CONFIG_ECRNX_SDM
+#define ECRNX_80211_CMD_TIMEOUT_MS (20 * 300)
+#elif defined(CONFIG_ECRNX_FHOST)
+#define ECRNX_80211_CMD_TIMEOUT_MS (10000)
+#else
+#define ECRNX_80211_CMD_TIMEOUT_MS (20 * 300) //300
+#endif
+
+#define ECRNX_CMD_FLAG_NONBLOCK BIT(0)
+#define ECRNX_CMD_FLAG_REQ_CFM BIT(1)
+#define ECRNX_CMD_FLAG_WAIT_PUSH BIT(2)
+#define ECRNX_CMD_FLAG_WAIT_ACK BIT(3)
+#define ECRNX_CMD_FLAG_WAIT_CFM BIT(4)
+#define ECRNX_CMD_FLAG_DONE BIT(5)
+/* ATM IPC design makes it possible to get the CFM before the ACK,
+ * otherwise this could have simply been a state enum */
+#define ECRNX_CMD_WAIT_COMPLETE(flags) \
+ (!(flags & (ECRNX_CMD_FLAG_WAIT_ACK | ECRNX_CMD_FLAG_WAIT_CFM)))
+
+#define ECRNX_CMD_MAX_QUEUED 8
+
+#ifdef CONFIG_ECRNX_FHOST
+#include "ipc_fhost.h"
+#define ecrnx_cmd_e2amsg ipc_fhost_msg
+#define ecrnx_cmd_a2emsg ipc_fhost_msg
+#define ECRNX_CMD_A2EMSG_LEN(m) (m->param_len)
+#define ECRNX_CMD_E2AMSG_LEN_MAX IPC_FHOST_MSG_BUF_SIZE
+struct ecrnx_term_stream;
+
+#else /* !CONFIG_ECRNX_FHOST*/
+#include "ipc_shared.h"
+#define ecrnx_cmd_e2amsg ipc_e2a_msg
+#define ecrnx_cmd_a2emsg lmac_msg
+#define ECRNX_CMD_A2EMSG_LEN(m) (sizeof(struct lmac_msg) + m->param_len)
+#define ECRNX_CMD_E2AMSG_LEN_MAX (IPC_E2A_MSG_PARAM_SIZE * 4)
+
+#endif /* CONFIG_ECRNX_FHOST*/
+
+struct ecrnx_hw;
+struct ecrnx_cmd;
+typedef int (*msg_cb_fct)(struct ecrnx_hw *ecrnx_hw, struct ecrnx_cmd *cmd,
+ struct ecrnx_cmd_e2amsg *msg);
+
+enum ecrnx_cmd_mgr_state {
+ ECRNX_CMD_MGR_STATE_DEINIT,
+ ECRNX_CMD_MGR_STATE_INITED,
+ ECRNX_CMD_MGR_STATE_CRASHED,
+};
+
+struct ecrnx_cmd {
+ struct list_head list;
+ lmac_msg_id_t id;
+ lmac_msg_id_t reqid;
+ struct ecrnx_cmd_a2emsg *a2e_msg;
+ char *e2a_msg;
+ u32 tkn;
+ u16 flags;
+
+ struct completion complete;
+ u32 result;
+ #ifdef CONFIG_ECRNX_FHOST
+ struct ecrnx_term_stream *stream;
+ #endif
+};
+
+struct ecrnx_cmd_mgr {
+ enum ecrnx_cmd_mgr_state state;
+ spinlock_t lock;
+ u32 next_tkn;
+ u32 queue_sz;
+ u32 max_queue_sz;
+
+ struct list_head cmds;
+
+ int (*queue)(struct ecrnx_cmd_mgr *, struct ecrnx_cmd *);
+ int (*llind)(struct ecrnx_cmd_mgr *, struct ecrnx_cmd *);
+ int (*msgind)(struct ecrnx_cmd_mgr *, struct ecrnx_cmd_e2amsg *, msg_cb_fct);
+ void (*print)(struct ecrnx_cmd_mgr *);
+ void (*drain)(struct ecrnx_cmd_mgr *);
+};
+
+void ecrnx_cmd_mgr_init(struct ecrnx_cmd_mgr *cmd_mgr);
+void ecrnx_cmd_mgr_deinit(struct ecrnx_cmd_mgr *cmd_mgr);
+
+#endif /* _ECRNX_CMDS_H_ */
diff --git a/drivers/net/wireless/eswin/ecrnx_compat.h b/drivers/net/wireless/eswin/ecrnx_compat.h
new file mode 100644
index 000000000000..e477d378ef0b
--- /dev/null
+++ b/drivers/net/wireless/eswin/ecrnx_compat.h
@@ -0,0 +1,529 @@
+/**
+ ******************************************************************************
+ *
+ * @file ecrnx_compat.h
+ *
+ * Ensure driver compilation for linux 4.4 to 5.9
+ *
+ * To avoid too many #if LINUX_VERSION_CODE if the code, when prototype change
+ * between different kernel version:
+ * - For external function, define a macro whose name is the function name with
+ * _compat suffix and prototype (actually the number of parameter) of the
+ * latest version. Then latest version this macro simply call the function
+ * and for older kernel version it call the function adapting the api.
+ * - For internal function (e.g. cfg80211_ops) do the same but the macro name
+ * doesn't need to have the _compat suffix when the function is not used
+ * directly by the driver
+ *
+ * Copyright (C) ESWIN 2020
+ *
+ ******************************************************************************
+ */
+#ifndef _ECRNX_COMPAT_H_
+#define _ECRNX_COMPAT_H_
+#include <linux/version.h>
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 8, 0)
+#error "Minimum kernel version supported is 3.8"
+#endif
+
+/******************************************************************************
+ * Generic
+ *****************************************************************************/
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 9, 0)
+#define __bf_shf(x) (__builtin_ffsll(x) - 1)
+#define FIELD_PREP(_mask, _val) \
+ (((typeof(_mask))(_val) << __bf_shf(_mask)) & (_mask))
+#else
+#include <linux/bitfield.h>
+#endif // 4.9
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(5,13,0)
+#define IEEE80211_HE_PHY_CAP3_RX_HE_MU_PPDU_FROM_NON_AP_STA IEEE80211_HE_PHY_CAP3_RX_PARTIAL_BW_SU_IN_20MHZ_MU
+#endif
+
+/******************************************************************************
+ * CFG80211
+ *****************************************************************************/
+
+ #if LINUX_VERSION_CODE > KERNEL_VERSION(5, 12, 0)
+#define regulatory_set_wiphy_regd_sync_rtnl regulatory_set_wiphy_regd_sync
+#endif
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(6, 1, 0)
+#define cfg80211_ch_switch_started_notify(dev, chandef, count) \
+ cfg80211_ch_switch_started_notify(dev, chandef, count, 1, false, 0)
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 1, 0)
+#define WLAN_EXT_CAPA3_MULTI_BSSID_SUPPORT 0
+
+#define IEEE80211_HE_PHY_CAP8_DCM_MAX_RU_242 0x00
+#define IEEE80211_HE_PHY_CAP8_DCM_MAX_RU_484 0x40
+#define IEEE80211_HE_PHY_CAP8_DCM_MAX_RU_996 0x80
+#define IEEE80211_HE_PHY_CAP8_DCM_MAX_RU_2x996 0xc0
+#define IEEE80211_HE_PHY_CAP8_DCM_MAX_RU_MASK 0xc0
+
+#define IEEE80211_HE_PHY_CAP9_NOMIMAL_PKT_PADDING_0US 0x00
+#define IEEE80211_HE_PHY_CAP9_NOMIMAL_PKT_PADDING_8US 0x40
+#define IEEE80211_HE_PHY_CAP9_NOMIMAL_PKT_PADDING_16US 0x80
+#define IEEE80211_HE_PHY_CAP9_NOMIMAL_PKT_PADDING_RESERVED 0xc0
+#define IEEE80211_HE_PHY_CAP9_NOMIMAL_PKT_PADDING_MASK 0xc0
+#else
+#define IEEE80211_HE_PHY_CAP9_NOMIMAL_PKT_PADDING_0US 0x00
+#define IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_8US 0x1
+#define IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_16US 0x2
+#define IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_RESERVED 0x3
+#endif // 5.1
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 0, 0)
+#define cfg80211_notify_new_peer_candidate(dev, addr, ie, ie_len, sig_dbm, gfp) \
+ cfg80211_notify_new_peer_candidate(dev, addr, ie, ie_len, gfp)
+
+#define WLAN_EXT_CAPA10_TWT_REQUESTER_SUPPORT BIT(5)
+#define WLAN_EXT_CAPA10_TWT_RESPONDER_SUPPORT BIT(6)
+
+#endif // 5.0
+
+#define WLAN_EXT_CAPA5_QOS_MAP_SUPPORT BIT(0)
+
+
+struct ecrnx_element {
+ u8 id;
+ u8 datalen;
+ u8 data[];
+} __packed;
+
+
+#define for_each_ecrnx_element(_elem, _data, _datalen) \
+ for (_elem = (const struct ecrnx_element *)(_data); \
+ (const u8 *)(_data) + (_datalen) - (const u8 *)_elem >= \
+ (int)sizeof(*_elem) && \
+ (const u8 *)(_data) + (_datalen) - (const u8 *)_elem >= \
+ (int)sizeof(*_elem) + _elem->datalen; \
+ _elem = (const struct ecrnx_element *)(_elem->data + _elem->datalen))
+
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 19, 0)
+#define IEEE80211_RADIOTAP_HE 23
+#define IEEE80211_RADIOTAP_HE_MU 24
+#endif
+
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4,19,0)
+struct ieee80211_radiotap_he {
+ __le16 data1, data2, data3, data4, data5, data6;
+};
+
+enum ieee80211_radiotap_he_bits {
+ IEEE80211_RADIOTAP_HE_DATA1_FORMAT_MASK = 3,
+ IEEE80211_RADIOTAP_HE_DATA1_FORMAT_SU = 0,
+ IEEE80211_RADIOTAP_HE_DATA1_FORMAT_EXT_SU = 1,
+ IEEE80211_RADIOTAP_HE_DATA1_FORMAT_MU = 2,
+ IEEE80211_RADIOTAP_HE_DATA1_FORMAT_TRIG = 3,
+
+ IEEE80211_RADIOTAP_HE_DATA1_BSS_COLOR_KNOWN = 0x0004,
+ IEEE80211_RADIOTAP_HE_DATA1_BEAM_CHANGE_KNOWN = 0x0008,
+ IEEE80211_RADIOTAP_HE_DATA1_UL_DL_KNOWN = 0x0010,
+ IEEE80211_RADIOTAP_HE_DATA1_DATA_MCS_KNOWN = 0x0020,
+ IEEE80211_RADIOTAP_HE_DATA1_DATA_DCM_KNOWN = 0x0040,
+ IEEE80211_RADIOTAP_HE_DATA1_CODING_KNOWN = 0x0080,
+ IEEE80211_RADIOTAP_HE_DATA1_LDPC_XSYMSEG_KNOWN = 0x0100,
+ IEEE80211_RADIOTAP_HE_DATA1_STBC_KNOWN = 0x0200,
+ IEEE80211_RADIOTAP_HE_DATA1_SPTL_REUSE_KNOWN = 0x0400,
+ IEEE80211_RADIOTAP_HE_DATA1_SPTL_REUSE2_KNOWN = 0x0800,
+ IEEE80211_RADIOTAP_HE_DATA1_SPTL_REUSE3_KNOWN = 0x1000,
+ IEEE80211_RADIOTAP_HE_DATA1_SPTL_REUSE4_KNOWN = 0x2000,
+ IEEE80211_RADIOTAP_HE_DATA1_BW_RU_ALLOC_KNOWN = 0x4000,
+ IEEE80211_RADIOTAP_HE_DATA1_DOPPLER_KNOWN = 0x8000,
+
+ IEEE80211_RADIOTAP_HE_DATA2_PRISEC_80_KNOWN = 0x0001,
+ IEEE80211_RADIOTAP_HE_DATA2_GI_KNOWN = 0x0002,
+ IEEE80211_RADIOTAP_HE_DATA2_NUM_LTF_SYMS_KNOWN = 0x0004,
+ IEEE80211_RADIOTAP_HE_DATA2_PRE_FEC_PAD_KNOWN = 0x0008,
+ IEEE80211_RADIOTAP_HE_DATA2_TXBF_KNOWN = 0x0010,
+ IEEE80211_RADIOTAP_HE_DATA2_PE_DISAMBIG_KNOWN = 0x0020,
+ IEEE80211_RADIOTAP_HE_DATA2_TXOP_KNOWN = 0x0040,
+ IEEE80211_RADIOTAP_HE_DATA2_MIDAMBLE_KNOWN = 0x0080,
+ IEEE80211_RADIOTAP_HE_DATA2_RU_OFFSET = 0x3f00,
+ IEEE80211_RADIOTAP_HE_DATA2_RU_OFFSET_KNOWN = 0x4000,
+ IEEE80211_RADIOTAP_HE_DATA2_PRISEC_80_SEC = 0x8000,
+
+ IEEE80211_RADIOTAP_HE_DATA3_BSS_COLOR = 0x003f,
+ IEEE80211_RADIOTAP_HE_DATA3_BEAM_CHANGE = 0x0040,
+ IEEE80211_RADIOTAP_HE_DATA3_UL_DL = 0x0080,
+ IEEE80211_RADIOTAP_HE_DATA3_DATA_MCS = 0x0f00,
+ IEEE80211_RADIOTAP_HE_DATA3_DATA_DCM = 0x1000,
+ IEEE80211_RADIOTAP_HE_DATA3_CODING = 0x2000,
+ IEEE80211_RADIOTAP_HE_DATA3_LDPC_XSYMSEG = 0x4000,
+ IEEE80211_RADIOTAP_HE_DATA3_STBC = 0x8000,
+
+ IEEE80211_RADIOTAP_HE_DATA4_SU_MU_SPTL_REUSE = 0x000f,
+ IEEE80211_RADIOTAP_HE_DATA4_MU_STA_ID = 0x7ff0,
+ IEEE80211_RADIOTAP_HE_DATA4_TB_SPTL_REUSE1 = 0x000f,
+ IEEE80211_RADIOTAP_HE_DATA4_TB_SPTL_REUSE2 = 0x00f0,
+ IEEE80211_RADIOTAP_HE_DATA4_TB_SPTL_REUSE3 = 0x0f00,
+ IEEE80211_RADIOTAP_HE_DATA4_TB_SPTL_REUSE4 = 0xf000,
+
+ IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC = 0x000f,
+ IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_20MHZ = 0,
+ IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_40MHZ = 1,
+ IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_80MHZ = 2,
+ IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_160MHZ = 3,
+ IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_26T = 4,
+ IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_52T = 5,
+ IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_106T = 6,
+ IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_242T = 7,
+ IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_484T = 8,
+ IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_996T = 9,
+ IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_2x996T = 10,
+
+ IEEE80211_RADIOTAP_HE_DATA5_GI = 0x0030,
+ IEEE80211_RADIOTAP_HE_DATA5_GI_0_8 = 0,
+ IEEE80211_RADIOTAP_HE_DATA5_GI_1_6 = 1,
+ IEEE80211_RADIOTAP_HE_DATA5_GI_3_2 = 2,
+
+ IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE = 0x00c0,
+ IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE_UNKNOWN = 0,
+ IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE_1X = 1,
+ IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE_2X = 2,
+ IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE_4X = 3,
+ IEEE80211_RADIOTAP_HE_DATA5_NUM_LTF_SYMS = 0x0700,
+ IEEE80211_RADIOTAP_HE_DATA5_PRE_FEC_PAD = 0x3000,
+ IEEE80211_RADIOTAP_HE_DATA5_TXBF = 0x4000,
+ IEEE80211_RADIOTAP_HE_DATA5_PE_DISAMBIG = 0x8000,
+
+ IEEE80211_RADIOTAP_HE_DATA6_NSTS = 0x000f,
+ IEEE80211_RADIOTAP_HE_DATA6_DOPPLER = 0x0010,
+ IEEE80211_RADIOTAP_HE_DATA6_TXOP = 0x7f00,
+ IEEE80211_RADIOTAP_HE_DATA6_MIDAMBLE_PDCTY = 0x8000,
+ };
+
+struct ieee80211_radiotap_he_mu {
+ __le16 flags1, flags2;
+ u8 ru_ch1[4];
+ u8 ru_ch2[4];
+};
+
+enum ieee80211_radiotap_he_mu_bits {
+ IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_MCS = 0x000f,
+ IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_MCS_KNOWN = 0x0010,
+ IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_DCM = 0x0020,
+ IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_DCM_KNOWN = 0x0040,
+ IEEE80211_RADIOTAP_HE_MU_FLAGS1_CH2_CTR_26T_RU_KNOWN = 0x0080,
+ IEEE80211_RADIOTAP_HE_MU_FLAGS1_CH1_RU_KNOWN = 0x0100,
+ IEEE80211_RADIOTAP_HE_MU_FLAGS1_CH2_RU_KNOWN = 0x0200,
+ IEEE80211_RADIOTAP_HE_MU_FLAGS1_CH1_CTR_26T_RU_KNOWN = 0x1000,
+ IEEE80211_RADIOTAP_HE_MU_FLAGS1_CH1_CTR_26T_RU = 0x2000,
+ IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_COMP_KNOWN = 0x4000,
+ IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_SYMS_USERS_KNOWN = 0x8000,
+
+ IEEE80211_RADIOTAP_HE_MU_FLAGS2_BW_FROM_SIG_A_BW = 0x0003,
+ IEEE80211_RADIOTAP_HE_MU_FLAGS2_BW_FROM_SIG_A_BW_20MHZ = 0x0000,
+ IEEE80211_RADIOTAP_HE_MU_FLAGS2_BW_FROM_SIG_A_BW_40MHZ = 0x0001,
+ IEEE80211_RADIOTAP_HE_MU_FLAGS2_BW_FROM_SIG_A_BW_80MHZ = 0x0002,
+ IEEE80211_RADIOTAP_HE_MU_FLAGS2_BW_FROM_SIG_A_BW_160MHZ = 0x0003,
+ IEEE80211_RADIOTAP_HE_MU_FLAGS2_BW_FROM_SIG_A_BW_KNOWN = 0x0004,
+ IEEE80211_RADIOTAP_HE_MU_FLAGS2_SIG_B_COMP = 0x0008,
+ IEEE80211_RADIOTAP_HE_MU_FLAGS2_SIG_B_SYMS_USERS = 0x00f0,
+ IEEE80211_RADIOTAP_HE_MU_FLAGS2_PUNC_FROM_SIG_A_BW = 0x0300,
+ IEEE80211_RADIOTAP_HE_MU_FLAGS2_PUNC_FROM_SIG_A_BW_KNOWN= 0x0400,
+ IEEE80211_RADIOTAP_HE_MU_FLAGS2_CH2_CTR_26T_RU = 0x0800,
+};
+
+enum {
+ IEEE80211_HE_MCS_SUPPORT_0_7 = 0,
+ IEEE80211_HE_MCS_SUPPORT_0_9 = 1,
+ IEEE80211_HE_MCS_SUPPORT_0_11 = 2,
+ IEEE80211_HE_MCS_NOT_SUPPORTED = 3,
+};
+#endif // 4.19
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 17, 0)
+#define cfg80211_probe_status(ndev, addr, cookie, ack, ack_pwr, pwr_valid, gfp) \
+ cfg80211_probe_status(ndev, addr, cookie, ack, gfp)
+#endif // 4.17
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 2, 0)
+#define cfg80211_disconnected(dev, reason, ie, len, local, gfp) \
+ cfg80211_disconnected(dev, reason, ie, len, gfp)
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0)
+#define ieee80211_chandef_to_operating_class(chan_def, op_class) 0
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0)
+#define ecrnx_cfg80211_add_iface(wiphy, name, name_assign_type, type, params) \
+ ecrnx_cfg80211_add_iface(wiphy, name, name_assign_type, type, u32 *flags, params)
+
+#define ecrnx_cfg80211_change_iface(wiphy, dev, type, params) \
+ ecrnx_cfg80211_change_iface(wiphy, dev, type, u32 *flags, params)
+
+#define CCFS0(vht) vht->center_freq_seg1_idx
+#define CCFS1(vht) vht->center_freq_seg2_idx
+
+#if 0
+#define nla_parse(tb, maxtype, head, len, policy, extack) \
+ nla_parse(tb, maxtype, head, len, policy)
+#endif
+
+struct cfg80211_roam_info {
+ struct ieee80211_channel *channel;
+ struct cfg80211_bss *bss;
+ const u8 *bssid;
+ const u8 *req_ie;
+ size_t req_ie_len;
+ const u8 *resp_ie;
+ size_t resp_ie_len;
+};
+
+#define cfg80211_roamed(_dev, _info, _gfp) \
+ cfg80211_roamed(_dev, (_info)->channel, (_info)->bssid, (_info)->req_ie, \
+ (_info)->req_ie_len, (_info)->resp_ie, (_info)->resp_ie_len, _gfp)
+
+#else // 4.12
+
+#define CCFS0(vht) vht->center_freq_seg0_idx
+#define CCFS1(vht) vht->center_freq_seg1_idx
+#endif // 4.12
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0)
+#define ecrnx_cfg80211_cqm_rssi_notify(dev, event, level, gfp) \
+ cfg80211_cqm_rssi_notify(dev, event, gfp)
+#else
+#define ecrnx_cfg80211_cqm_rssi_notify(dev, event, level, gfp) \
+ cfg80211_cqm_rssi_notify(dev, event, level, gfp)
+#endif // 4.11
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 9, 0)
+#define ieee80211_amsdu_to_8023s(skb, list, addr, iftype, extra_headroom, check_da, check_sa) \
+ ieee80211_amsdu_to_8023s(skb, list, addr, iftype, extra_headroom, false)
+#endif // 4.9
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 7, 0)
+#define NUM_NL80211_BANDS IEEE80211_NUM_BANDS
+#endif // 4.7
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0)
+#define SURVEY_INFO_TIME SURVEY_INFO_CHANNEL_TIME
+#define SURVEY_INFO_TIME_BUSY SURVEY_INFO_CHANNEL_TIME_BUSY
+#define SURVEY_INFO_TIME_EXT_BUSY SURVEY_INFO_CHANNEL_TIME_EXT_BUSY
+#define SURVEY_INFO_TIME_RX SURVEY_INFO_CHANNEL_TIME_RX
+#define SURVEY_INFO_TIME_TX SURVEY_INFO_CHANNEL_TIME_TX
+
+#define SURVEY_TIME(s) s->channel_time
+#define SURVEY_TIME_BUSY(s) s->channel_time_busy
+#else
+#define SURVEY_TIME(s) s->time
+#define SURVEY_TIME_BUSY(s) s->time_busy
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 19, 0)
+#define cfg80211_ch_switch_started_notify(dev, chandef, count)
+
+#define WLAN_BSS_COEX_INFORMATION_REQUEST BIT(0)
+#define WLAN_EXT_CAPA1_EXT_CHANNEL_SWITCHING BIT(2)
+#define WLAN_EXT_CAPA4_TDLS_BUFFER_STA BIT(4)
+#define WLAN_EXT_CAPA4_TDLS_PEER_PSM BIT(5)
+#define WLAN_EXT_CAPA4_TDLS_CHAN_SWITCH BIT(6)
+#define WLAN_EXT_CAPA5_TDLS_CH_SW_PROHIBITED BIT(7)
+#define NL80211_FEATURE_TDLS_CHANNEL_SWITCH 0
+
+#define STA_TDLS_INITIATOR(sta) 0
+
+#define REGULATORY_IGNORE_STALE_KICKOFF 0
+#else
+#define STA_TDLS_INITIATOR(sta) sta->tdls_initiator
+#endif
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 18, 0)) && (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0))
+#define cfg80211_rx_mgmt(wdev, freq, rssi, buf, len, flags) \
+ cfg80211_rx_mgmt(wdev, freq, rssi, buf, len, flags, GFP_ATOMIC)
+#elif LINUX_VERSION_CODE < KERNEL_VERSION(3, 11, 0)
+#define cfg80211_rx_mgmt(wdev, freq, rssi, buf, len, flags) \
+ cfg80211_rx_mgmt(wdev, freq, rssi, buf, len, GFP_ATOMIC)
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0)
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 15, 0)
+#define rwnx_cfg80211_tdls_mgmt(wiphy, dev, peer, act, tok, status, peer_capability, initiator, buf, len) \
+ rwnx_cfg80211_tdls_mgmt(wiphy, dev, peer, act, tok, status, peer_capability, buf, len)
+#else
+#define rwnx_cfg80211_tdls_mgmt(wiphy, dev, peer, act, tok, status, peer_capability, initiator, buf, len) \
+ rwnx_cfg80211_tdls_mgmt(wiphy, dev, peer, act, tok, status, buf, len)
+#endif
+
+#include <linux/types.h>
+
+struct ieee80211_wmm_ac_param {
+ u8 aci_aifsn; /* AIFSN, ACM, ACI */
+ u8 cw; /* ECWmin, ECWmax (CW = 2^ECW - 1) */
+ __le16 txop_limit;
+} __packed;
+
+struct ieee80211_wmm_param_ie {
+ u8 element_id; /* Element ID: 221 (0xdd); */
+ u8 len; /* Length: 24 */
+ /* required fields for WMM version 1 */
+ u8 oui[3]; /* 00:50:f2 */
+ u8 oui_type; /* 2 */
+ u8 oui_subtype; /* 1 */
+ u8 version; /* 1 for WMM version 1.0 */
+ u8 qos_info; /* AP/STA specific QoS info */
+ u8 reserved; /* 0 */
+ /* AC_BE, AC_BK, AC_VI, AC_VO */
+ struct ieee80211_wmm_ac_param ac[4];
+} __packed;
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 10, 0)
+/**
+ * struct cfg80211_update_ft_ies_params - FT IE Information
+ *
+ * This structure provides information needed to update the fast transition IE
+ *
+ * @md: The Mobility Domain ID, 2 Octet value
+ * @ie: Fast Transition IEs
+ * @ie_len: Length of ft_ie in octets
+ */
+struct cfg80211_update_ft_ies_params {
+ u16 md;
+ const u8 *ie;
+ size_t ie_len;
+};
+#endif
+
+/******************************************************************************
+ * MAC80211
+ *****************************************************************************/
+#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 3, 0)
+#define ecrnx_ops_cancel_remain_on_channel(hw, vif) \
+ ecrnx_ops_cancel_remain_on_channel(hw)
+#endif // 5.3
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 18, 0)
+#define ecrnx_ops_mgd_prepare_tx(hw, vif, duration) \
+ ecrnx_ops_mgd_prepare_tx(hw, vif)
+#endif // 4.18
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0)
+
+#define RX_ENC_HT(s) s->flag |= RX_FLAG_HT
+#define RX_ENC_HT_GF(s) s->flag |= (RX_FLAG_HT | RX_FLAG_HT_GF)
+#define RX_ENC_VHT(s) s->flag |= RX_FLAG_HT
+#define RX_ENC_HE(s) s->flag |= RX_FLAG_HT
+#define RX_ENC_FLAG_SHORT_GI(s) s->flag |= RX_FLAG_SHORT_GI
+#define RX_ENC_FLAG_SHORT_PRE(s) s->flag |= RX_FLAG_SHORTPRE
+#define RX_ENC_FLAG_LDPC(s) s->flag |= RX_FLAG_LDPC
+#define RX_BW_40MHZ(s) s->flag |= RX_FLAG_40MHZ
+#define RX_BW_80MHZ(s) s->vht_flag |= RX_VHT_FLAG_80MHZ
+#define RX_BW_160MHZ(s) s->vht_flag |= RX_VHT_FLAG_160MHZ
+#define RX_NSS(s) s->vht_nss
+
+#else
+#define RX_ENC_HT(s) s->encoding = RX_ENC_HT
+#define RX_ENC_HT_GF(s) { s->encoding = RX_ENC_HT; \
+ s->enc_flags |= RX_ENC_FLAG_HT_GF; }
+#define RX_ENC_VHT(s) s->encoding = RX_ENC_VHT
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 19, 0)
+#define RX_ENC_HE(s) s->encoding = RX_ENC_VHT
+#else
+#define RX_ENC_HE(s) s->encoding = RX_ENC_HE
+#endif
+#define RX_ENC_FLAG_SHORT_GI(s) s->enc_flags |= RX_ENC_FLAG_SHORT_GI
+#define RX_ENC_FLAG_SHORT_PRE(s) s->enc_flags |= RX_ENC_FLAG_SHORTPRE
+#define RX_ENC_FLAG_LDPC(s) s->enc_flags |= RX_ENC_FLAG_LDPC
+#define RX_BW_40MHZ(s) s->bw = RATE_INFO_BW_40
+#define RX_BW_80MHZ(s) s->bw = RATE_INFO_BW_80
+#define RX_BW_160MHZ(s) s->bw = RATE_INFO_BW_160
+#define RX_NSS(s) s->nss
+
+#endif // 4.12
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0)
+#define ecrnx_ieee80211_cqm_rssi_notify(vif, event, level, gfp) \
+ ieee80211_cqm_rssi_notify(vif, event, gfp)
+#else
+#define ecrnx_ieee80211_cqm_rssi_notify(vif, event, level, gfp) \
+ ieee80211_cqm_rssi_notify(vif, event, level, gfp)
+#endif // 4.11
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 7, 0)
+#define RX_FLAG_MIC_STRIPPED 0
+#endif // 4.7
+
+#ifndef CONFIG_VENDOR_ECRNX_AMSDUS_TX
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 6, 0))
+#define ecrnx_ops_ampdu_action(hw, vif, params) \
+ ecrnx_ops_ampdu_action(hw, vif, enum ieee80211_ampdu_mlme_action action, \
+ struct ieee80211_sta *sta, u16 tid, u16 *ssn, u8 buf_size, \
+ bool amsdu)
+#endif // 4.6
+#endif /* CONFIG_VENDOR_ECRNX_AMSDUS_TX */
+
+/******************************************************************************
+ * NET
+ *****************************************************************************/
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 13, 0)
+#define rwnx_select_queue(dev, skb, sb_dev) \
+ rwnx_select_queue(dev, skb)
+#elif LINUX_VERSION_CODE < KERNEL_VERSION(4, 19, 0)
+#define ecrnx_select_queue(dev, skb, sb_dev) \
+ ecrnx_select_queue(dev, skb, void *accel_priv, select_queue_fallback_t fallback)
+#elif LINUX_VERSION_CODE < KERNEL_VERSION(5, 0, 0)
+#define ecrnx_select_queue(dev, skb, sb_dev) \
+ ecrnx_select_queue(dev, skb, sb_dev, select_queue_fallback_t fallback)
+#endif //3.13
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 16, 0)) && !(defined CONFIG_VENDOR_ECRNX)
+#define sk_pacing_shift_update(sk, shift)
+#endif // 4.16
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0)
+#define alloc_netdev_mqs(size, name, assign, setup, txqs, rxqs) \
+ alloc_netdev_mqs(size, name, setup, txqs, rxqs)
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0)
+#define NET_NAME_UNKNOWN 0
+#endif
+
+/******************************************************************************
+ * TRACE
+ *****************************************************************************/
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 2, 0)
+#define trace_print_symbols_seq ftrace_print_symbols_seq
+#endif // 4.2
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0)
+#define trace_seq_buffer_ptr(p) p->buffer + p->len
+#endif
+
+/******************************************************************************
+ * TIME
+ *****************************************************************************/
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0)
+#define time64_to_tm(t, o, tm) time_to_tm((time_t)t, o, tm)
+#endif // 4.8
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 19, 0)
+#define ktime_get_real_seconds get_seconds
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0)
+typedef __s64 time64_t;
+#endif
+
+/******************************************************************************
+ * timer
+ *****************************************************************************/
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0)
+#define from_timer(var, callback_timer, timer_fieldname) \
+ container_of(callback_timer, typeof(*var), timer_fieldname)
+
+#define timer_setup(timer, callback, flags) \
+ __setup_timer(timer, (void (*)(unsigned long))callback, (unsigned long)timer, flags)
+#endif // 4.14
+
+#endif /* _ECRNX_COMPAT_H_ */
diff --git a/drivers/net/wireless/eswin/ecrnx_debug.c b/drivers/net/wireless/eswin/ecrnx_debug.c
new file mode 100644
index 000000000000..9c2f67eb506d
--- /dev/null
+++ b/drivers/net/wireless/eswin/ecrnx_debug.c
@@ -0,0 +1,46 @@
+/**
+ ****************************************************************************************
+ *
+ * @file ecrnx_debug.c
+ *
+ * @brief ecrnx driver debug functions;
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ****************************************************************************************
+ */
+#include <linux/stdarg.h>
+#include <linux/init.h>
+#include "ecrnx_defs.h"
+#include "eswin_utils.h"
+
+#ifdef CONFIG_ECRNX_DBG_LEVEL
+int ecrnx_dbg_level = CONFIG_ECRNX_DBG_LEVEL; //defined in the 6600u_feature file
+#else
+int ecrnx_dbg_level = DRV_DBG_TYPE_NONE;
+#endif
+
+LOG_CTL_ST log_ctl={
+ .level = 2,
+ .dir = 0,
+};
+
+#ifndef CONFIG_ECRNX_DEBUGFS_CUSTOM
+int ecrnx_fw_log_level_set(u32 level, u32 dir)
+{
+ uint32_t dbg_info[3] = {0};
+
+ dbg_info[0] = 0x01; //SLAVE_LOG_LEVEL
+ dbg_info[1] = level;
+ dbg_info[2] = dir;
+
+ ECRNX_PRINT("%s: fstype:%d, level:%d, dir:%d \n", __func__, dbg_info[0], dbg_info[1], dbg_info[2]);
+ ECRNX_PRINT("info_len:%d \n", sizeof(dbg_info));
+ return host_send(dbg_info, sizeof(dbg_info), TX_FLAG_MSG_DEBUGFS_IE);
+}
+
+#endif
+
+// #endif
+
+
diff --git a/drivers/net/wireless/eswin/ecrnx_debug.h b/drivers/net/wireless/eswin/ecrnx_debug.h
new file mode 100644
index 000000000000..b322487851c0
--- /dev/null
+++ b/drivers/net/wireless/eswin/ecrnx_debug.h
@@ -0,0 +1,106 @@
+/**
+ ****************************************************************************************
+ *
+ * @file ecrnx_debug.h
+ *
+ * @brief ecrnx driver debug structure declarations
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ****************************************************************************************
+ */
+
+#ifndef ECRNX_DEBUG_H_
+#define ECRNX_DEBUG_H_
+
+#ifdef CONFIG_ECRNX_SOFTMAC
+#define FW_STR "lmac"
+#elif defined CONFIG_ECRNX_FULLMAC
+#define FW_STR "fmac"
+#endif
+#if 0
+#ifdef CONFIG_ECRNX_DBG
+/* #define ECRNX_DBG(format, arg...) pr_warn(format, ## arg) */
+#define ECRNX_DBG printk
+#else
+#define ECRNX_DBG(a...) do {} while (0)
+#endif
+#endif
+
+#ifdef CONFIG_ECRNX_DBG
+
+#define ECRNX_FN_ENTRY_STR "%s() enter, line:%d\n", __func__, __LINE__
+#define DBG_PREFIX "[ecrnx] "
+#define DBG_PREFIX_IW_CFM "[ecrnx] iwpriv cfm:"
+#define DBG_PREFIX_PAT "[ecrnx] pattern error:"
+#define DBG_PREFIX_CRC_CHECK "[ecrnx] crc check:"
+#define DBG_PREFIX_SDIO_RX "[ecrnx] sdio rx:"
+#define DBG_PREFIX_SDIO_TX "[ecrnx] sdio tx:"
+
+/* driver log level*/
+enum ECRNX_DRV_DBG_TYEP{
+ DRV_DBG_TYPE_NONE,
+ DRV_DBG_TYPE_ALWAYS,
+ DRV_DBG_TYPE_ERR,
+ DRV_DBG_TYPE_WARNING,
+ DRV_DBG_TYPE_INFO,
+ DRV_DBG_TYPE_DEBUG,
+ DRV_DBG_TYPE_MAX,
+};
+
+#define ECRNX_PRINT(fmt, arg...) \
+ do {\
+ if (DRV_DBG_TYPE_ALWAYS <= ecrnx_dbg_level) {\
+ printk(DBG_PREFIX fmt, ##arg);\
+ } \
+ } while (0)
+
+#define ECRNX_ERR(fmt, arg...) \
+ do {\
+ if (DRV_DBG_TYPE_ERR <= ecrnx_dbg_level) {\
+ printk(DBG_PREFIX " ERROR " fmt, ##arg);\
+ } \
+ } while (0)
+
+#define ECRNX_WARN(fmt, arg...) \
+ do {\
+ if (DRV_DBG_TYPE_WARNING <= ecrnx_dbg_level) {\
+ printk(DBG_PREFIX " WARN " fmt, ##arg);\
+ } \
+ } while (0)
+
+#define ECRNX_INFO(fmt, arg...) \
+ do {\
+ if (DRV_DBG_TYPE_INFO <= ecrnx_dbg_level) {\
+ printk(DBG_PREFIX fmt, ##arg);\
+ } \
+ } while (0)
+
+#define ECRNX_DBG(fmt, arg...) \
+ do {\
+ if (DRV_DBG_TYPE_DEBUG <= ecrnx_dbg_level) {\
+ printk(DBG_PREFIX fmt, ##arg);\
+ } \
+ } while (0)
+
+#else
+#define ECRNX_PRINT(...)
+#define ECRNX_ERR(...)
+#define ECRNX_WARN(...)
+#define ECRNX_INFO(...)
+#define ECRNX_DBG(...)
+#endif
+
+typedef struct {
+ u32 level;
+ u32 dir;
+} LOG_CTL_ST;
+
+extern int ecrnx_dbg_level;
+extern LOG_CTL_ST log_ctl;
+
+#ifndef CONFIG_ECRNX_DEBUGFS_CUSTOM
+int ecrnx_fw_log_level_set(u32 level, u32 dir);
+#endif
+
+#endif /* ECRNX_DEBUG_H_ */
diff --git a/drivers/net/wireless/eswin/ecrnx_debugfs.c b/drivers/net/wireless/eswin/ecrnx_debugfs.c
new file mode 100644
index 000000000000..a7b4232ed6b7
--- /dev/null
+++ b/drivers/net/wireless/eswin/ecrnx_debugfs.c
@@ -0,0 +1,2576 @@
+/**
+ ******************************************************************************
+ *
+ * @file ecrnx_debugfs.c
+ *
+ * @brief Definition of debugfs entries
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+
+
+#include <linux/kernel.h>
+#include <linux/kmod.h>
+#include <linux/debugfs.h>
+#include <linux/string.h>
+#include <linux/sort.h>
+
+#include "ecrnx_debugfs.h"
+#include "ecrnx_msg_tx.h"
+#include "ecrnx_radar.h"
+#include "ecrnx_tx.h"
+
+#define CONFIG_ECRNX_DBGFS_FW_TRACE 0
+
+#ifdef CONFIG_ECRNX_SOFTMAC
+static ssize_t ecrnx_dbgfs_stats_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ecrnx_hw *priv = file->private_data;
+ char *buf;
+ int per;
+ int ret;
+ int i, skipped;
+ ssize_t read;
+ int bufsz = (10 + NX_TX_PAYLOAD_MAX + NX_TXQ_CNT + IEEE80211_MAX_AMPDU_BUF) * 50;
+
+ buf = kmalloc(bufsz, GFP_ATOMIC);
+ if (buf == NULL)
+ return 0;
+
+ if (priv->stats.agg_done)
+ per = DIV_ROUND_UP((priv->stats.agg_retries + priv->stats.agg_died) *
+ 100, priv->stats.agg_done);
+ else
+ per = 0;
+
+ ret = scnprintf(buf, min_t(size_t, bufsz - 1, count),
+ "agg_done %10d\n"
+ "agg_retries %10d\n"
+ "agg_retries_last %10d\n"
+ "agg_died %10d\n"
+ "ampdu_all_ko %10d\n"
+ "agg_PER (%%) %10d\n"
+ "queues_stops %10d\n\n",
+ priv->stats.agg_done,
+ priv->stats.agg_retries,
+ priv->stats.agg_retries_last,
+ priv->stats.agg_died,
+ priv->stats.ampdu_all_ko,
+ per,
+ priv->stats.queues_stops);
+
+ ret += scnprintf(&buf[ret], min_t(size_t, bufsz - 1, count - ret),
+ "TXQs CFM balances ");
+ for (i = 0; i < NX_TXQ_CNT; i++)
+ ret += scnprintf(&buf[ret], min_t(size_t, bufsz - 1, count - ret),
+ " [%1d]:%3d", i,
+ priv->stats.cfm_balance[i]);
+
+#ifdef CONFIG_ECRNX_SPLIT_TX_BUF
+ ret += scnprintf(&buf[ret], min_t(size_t, bufsz - 1, count - ret),
+ "\n\nAMSDU[len] done failed(%%)\n");
+ for (i = skipped = 0; i < NX_TX_PAYLOAD_MAX; i++) {
+ if (priv->stats.amsdus[i].done) {
+ per = DIV_ROUND_UP((priv->stats.amsdus[i].failed) *
+ 100, priv->stats.amsdus[i].done);
+ } else {
+ per = 0;
+ skipped = 1;
+ continue;
+ }
+ if (skipped) {
+ ret += scnprintf(&buf[ret], min_t(size_t, bufsz - 1, count - ret),
+ " * * * %10d %10d\n", 0, 0);
+ skipped = 0;
+ }
+
+ ret += scnprintf(&buf[ret], min_t(size_t, bufsz - 1, count - ret),
+ " [%1d] %10d %10d\n", i + 1,
+ priv->stats.amsdus[i].done, per);
+ }
+ if (skipped)
+ ret += scnprintf(&buf[ret], min_t(size_t, bufsz - 1, count - ret),
+ " * * * %10d %10d\n", 0, 0);
+#endif
+
+ ret += scnprintf(&buf[ret], min_t(size_t, bufsz - ret - 1, count - ret),
+ "\nIn-AMPDU TX failures(%%) RX counts\n");
+ for (i = skipped = 0; i < IEEE80211_MAX_AMPDU_BUF; i++) {
+ int failed;
+
+ if (priv->stats.in_ampdu[i].done) {
+ failed = DIV_ROUND_UP(priv->stats.in_ampdu[i].failed *
+ 100, priv->stats.in_ampdu[i].done);
+ } else {
+ if (!priv->stats.rx_in_ampdu[i].cnt) {
+ skipped = 1;
+ continue;
+ }
+ failed = 0;
+ }
+ if (skipped) {
+ ret += scnprintf(&buf[ret],
+ min_t(size_t, bufsz - ret - 1, count - ret),
+ " * * * %10d %10d\n", 0, 0);
+ skipped = 0;
+ }
+ ret += scnprintf(&buf[ret],
+ min_t(size_t, bufsz - ret - 1, count - ret),
+ " mpdu#%2d %10d %10d\n", i, failed,
+ priv->stats.rx_in_ampdu[i].cnt);
+
+ }
+ if (skipped)
+ ret += scnprintf(&buf[ret],
+ min_t(size_t, bufsz - ret - 1, count - ret),
+ " * * * %10d %10d\n", 0, 0);
+
+ read = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
+
+ kfree(buf);
+
+ return read;
+}
+
+#else
+
+static ssize_t ecrnx_dbgfs_stats_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ecrnx_hw *priv = file->private_data;
+ char *buf;
+ int ret;
+ int i, skipped;
+ ssize_t read;
+ int bufsz = (NX_TXQ_CNT) * 20 + (ARRAY_SIZE(priv->stats.amsdus_rx) + 1) * 40
+ + (ARRAY_SIZE(priv->stats.ampdus_tx) * 30);
+
+ if (*ppos)
+ return 0;
+
+ buf = kmalloc(bufsz, GFP_ATOMIC);
+ if (buf == NULL)
+ return 0;
+
+ ret = scnprintf(buf, bufsz, "TXQs CFM balances ");
+ for (i = 0; i < NX_TXQ_CNT; i++)
+ ret += scnprintf(&buf[ret], bufsz - ret,
+ " [%1d]:%3d", i,
+ priv->stats.cfm_balance[i]);
+
+ ret += scnprintf(&buf[ret], bufsz - ret, "\n");
+
+#ifdef CONFIG_ECRNX_SPLIT_TX_BUF
+ int per = 0;
+ ret += scnprintf(&buf[ret], bufsz - ret,
+ "\nAMSDU[len] done failed received\n");
+ for (i = skipped = 0; i < NX_TX_PAYLOAD_MAX; i++) {
+ if (priv->stats.amsdus[i].done) {
+ per = DIV_ROUND_UP((priv->stats.amsdus[i].failed) *
+ 100, priv->stats.amsdus[i].done);
+ } else if (priv->stats.amsdus_rx[i]) {
+ per = 0;
+ } else {
+ per = 0;
+ skipped = 1;
+ continue;
+ }
+ if (skipped) {
+ ret += scnprintf(&buf[ret], bufsz - ret, " ...\n");
+ skipped = 0;
+ }
+
+ ret += scnprintf(&buf[ret], bufsz - ret,
+ " [%2d] %10d %8d(%3d%%) %10d\n", i ? i + 1 : i,
+ priv->stats.amsdus[i].done,
+ priv->stats.amsdus[i].failed, per,
+ priv->stats.amsdus_rx[i]);
+ }
+
+ for (; i < ARRAY_SIZE(priv->stats.amsdus_rx); i++) {
+ if (!priv->stats.amsdus_rx[i]) {
+ skipped = 1;
+ continue;
+ }
+ if (skipped) {
+ ret += scnprintf(&buf[ret], bufsz - ret, " ...\n");
+ skipped = 0;
+ }
+
+ ret += scnprintf(&buf[ret], bufsz - ret,
+ " [%2d] %10d\n",
+ i + 1, priv->stats.amsdus_rx[i]);
+ }
+#else
+ ret += scnprintf(&buf[ret], bufsz - ret,
+ "\nAMSDU[len] received\n");
+ for (i = skipped = 0; i < ARRAY_SIZE(priv->stats.amsdus_rx); i++) {
+ if (!priv->stats.amsdus_rx[i]) {
+ skipped = 1;
+ continue;
+ }
+ if (skipped) {
+ ret += scnprintf(&buf[ret], bufsz - ret,
+ " ...\n");
+ skipped = 0;
+ }
+
+ ret += scnprintf(&buf[ret], bufsz - ret,
+ " [%2d] %10d\n",
+ i + 1, priv->stats.amsdus_rx[i]);
+ }
+
+#endif /* CONFIG_ECRNX_SPLIT_TX_BUF */
+
+ ret += scnprintf(&buf[ret], bufsz - ret,
+ "\nAMPDU[len] done received\n");
+ for (i = skipped = 0; i < ARRAY_SIZE(priv->stats.ampdus_tx); i++) {
+ if (!priv->stats.ampdus_tx[i] && !priv->stats.ampdus_rx[i]) {
+ skipped = 1;
+ continue;
+ }
+ if (skipped) {
+ ret += scnprintf(&buf[ret], bufsz - ret,
+ " ...\n");
+ skipped = 0;
+ }
+
+ ret += scnprintf(&buf[ret], bufsz - ret,
+ " [%2d] %9d %9d\n", i ? i + 1 : i,
+ priv->stats.ampdus_tx[i], priv->stats.ampdus_rx[i]);
+ }
+
+ ret += scnprintf(&buf[ret], bufsz - ret,
+ "#mpdu missed %9d\n",
+ priv->stats.ampdus_rx_miss);
+
+ ret += scnprintf(&buf[ret], bufsz - ret,
+ "\nmsg_tx:%d,%d; data_tx:%d,%d\n",
+ priv->msg_tx, priv->msg_tx_done, priv->data_tx, priv->data_tx_done);
+ ret += scnprintf(&buf[ret], bufsz - ret,
+ "usb_rx:%d, data_rx:%d, msg_rx:%d\n",
+ priv->usb_rx, priv->data_rx, priv->msg_rx);
+ read = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
+
+ kfree(buf);
+
+ return read;
+}
+#endif /* CONFIG_ECRNX_SOFTMAC */
+
+static ssize_t ecrnx_dbgfs_stats_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ecrnx_hw *priv = file->private_data;
+
+ /* Prevent from interrupt preemption as these statistics are updated under
+ * interrupt */
+ spin_lock_bh(&priv->tx_lock);
+
+ memset(&priv->stats, 0, sizeof(priv->stats));
+
+ spin_unlock_bh(&priv->tx_lock);
+
+ return count;
+}
+
+DEBUGFS_READ_WRITE_FILE_OPS(stats);
+
+#define TXQ_STA_PREF "tid|"
+#define TXQ_STA_PREF_FMT "%3d|"
+
+#ifdef CONFIG_ECRNX_FULLMAC
+#define TXQ_VIF_PREF "type|"
+#define TXQ_VIF_PREF_FMT "%4s|"
+#else
+#define TXQ_VIF_PREF "AC|"
+#define TXQ_VIF_PREF_FMT "%2s|"
+#endif /* CONFIG_ECRNX_FULLMAC */
+
+#define TXQ_HDR "idx| status|credit|ready|retry|pushed"
+#define TXQ_HDR_FMT "%3d|%s%s%s%s%s%s%s%s|%6d|%5d|%5d|%6d"
+
+#ifdef CONFIG_ECRNX_AMSDUS_TX
+#ifdef CONFIG_ECRNX_FULLMAC
+#define TXQ_HDR_SUFF "|amsdu"
+#define TXQ_HDR_SUFF_FMT "|%5d"
+#else
+#define TXQ_HDR_SUFF "|amsdu-ht|amdsu-vht"
+#define TXQ_HDR_SUFF_FMT "|%8d|%9d"
+#endif /* CONFIG_ECRNX_FULLMAC */
+#else
+#define TXQ_HDR_SUFF ""
+#define TXQ_HDR_SUF_FMT ""
+#endif /* CONFIG_ECRNX_AMSDUS_TX */
+
+#define TXQ_HDR_MAX_LEN (sizeof(TXQ_STA_PREF) + sizeof(TXQ_HDR) + sizeof(TXQ_HDR_SUFF) + 1)
+
+#ifdef CONFIG_ECRNX_FULLMAC
+#define PS_HDR "Legacy PS: ready=%d, sp=%d / UAPSD: ready=%d, sp=%d"
+#define PS_HDR_LEGACY "Legacy PS: ready=%d, sp=%d"
+#define PS_HDR_UAPSD "UAPSD: ready=%d, sp=%d"
+#define PS_HDR_MAX_LEN sizeof("Legacy PS: ready=xxx, sp=xxx / UAPSD: ready=xxx, sp=xxx\n")
+#else
+#define PS_HDR ""
+#define PS_HDR_MAX_LEN 0
+#endif /* CONFIG_ECRNX_FULLMAC */
+
+#define STA_HDR "** STA %d (%pM)\n"
+#define STA_HDR_MAX_LEN sizeof("- STA xx (xx:xx:xx:xx:xx:xx)\n") + PS_HDR_MAX_LEN
+
+#ifdef CONFIG_ECRNX_FULLMAC
+#define VIF_HDR "* VIF [%d] %s\n"
+#define VIF_HDR_MAX_LEN sizeof(VIF_HDR) + IFNAMSIZ
+#else
+#define VIF_HDR "* VIF [%d]\n"
+#define VIF_HDR_MAX_LEN sizeof(VIF_HDR)
+#endif
+
+
+#ifdef CONFIG_ECRNX_AMSDUS_TX
+
+#ifdef CONFIG_ECRNX_FULLMAC
+#define VIF_SEP "---------------------------------------\n"
+#else
+#define VIF_SEP "----------------------------------------------------\n"
+#endif /* CONFIG_ECRNX_FULLMAC */
+
+#else /* ! CONFIG_ECRNX_AMSDUS_TX */
+#define VIF_SEP "---------------------------------\n"
+#endif /* CONFIG_ECRNX_AMSDUS_TX*/
+
+#define VIF_SEP_LEN sizeof(VIF_SEP)
+
+#define CAPTION "status: L=in hwq list, F=stop full, P=stop sta PS, V=stop vif PS,\
+ C=stop channel, S=stop CSA, M=stop MU, N=Ndev queue stopped"
+#define CAPTION_LEN sizeof(CAPTION)
+
+#define STA_TXQ 0
+#define VIF_TXQ 1
+
+static int ecrnx_dbgfs_txq(char *buf, size_t size, struct ecrnx_txq *txq, int type, int tid, char *name)
+{
+ int res, idx = 0;
+ int i, pushed = 0;
+
+ if (type == STA_TXQ) {
+ res = scnprintf(&buf[idx], size, TXQ_STA_PREF_FMT, tid);
+ idx += res;
+ size -= res;
+ } else {
+ res = scnprintf(&buf[idx], size, TXQ_VIF_PREF_FMT, name);
+ idx += res;
+ size -= res;
+ }
+
+ for (i = 0; i < CONFIG_USER_MAX; i++) {
+ pushed += txq->pkt_pushed[i];
+ }
+
+ res = scnprintf(&buf[idx], size, TXQ_HDR_FMT, txq->idx,
+ (txq->status & ECRNX_TXQ_IN_HWQ_LIST) ? "L" : " ",
+ (txq->status & ECRNX_TXQ_STOP_FULL) ? "F" : " ",
+ (txq->status & ECRNX_TXQ_STOP_STA_PS) ? "P" : " ",
+ (txq->status & ECRNX_TXQ_STOP_VIF_PS) ? "V" : " ",
+ (txq->status & ECRNX_TXQ_STOP_CHAN) ? "C" : " ",
+ (txq->status & ECRNX_TXQ_STOP_CSA) ? "S" : " ",
+ (txq->status & ECRNX_TXQ_STOP_MU_POS) ? "M" : " ",
+ (txq->status & ECRNX_TXQ_NDEV_FLOW_CTRL) ? "N" : " ",
+ txq->credits, skb_queue_len(&txq->sk_list),
+ txq->nb_retry, pushed);
+ idx += res;
+ size -= res;
+
+#ifdef CONFIG_ECRNX_AMSDUS_TX
+ if (type == STA_TXQ) {
+ res = scnprintf(&buf[idx], size, TXQ_HDR_SUFF_FMT,
+#ifdef CONFIG_ECRNX_FULLMAC
+ txq->amsdu_len
+#else
+ txq->amsdu_ht_len_cap, txq->amsdu_vht_len_cap
+#endif /* CONFIG_ECRNX_FULLMAC */
+ );
+ idx += res;
+ size -= res;
+ }
+#endif
+
+ res = scnprintf(&buf[idx], size, "\n");
+ idx += res;
+ size -= res;
+
+ return idx;
+}
+
+static int ecrnx_dbgfs_txq_sta(char *buf, size_t size, struct ecrnx_sta *ecrnx_sta,
+ struct ecrnx_hw *ecrnx_hw)
+{
+ int tid, res, idx = 0;
+ struct ecrnx_txq *txq;
+#ifdef CONFIG_ECRNX_SOFTMAC
+ struct ieee80211_sta *sta = ecrnx_to_ieee80211_sta(ecrnx_sta);
+#endif /* CONFIG_ECRNX_SOFTMAC */
+
+ res = scnprintf(&buf[idx], size, "\n" STA_HDR,
+ ecrnx_sta->sta_idx,
+#ifdef CONFIG_ECRNX_SOFTMAC
+ sta->addr
+#else
+ ecrnx_sta->mac_addr
+#endif /* CONFIG_ECRNX_SOFTMAC */
+ );
+ idx += res;
+ size -= res;
+
+#ifdef CONFIG_ECRNX_FULLMAC
+ if (ecrnx_sta->ps.active) {
+ if (ecrnx_sta->uapsd_tids &&
+ (ecrnx_sta->uapsd_tids == ((1 << NX_NB_TXQ_PER_STA) - 1)))
+ res = scnprintf(&buf[idx], size, PS_HDR_UAPSD "\n",
+ ecrnx_sta->ps.pkt_ready[UAPSD_ID],
+ ecrnx_sta->ps.sp_cnt[UAPSD_ID]);
+ else if (ecrnx_sta->uapsd_tids)
+ res = scnprintf(&buf[idx], size, PS_HDR "\n",
+ ecrnx_sta->ps.pkt_ready[LEGACY_PS_ID],
+ ecrnx_sta->ps.sp_cnt[LEGACY_PS_ID],
+ ecrnx_sta->ps.pkt_ready[UAPSD_ID],
+ ecrnx_sta->ps.sp_cnt[UAPSD_ID]);
+ else
+ res = scnprintf(&buf[idx], size, PS_HDR_LEGACY "\n",
+ ecrnx_sta->ps.pkt_ready[LEGACY_PS_ID],
+ ecrnx_sta->ps.sp_cnt[LEGACY_PS_ID]);
+ idx += res;
+ size -= res;
+ } else {
+ res = scnprintf(&buf[idx], size, "\n");
+ idx += res;
+ size -= res;
+ }
+#endif /* CONFIG_ECRNX_FULLMAC */
+
+
+ res = scnprintf(&buf[idx], size, TXQ_STA_PREF TXQ_HDR TXQ_HDR_SUFF "\n");
+ idx += res;
+ size -= res;
+
+
+ foreach_sta_txq(ecrnx_sta, txq, tid, ecrnx_hw) {
+ res = ecrnx_dbgfs_txq(&buf[idx], size, txq, STA_TXQ, tid, NULL);
+ idx += res;
+ size -= res;
+ }
+
+ return idx;
+}
+
+static int ecrnx_dbgfs_txq_vif(char *buf, size_t size, struct ecrnx_vif *ecrnx_vif,
+ struct ecrnx_hw *ecrnx_hw)
+{
+ int res, idx = 0;
+ struct ecrnx_txq *txq;
+ struct ecrnx_sta *ecrnx_sta;
+
+#ifdef CONFIG_ECRNX_FULLMAC
+ res = scnprintf(&buf[idx], size, VIF_HDR, ecrnx_vif->vif_index, ecrnx_vif->ndev->name);
+ idx += res;
+ size -= res;
+ if (!ecrnx_vif->up || ecrnx_vif->ndev == NULL)
+ return idx;
+
+#else
+ int ac;
+ char ac_name[2] = {'0', '\0'};
+
+ res = scnprintf(&buf[idx], size, VIF_HDR, ecrnx_vif->vif_index);
+ idx += res;
+ size -= res;
+#endif /* CONFIG_ECRNX_FULLMAC */
+
+#ifdef CONFIG_ECRNX_FULLMAC
+ if (ECRNX_VIF_TYPE(ecrnx_vif) == NL80211_IFTYPE_AP ||
+ ECRNX_VIF_TYPE(ecrnx_vif) == NL80211_IFTYPE_P2P_GO ||
+ ECRNX_VIF_TYPE(ecrnx_vif) == NL80211_IFTYPE_MESH_POINT) {
+ res = scnprintf(&buf[idx], size, TXQ_VIF_PREF TXQ_HDR "\n");
+ idx += res;
+ size -= res;
+ txq = ecrnx_txq_vif_get(ecrnx_vif, NX_UNK_TXQ_TYPE);
+ res = ecrnx_dbgfs_txq(&buf[idx], size, txq, VIF_TXQ, 0, "UNK");
+ idx += res;
+ size -= res;
+ txq = ecrnx_txq_vif_get(ecrnx_vif, NX_BCMC_TXQ_TYPE);
+ res = ecrnx_dbgfs_txq(&buf[idx], size, txq, VIF_TXQ, 0, "BCMC");
+ idx += res;
+ size -= res;
+ ecrnx_sta = &ecrnx_hw->sta_table[ecrnx_vif->ap.bcmc_index];
+ if (ecrnx_sta->ps.active) {
+ res = scnprintf(&buf[idx], size, PS_HDR_LEGACY "\n",
+ ecrnx_sta->ps.sp_cnt[LEGACY_PS_ID],
+ ecrnx_sta->ps.sp_cnt[LEGACY_PS_ID]);
+ idx += res;
+ size -= res;
+ } else {
+ res = scnprintf(&buf[idx], size, "\n");
+ idx += res;
+ size -= res;
+ }
+
+ list_for_each_entry(ecrnx_sta, &ecrnx_vif->ap.sta_list, list) {
+ res = ecrnx_dbgfs_txq_sta(&buf[idx], size, ecrnx_sta, ecrnx_hw);
+ idx += res;
+ size -= res;
+ }
+ } else if (ECRNX_VIF_TYPE(ecrnx_vif) == NL80211_IFTYPE_STATION ||
+ ECRNX_VIF_TYPE(ecrnx_vif) == NL80211_IFTYPE_P2P_CLIENT) {
+ if (ecrnx_vif->sta.ap) {
+ res = ecrnx_dbgfs_txq_sta(&buf[idx], size, ecrnx_vif->sta.ap, ecrnx_hw);
+ idx += res;
+ size -= res;
+ }
+ }
+
+#else
+ res = scnprintf(&buf[idx], size, TXQ_VIF_PREF TXQ_HDR "\n");
+ idx += res;
+ size -= res;
+
+ foreach_vif_txq(ecrnx_vif, txq, ac) {
+ ac_name[0]++;
+ res = ecrnx_dbgfs_txq(&buf[idx], size, txq, VIF_TXQ, 0, ac_name);
+ idx += res;
+ size -= res;
+ }
+
+ list_for_each_entry(ecrnx_sta, &ecrnx_vif->stations, list) {
+ res = ecrnx_dbgfs_txq_sta(&buf[idx], size, ecrnx_sta, ecrnx_hw);
+ idx += res;
+ size -= res;
+ }
+#endif /* CONFIG_ECRNX_FULLMAC */
+ return idx;
+}
+
+static ssize_t ecrnx_dbgfs_txq_read(struct file *file ,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ecrnx_hw *ecrnx_hw = file->private_data;
+ struct ecrnx_vif *vif;
+ char *buf;
+ int idx, res;
+ ssize_t read;
+ size_t bufsz = ((NX_VIRT_DEV_MAX * (VIF_HDR_MAX_LEN + 2 * VIF_SEP_LEN)) +
+ (NX_REMOTE_STA_MAX * STA_HDR_MAX_LEN) +
+ ((NX_REMOTE_STA_MAX + NX_VIRT_DEV_MAX + NX_NB_TXQ) *
+ TXQ_HDR_MAX_LEN) + CAPTION_LEN);
+
+ /* everything is read in one go */
+ if (*ppos)
+ return 0;
+
+ bufsz = min_t(size_t, bufsz, count);
+ buf = kmalloc(bufsz, GFP_ATOMIC);
+ if (buf == NULL)
+ return 0;
+
+ bufsz--;
+ idx = 0;
+
+ res = scnprintf(&buf[idx], bufsz, CAPTION);
+ idx += res;
+ bufsz -= res;
+
+ //spin_lock_bh(&ecrnx_hw->tx_lock);
+ list_for_each_entry(vif, &ecrnx_hw->vifs, list) {
+ res = scnprintf(&buf[idx], bufsz, "\n"VIF_SEP);
+ idx += res;
+ bufsz -= res;
+ res = ecrnx_dbgfs_txq_vif(&buf[idx], bufsz, vif, ecrnx_hw);
+ idx += res;
+ bufsz -= res;
+ res = scnprintf(&buf[idx], bufsz, VIF_SEP);
+ idx += res;
+ bufsz -= res;
+ }
+ //spin_unlock_bh(&ecrnx_hw->tx_lock);
+
+ read = simple_read_from_buffer(user_buf, count, ppos, buf, idx);
+ kfree(buf);
+
+ return read;
+}
+DEBUGFS_READ_FILE_OPS(txq);
+
+static ssize_t ecrnx_dbgfs_acsinfo_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ecrnx_hw *priv = file->private_data;
+ #ifdef CONFIG_ECRNX_SOFTMAC
+ struct wiphy *wiphy = priv->hw->wiphy;
+ #else //CONFIG_ECRNX_SOFTMAC
+ struct wiphy *wiphy = priv->wiphy;
+ #endif //CONFIG_ECRNX_SOFTMAC
+ ssize_t read;
+ char *buf = kmalloc((SCAN_CHANNEL_MAX + 1) * 43, GFP_ATOMIC);
+
+ //char buf[(SCAN_CHANNEL_MAX + 1) * 43];
+ int survey_cnt = 0;
+ int len = 0;
+ int band, chan_cnt;
+
+ if(!buf){
+ return 0;
+ }
+
+ mutex_lock(&priv->dbgdump_elem.mutex);
+
+ len += scnprintf(buf, min_t(size_t, sizeof(buf) - 1, count),
+ "FREQ TIME(ms) BUSY(ms) NOISE(dBm)\n");
+
+#ifdef CONFIG_ECRNX_5G
+ for (band = NL80211_BAND_2GHZ; band <= NL80211_BAND_5GHZ; band++) {
+#else
+ for (band = NL80211_BAND_2GHZ; band <= NL80211_BAND_2GHZ; band++) {
+#endif
+ for (chan_cnt = 0; chan_cnt < wiphy->bands[band]->n_channels; chan_cnt++) {
+ struct ecrnx_survey_info *p_survey_info = &priv->survey[survey_cnt];
+ struct ieee80211_channel *p_chan = &wiphy->bands[band]->channels[chan_cnt];
+
+ if (p_survey_info->filled) {
+ len += scnprintf(&buf[len], min_t(size_t, sizeof(buf) - len - 1, count),
+ "%d %03d %03d %d\n",
+ p_chan->center_freq,
+ p_survey_info->chan_time_ms,
+ p_survey_info->chan_time_busy_ms,
+ p_survey_info->noise_dbm);
+ } else {
+ len += scnprintf(&buf[len], min_t(size_t, sizeof(buf) -len -1, count),
+ "%d NOT AVAILABLE\n",
+ p_chan->center_freq);
+ }
+
+ survey_cnt++;
+ }
+ }
+
+ mutex_unlock(&priv->dbgdump_elem.mutex);
+ read = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+ kfree(buf);
+
+ return read;
+}
+
+DEBUGFS_READ_FILE_OPS(acsinfo);
+
+static ssize_t ecrnx_dbgfs_fw_dbg_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ char help[]="usage: [MOD:<ALL|KE|DBG|IPC|DMA|MM|TX|RX|PHY>]* "
+ "[DBG:<NONE|CRT|ERR|WRN|INF|VRB>]\n";
+
+ return simple_read_from_buffer(user_buf, count, ppos, help, sizeof(help));
+}
+
+
+static ssize_t ecrnx_dbgfs_fw_dbg_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ecrnx_hw *priv = file->private_data;
+ char buf[32];
+ int idx = 0;
+ u32 mod = 0;
+ size_t len = min_t(size_t, count, sizeof(buf) - 1);
+
+ if (copy_from_user(buf, user_buf, len))
+ return -EFAULT;
+ buf[len] = '\0';
+
+#define ECRNX_MOD_TOKEN(str, val) \
+ if (strncmp(&buf[idx], str, sizeof(str) - 1 ) == 0) { \
+ idx += sizeof(str) - 1; \
+ mod |= val; \
+ continue; \
+ }
+
+#define ECRNX_DBG_TOKEN(str, val) \
+ if (strncmp(&buf[idx], str, sizeof(str) - 1) == 0) { \
+ idx += sizeof(str) - 1; \
+ dbg = val; \
+ goto dbg_done; \
+ }
+
+ while ((idx + 4) < len) {
+ if (strncmp(&buf[idx], "MOD:", 4) == 0) {
+ idx += 4;
+ ECRNX_MOD_TOKEN("ALL", 0xffffffff);
+ ECRNX_MOD_TOKEN("KE", BIT(0));
+ ECRNX_MOD_TOKEN("DBG", BIT(1));
+ ECRNX_MOD_TOKEN("IPC", BIT(2));
+ ECRNX_MOD_TOKEN("DMA", BIT(3));
+ ECRNX_MOD_TOKEN("MM", BIT(4));
+ ECRNX_MOD_TOKEN("TX", BIT(5));
+ ECRNX_MOD_TOKEN("RX", BIT(6));
+ ECRNX_MOD_TOKEN("PHY", BIT(7));
+ idx++;
+ } else if (strncmp(&buf[idx], "DBG:", 4) == 0) {
+ u32 dbg = 0;
+ idx += 4;
+ ECRNX_DBG_TOKEN("NONE", 0);
+ ECRNX_DBG_TOKEN("CRT", 1);
+ ECRNX_DBG_TOKEN("ERR", 2);
+ ECRNX_DBG_TOKEN("WRN", 3);
+ ECRNX_DBG_TOKEN("INF", 4);
+ ECRNX_DBG_TOKEN("VRB", 5);
+ idx++;
+ continue;
+ dbg_done:
+ ecrnx_send_dbg_set_sev_filter_req(priv, dbg);
+ } else {
+ idx++;
+ }
+ }
+
+ if (mod) {
+ ecrnx_send_dbg_set_mod_filter_req(priv, mod);
+ }
+
+ return count;
+}
+
+DEBUGFS_READ_WRITE_FILE_OPS(fw_dbg);
+
+static ssize_t ecrnx_dbgfs_sys_stats_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ecrnx_hw *priv = file->private_data;
+ char buf[3*64];
+ int len = 0;
+ ssize_t read;
+ int error = 0;
+ struct dbg_get_sys_stat_cfm cfm;
+ u32 sleep_int, sleep_frac, doze_int, doze_frac;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ /* Get the information from the FW */
+ if ((error = ecrnx_send_dbg_get_sys_stat_req(priv, &cfm)))
+ return error;
+
+ if (cfm.stats_time == 0)
+ return 0;
+
+ sleep_int = ((cfm.cpu_sleep_time * 100) / cfm.stats_time);
+ sleep_frac = (((cfm.cpu_sleep_time * 100) % cfm.stats_time) * 10) / cfm.stats_time;
+ doze_int = ((cfm.doze_time * 100) / cfm.stats_time);
+ doze_frac = (((cfm.doze_time * 100) % cfm.stats_time) * 10) / cfm.stats_time;
+
+ len += scnprintf(buf, min_t(size_t, sizeof(buf) - 1, count),
+ "\nSystem statistics:\n");
+ len += scnprintf(&buf[len], min_t(size_t, sizeof(buf) - 1, count),
+ " CPU sleep [%%]: %d.%d\n", sleep_int, sleep_frac);
+ len += scnprintf(&buf[len], min_t(size_t, sizeof(buf) - 1, count),
+ " Doze [%%]: %d.%d\n", doze_int, doze_frac);
+
+ read = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+
+ return read;
+}
+
+DEBUGFS_READ_FILE_OPS(sys_stats);
+
+#ifdef CONFIG_ECRNX_MUMIMO_TX
+static ssize_t ecrnx_dbgfs_mu_group_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ecrnx_hw *ecrnx_hw = file->private_data;
+ struct ecrnx_mu_info *mu = &ecrnx_hw->mu;
+ struct ecrnx_mu_group *group;
+ size_t bufsz = NX_MU_GROUP_MAX * sizeof("xx = (xx - xx - xx - xx)\n") + 50;
+ char *buf;
+ int j, res, idx = 0;
+
+ if (*ppos)
+ return 0;
+
+ buf = kmalloc(bufsz, GFP_ATOMIC);
+ if (buf == NULL)
+ return 0;
+
+ res = scnprintf(&buf[idx], bufsz, "MU Group list (%d groups, %d users max)\n",
+ NX_MU_GROUP_MAX, CONFIG_USER_MAX);
+ idx += res;
+ bufsz -= res;
+
+ list_for_each_entry(group, &mu->active_groups, list) {
+ if (group->user_cnt) {
+ res = scnprintf(&buf[idx], bufsz, "%2d = (", group->group_id);
+ idx += res;
+ bufsz -= res;
+ for (j = 0; j < (CONFIG_USER_MAX - 1) ; j++) {
+ if (group->users[j])
+ res = scnprintf(&buf[idx], bufsz, "%2d - ",
+ group->users[j]->sta_idx);
+ else
+ res = scnprintf(&buf[idx], bufsz, ".. - ");
+
+ idx += res;
+ bufsz -= res;
+ }
+
+ if (group->users[j])
+ res = scnprintf(&buf[idx], bufsz, "%2d)\n",
+ group->users[j]->sta_idx);
+ else
+ res = scnprintf(&buf[idx], bufsz, "..)\n");
+
+ idx += res;
+ bufsz -= res;
+ }
+ }
+
+ res = simple_read_from_buffer(user_buf, count, ppos, buf, idx);
+ kfree(buf);
+
+ return res;
+}
+
+DEBUGFS_READ_FILE_OPS(mu_group);
+#endif
+
+#ifdef CONFIG_ECRNX_P2P_DEBUGFS
+static ssize_t ecrnx_dbgfs_oppps_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ecrnx_hw *rw_hw = file->private_data;
+ struct ecrnx_vif *rw_vif;
+ char buf[32];
+ size_t len = min_t(size_t, count, sizeof(buf) - 1);
+ int ctw;
+
+ if (copy_from_user(buf, user_buf, len))
+ return -EFAULT;
+ buf[len] = '\0';
+
+ /* Read the written CT Window (provided in ms) value */
+ if (sscanf(buf, "ctw=%d", &ctw) > 0) {
+ /* Check if at least one VIF is configured as P2P GO */
+ list_for_each_entry(rw_vif, &rw_hw->vifs, list) {
+#ifdef CONFIG_ECRNX_SOFTMAC
+ if ((ECRNX_VIF_TYPE(rw_vif) == NL80211_IFTYPE_AP) && rw_vif->vif->p2p) {
+#else /* CONFIG_ECRNX_FULLMAC */
+ if (ECRNX_VIF_TYPE(rw_vif) == NL80211_IFTYPE_P2P_GO) {
+#endif /* CONFIG_ECRNX_SOFTMAC */
+ struct mm_set_p2p_oppps_cfm cfm;
+
+ /* Forward request to the embedded and wait for confirmation */
+ ecrnx_send_p2p_oppps_req(rw_hw, rw_vif, (u8)ctw, &cfm);
+
+ break;
+ }
+ }
+ }
+
+ return count;
+}
+
+DEBUGFS_WRITE_FILE_OPS(oppps);
+
+static ssize_t ecrnx_dbgfs_noa_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ecrnx_hw *rw_hw = file->private_data;
+ struct ecrnx_vif *rw_vif;
+ char buf[64];
+ size_t len = min_t(size_t, count, sizeof(buf) - 1);
+ int noa_count, interval, duration, dyn_noa;
+
+ if (copy_from_user(buf, user_buf, len))
+ return -EFAULT;
+ buf[len] = '\0';
+
+ /* Read the written NOA information */
+ if (sscanf(buf, "count=%d interval=%d duration=%d dyn=%d",
+ &noa_count, &interval, &duration, &dyn_noa) > 0) {
+ /* Check if at least one VIF is configured as P2P GO */
+ list_for_each_entry(rw_vif, &rw_hw->vifs, list) {
+#ifdef CONFIG_ECRNX_SOFTMAC
+ if ((ECRNX_VIF_TYPE(rw_vif) == NL80211_IFTYPE_AP) && rw_vif->vif->p2p) {
+#else /* CONFIG_ECRNX_FULLMAC */
+ if (ECRNX_VIF_TYPE(rw_vif) == NL80211_IFTYPE_P2P_GO) {
+#endif /* CONFIG_ECRNX_SOFTMAC */
+ struct mm_set_p2p_noa_cfm cfm;
+
+ /* Forward request to the embedded and wait for confirmation */
+ ecrnx_send_p2p_noa_req(rw_hw, rw_vif, noa_count, interval,
+ duration, (dyn_noa > 0), &cfm);
+
+ break;
+ }
+ }
+ }
+
+ return count;
+}
+
+DEBUGFS_WRITE_FILE_OPS(noa);
+#endif /* CONFIG_ECRNX_P2P_DEBUGFS */
+
+struct ecrnx_dbgfs_fw_trace {
+ struct ecrnx_fw_trace_local_buf lbuf;
+ struct ecrnx_fw_trace *trace;
+ struct ecrnx_hw *ecrnx_hw;
+};
+
+static int ecrnx_dbgfs_fw_trace_open(struct inode *inode, struct file *file)
+{
+ struct ecrnx_dbgfs_fw_trace *ltrace = kmalloc(sizeof(*ltrace), GFP_KERNEL);
+ struct ecrnx_hw *priv = inode->i_private;
+
+ if (!ltrace)
+ return -ENOMEM;
+
+ if (ecrnx_fw_trace_alloc_local(&ltrace->lbuf, 5120)) {
+ kfree(ltrace);
+ }
+
+ ltrace->trace = &priv->debugfs.fw_trace;
+ ltrace->ecrnx_hw = priv;
+ file->private_data = ltrace;
+ return 0;
+}
+
+static int ecrnx_dbgfs_fw_trace_release(struct inode *inode, struct file *file)
+{
+ struct ecrnx_dbgfs_fw_trace *ltrace = file->private_data;
+
+ if (ltrace) {
+ ecrnx_fw_trace_free_local(&ltrace->lbuf);
+ kfree(ltrace);
+ }
+
+ return 0;
+}
+
+static ssize_t ecrnx_dbgfs_fw_trace_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ecrnx_dbgfs_fw_trace *ltrace = file->private_data;
+ bool dont_wait = ((file->f_flags & O_NONBLOCK) ||
+ ltrace->ecrnx_hw->debugfs.unregistering);
+
+ return ecrnx_fw_trace_read(ltrace->trace, &ltrace->lbuf,
+ dont_wait, user_buf, count);
+}
+
+static ssize_t ecrnx_dbgfs_fw_trace_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ecrnx_dbgfs_fw_trace *ltrace = file->private_data;
+ int ret;
+
+ ret = _ecrnx_fw_trace_reset(ltrace->trace, true);
+ if (ret)
+ return ret;
+
+ return count;
+}
+
+DEBUGFS_READ_WRITE_OPEN_RELEASE_FILE_OPS(fw_trace);
+
+static ssize_t ecrnx_dbgfs_fw_trace_level_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ecrnx_hw *priv = file->private_data;
+ return ecrnx_fw_trace_level_read(&priv->debugfs.fw_trace, user_buf,
+ count, ppos);
+}
+
+static ssize_t ecrnx_dbgfs_fw_trace_level_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ecrnx_hw *priv = file->private_data;
+ return ecrnx_fw_trace_level_write(&priv->debugfs.fw_trace, user_buf, count);
+}
+DEBUGFS_READ_WRITE_FILE_OPS(fw_trace_level);
+
+
+#ifdef CONFIG_ECRNX_RADAR
+static ssize_t ecrnx_dbgfs_pulses_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos,
+ int rd_idx)
+{
+ struct ecrnx_hw *priv = file->private_data;
+ char *buf;
+ int len = 0;
+ int bufsz;
+ int i;
+ int index;
+ struct ecrnx_radar_pulses *p = &priv->radar.pulses[rd_idx];
+ ssize_t read;
+
+ if (*ppos != 0)
+ return 0;
+
+ /* Prevent from interrupt preemption */
+ spin_lock_bh(&priv->radar.lock);
+ bufsz = p->count * 34 + 51;
+ bufsz += ecrnx_radar_dump_pattern_detector(NULL, 0, &priv->radar, rd_idx);
+ buf = kmalloc(bufsz, GFP_ATOMIC);
+ if (buf == NULL) {
+ spin_unlock_bh(&priv->radar.lock);
+ return 0;
+ }
+
+ if (p->count) {
+ len += scnprintf(&buf[len], bufsz - len,
+ " PRI WIDTH FOM FREQ\n");
+ index = p->index;
+ for (i = 0; i < p->count; i++) {
+ struct radar_pulse *pulse;
+
+ if (index > 0)
+ index--;
+ else
+ index = ECRNX_RADAR_PULSE_MAX - 1;
+
+ pulse = (struct radar_pulse *) &p->buffer[index];
+
+ len += scnprintf(&buf[len], bufsz - len,
+ "%05dus %03dus %2d%% %+3dMHz\n", pulse->rep,
+ 2 * pulse->len, 6 * pulse->fom, 2*pulse->freq);
+ }
+ }
+
+ len += ecrnx_radar_dump_pattern_detector(&buf[len], bufsz - len,
+ &priv->radar, rd_idx);
+
+ spin_unlock_bh(&priv->radar.lock);
+
+ read = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+
+ kfree(buf);
+
+ return read;
+}
+
+static ssize_t ecrnx_dbgfs_pulses_prim_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ return ecrnx_dbgfs_pulses_read(file, user_buf, count, ppos, 0);
+}
+
+DEBUGFS_READ_FILE_OPS(pulses_prim);
+
+static ssize_t ecrnx_dbgfs_pulses_sec_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ return ecrnx_dbgfs_pulses_read(file, user_buf, count, ppos, 1);
+}
+
+DEBUGFS_READ_FILE_OPS(pulses_sec);
+
+static ssize_t ecrnx_dbgfs_detected_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ecrnx_hw *priv = file->private_data;
+ char *buf;
+ int bufsz,len = 0;
+ ssize_t read;
+
+ if (*ppos != 0)
+ return 0;
+
+ bufsz = 5; // RIU:\n
+ bufsz += ecrnx_radar_dump_radar_detected(NULL, 0, &priv->radar,
+ ECRNX_RADAR_RIU);
+
+ if (priv->phy.cnt > 1) {
+ bufsz += 5; // FCU:\n
+ bufsz += ecrnx_radar_dump_radar_detected(NULL, 0, &priv->radar,
+ ECRNX_RADAR_FCU);
+ }
+
+ buf = kmalloc(bufsz, GFP_KERNEL);
+ if (buf == NULL) {
+ return 0;
+ }
+
+ len = scnprintf(&buf[len], bufsz, "RIU:\n");
+ len += ecrnx_radar_dump_radar_detected(&buf[len], bufsz - len, &priv->radar,
+ ECRNX_RADAR_RIU);
+
+ if (priv->phy.cnt > 1) {
+ len += scnprintf(&buf[len], bufsz - len, "FCU:\n");
+ len += ecrnx_radar_dump_radar_detected(&buf[len], bufsz - len,
+ &priv->radar, ECRNX_RADAR_FCU);
+ }
+
+ read = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+
+ kfree(buf);
+
+ return read;
+}
+
+DEBUGFS_READ_FILE_OPS(detected);
+
+static ssize_t ecrnx_dbgfs_enable_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ecrnx_hw *priv = file->private_data;
+ char buf[32];
+ int ret;
+ ssize_t read;
+
+ ret = scnprintf(buf, min_t(size_t, sizeof(buf) - 1, count),
+ "RIU=%d FCU=%d\n", priv->radar.dpd[ECRNX_RADAR_RIU]->enabled,
+ priv->radar.dpd[ECRNX_RADAR_FCU]->enabled);
+
+ read = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
+
+ return read;
+}
+
+static ssize_t ecrnx_dbgfs_enable_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ecrnx_hw *priv = file->private_data;
+ char buf[32];
+ int val;
+ size_t len = min_t(size_t, count, sizeof(buf) - 1);
+
+ if (copy_from_user(buf, user_buf, len))
+ return -EFAULT;
+
+ buf[len] = '\0';
+
+ if (sscanf(buf, "RIU=%d", &val) > 0)
+ ecrnx_radar_detection_enable(&priv->radar, val, ECRNX_RADAR_RIU);
+
+ if (sscanf(buf, "FCU=%d", &val) > 0)
+ ecrnx_radar_detection_enable(&priv->radar, val, ECRNX_RADAR_FCU);
+
+ return count;
+}
+
+DEBUGFS_READ_WRITE_FILE_OPS(enable);
+
+static ssize_t ecrnx_dbgfs_band_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ecrnx_hw *priv = file->private_data;
+ char buf[32];
+ int ret;
+ ssize_t read;
+
+ ret = scnprintf(buf, min_t(size_t, sizeof(buf) - 1, count),
+ "BAND=%d\n", priv->phy.sec_chan.band);
+
+ read = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
+
+ return read;
+}
+
+static ssize_t ecrnx_dbgfs_band_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ecrnx_hw *priv = file->private_data;
+ char buf[32];
+ int val;
+ size_t len = min_t(size_t, count, sizeof(buf) - 1);
+
+ if (copy_from_user(buf, user_buf, len))
+ return -EFAULT;
+
+ buf[len] = '\0';
+
+#ifdef CONFIG_ECRNX_5G
+ if ((sscanf(buf, "%d", &val) > 0) && (val >= 0) && (val <= NL80211_BAND_5GHZ))
+#else
+ if ((sscanf(buf, "%d", &val) > 0) && (val >= 0) && (val <= NL80211_BAND_2GHZ))
+#endif
+ priv->phy.sec_chan.band = val;
+
+ return count;
+}
+
+DEBUGFS_READ_WRITE_FILE_OPS(band);
+
+static ssize_t ecrnx_dbgfs_type_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ecrnx_hw *priv = file->private_data;
+ char buf[32];
+ int ret;
+ ssize_t read;
+
+ ret = scnprintf(buf, min_t(size_t, sizeof(buf) - 1, count),
+ "TYPE=%d\n", priv->phy.sec_chan.type);
+
+ read = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
+
+ return read;
+}
+
+static ssize_t ecrnx_dbgfs_type_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ecrnx_hw *priv = file->private_data;
+ char buf[32];
+ int val;
+ size_t len = min_t(size_t, count, sizeof(buf) - 1);
+
+ if (copy_from_user(buf, user_buf, len))
+ return -EFAULT;
+
+ buf[len] = '\0';
+
+ if ((sscanf(buf, "%d", &val) > 0) && (val >= PHY_CHNL_BW_20) &&
+ (val <= PHY_CHNL_BW_80P80))
+ priv->phy.sec_chan.type = val;
+
+ return count;
+}
+
+DEBUGFS_READ_WRITE_FILE_OPS(type);
+
+static ssize_t ecrnx_dbgfs_prim20_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ecrnx_hw *priv = file->private_data;
+ char buf[32];
+ int ret;
+ ssize_t read;
+
+ ret = scnprintf(buf, min_t(size_t, sizeof(buf) - 1, count),
+ "PRIM20=%dMHz\n", priv->phy.sec_chan.prim20_freq);
+
+ read = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
+
+ return read;
+}
+
+static ssize_t ecrnx_dbgfs_prim20_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ecrnx_hw *priv = file->private_data;
+ char buf[32];
+ int val;
+ size_t len = min_t(size_t, count, sizeof(buf) - 1);
+
+ if (copy_from_user(buf, user_buf, len))
+ return -EFAULT;
+
+ buf[len] = '\0';
+
+ if (sscanf(buf, "%d", &val) > 0)
+ priv->phy.sec_chan.prim20_freq = val;
+
+ return count;
+}
+
+DEBUGFS_READ_WRITE_FILE_OPS(prim20);
+
+static ssize_t ecrnx_dbgfs_center1_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ecrnx_hw *priv = file->private_data;
+ char buf[32];
+ int ret;
+ ssize_t read;
+
+ ret = scnprintf(buf, min_t(size_t, sizeof(buf) - 1, count),
+ "CENTER1=%dMHz\n", priv->phy.sec_chan.center1_freq);
+
+ read = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
+
+ return read;
+}
+
+static ssize_t ecrnx_dbgfs_center1_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ecrnx_hw *priv = file->private_data;
+ char buf[32];
+ int val;
+ size_t len = min_t(size_t, count, sizeof(buf) - 1);
+
+ if (copy_from_user(buf, user_buf, len))
+ return -EFAULT;
+
+ buf[len] = '\0';
+
+ if (sscanf(buf, "%d", &val) > 0)
+ priv->phy.sec_chan.center1_freq = val;
+
+ return count;
+}
+
+DEBUGFS_READ_WRITE_FILE_OPS(center1);
+
+static ssize_t ecrnx_dbgfs_center2_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ecrnx_hw *priv = file->private_data;
+ char buf[32];
+ int ret;
+ ssize_t read;
+
+ ret = scnprintf(buf, min_t(size_t, sizeof(buf) - 1, count),
+ "CENTER2=%dMHz\n", priv->phy.sec_chan.center2_freq);
+
+ read = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
+
+ return read;
+}
+
+static ssize_t ecrnx_dbgfs_center2_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ecrnx_hw *priv = file->private_data;
+ char buf[32];
+ int val;
+ size_t len = min_t(size_t, count, sizeof(buf) - 1);
+
+ if (copy_from_user(buf, user_buf, len))
+ return -EFAULT;
+
+ buf[len] = '\0';
+
+ if (sscanf(buf, "%d", &val) > 0)
+ priv->phy.sec_chan.center2_freq = val;
+
+ return count;
+}
+
+DEBUGFS_READ_WRITE_FILE_OPS(center2);
+
+
+static ssize_t ecrnx_dbgfs_set_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ return 0;
+}
+
+static ssize_t ecrnx_dbgfs_set_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ecrnx_hw *priv = file->private_data;
+
+ ecrnx_send_set_channel(priv, 1, NULL);
+ ecrnx_radar_detection_enable(&priv->radar, ECRNX_RADAR_DETECT_ENABLE,
+ ECRNX_RADAR_FCU);
+
+ return count;
+}
+
+DEBUGFS_READ_WRITE_FILE_OPS(set);
+#endif /* CONFIG_ECRNX_RADAR */
+
+#ifdef CONFIG_ECRNX_FULLMAC
+
+#define LINE_MAX_SZ 150
+
+struct st {
+ char line[LINE_MAX_SZ + 1];
+ unsigned int r_idx;
+};
+
+static int compare_idx(const void *st1, const void *st2)
+{
+ int index1 = ((struct st *)st1)->r_idx;
+ int index2 = ((struct st *)st2)->r_idx;
+
+ if (index1 > index2) return 1;
+ if (index1 < index2) return -1;
+
+ return 0;
+}
+
+static const int ru_size[] =
+{
+ 26,
+ 52,
+ 106,
+ 242,
+ 484,
+ 996
+};
+
+static int print_rate(char *buf, int size, int format, int nss, int mcs, int bw,
+ int sgi, int pre, int dcm, int *r_idx)
+{
+ int res = 0;
+ int bitrates_cck[4] = { 10, 20, 55, 110 };
+ int bitrates_ofdm[8] = { 6, 9, 12, 18, 24, 36, 48, 54};
+ char he_gi[3][4] = {"0.8", "1.6", "3.2"};
+
+ if (format < FORMATMOD_HT_MF) {
+ if (mcs < 4) {
+ if (r_idx) {
+ *r_idx = (mcs * 2) + pre;
+ res = scnprintf(buf, size - res, "%3d ", *r_idx);
+ }
+ res += scnprintf(&buf[res], size - res, "L-CCK/%cP %2u.%1uM ",
+ pre > 0 ? 'L' : 'S',
+ bitrates_cck[mcs] / 10,
+ bitrates_cck[mcs] % 10);
+ } else {
+ mcs -= 4;
+ if (r_idx) {
+ *r_idx = N_CCK + mcs;
+ res = scnprintf(buf, size - res, "%3d ", *r_idx);
+ }
+ res += scnprintf(&buf[res], size - res, "L-OFDM %2u.0M ",
+ bitrates_ofdm[mcs]);
+ }
+ } else if (format < FORMATMOD_VHT) {
+ if (r_idx) {
+ *r_idx = N_CCK + N_OFDM + nss * 32 + mcs * 4 + bw * 2 + sgi;
+ res = scnprintf(buf, size - res, "%3d ", *r_idx);
+ }
+ mcs += nss * 8;
+ res += scnprintf(&buf[res], size - res, "HT%d/%cGI MCS%-2d ",
+ 20 * (1 << bw), sgi ? 'S' : 'L', mcs);
+ } else if (format == FORMATMOD_VHT){
+ if (r_idx) {
+ *r_idx = N_CCK + N_OFDM + N_HT + nss * 80 + mcs * 8 + bw * 2 + sgi;
+ res = scnprintf(buf, size - res, "%3d ", *r_idx);
+ }
+ res += scnprintf(&buf[res], size - res, "VHT%d/%cGI%*cMCS%d/%1d ",
+ 20 * (1 << bw), sgi ? 'S' : 'L', bw > 2 ? 9 : 10, ' ',
+ mcs, nss + 1);
+ } else if (format == FORMATMOD_HE_SU){
+ if (r_idx) {
+ *r_idx = N_CCK + N_OFDM + N_HT + N_VHT + nss * 144 + mcs * 12 + bw * 3 + sgi;
+ res = scnprintf(buf, size - res, "%3d ", *r_idx);
+ }
+ res += scnprintf(&buf[res], size - res, "HE%d/GI%s%4s%*cMCS%d/%1d%*c",
+ 20 * (1 << bw), he_gi[sgi], dcm ? "/DCM" : "",
+ bw > 2 ? 4 : 5, ' ', mcs, nss + 1, mcs > 9 ? 1 : 2, ' ');
+ } else {
+ if (r_idx) {
+ *r_idx = N_CCK + N_OFDM + N_HT + N_VHT + N_HE_SU + nss * 216 + mcs * 18 + bw * 3 + sgi;
+ res = scnprintf(buf, size - res, "%3d ", *r_idx);
+ }
+ res += scnprintf(&buf[res], size - res, "HEMU-%d/GI%s%*cMCS%d/%1d%*c",
+ ru_size[bw], he_gi[sgi], bw > 1 ? 1 : 2, ' ',
+ mcs, nss + 1, mcs > 9 ? 1 : 2, ' ');
+
+ }
+
+ return res;
+}
+
+static int print_rate_from_cfg(char *buf, int size, u32 rate_config, int *r_idx, int ru_size)
+{
+ union ecrnx_rate_ctrl_info *r_cfg = (union ecrnx_rate_ctrl_info *)&rate_config;
+ union ecrnx_mcs_index *mcs_index = (union ecrnx_mcs_index *)&rate_config;
+ unsigned int ft, pre, gi, bw, nss, mcs, dcm, len;
+
+ ft = r_cfg->formatModTx;
+ pre = r_cfg->giAndPreTypeTx >> 1;
+ gi = r_cfg->giAndPreTypeTx;
+ bw = r_cfg->bwTx;
+ dcm = 0;
+ if (ft == FORMATMOD_HE_MU) {
+ mcs = mcs_index->he.mcs;
+ nss = mcs_index->he.nss;
+ bw = ru_size;
+ dcm = r_cfg->dcmTx;
+ } else if (ft == FORMATMOD_HE_SU) {
+ mcs = mcs_index->he.mcs;
+ nss = mcs_index->he.nss;
+ dcm = r_cfg->dcmTx;
+ } else if (ft == FORMATMOD_VHT) {
+ mcs = mcs_index->vht.mcs;
+ nss = mcs_index->vht.nss;
+ } else if (ft >= FORMATMOD_HT_MF) {
+ mcs = mcs_index->ht.mcs;
+ nss = mcs_index->ht.nss;
+ } else {
+ mcs = mcs_index->legacy;
+ nss = 0;
+ }
+
+ len = print_rate(buf, size, ft, nss, mcs, bw, gi, pre, dcm, r_idx);
+ return len;
+}
+
+static void idx_to_rate_cfg(int idx, union ecrnx_rate_ctrl_info *r_cfg, int *ru_size)
+{
+ r_cfg->value = 0;
+ if (idx < N_CCK)
+ {
+ r_cfg->formatModTx = FORMATMOD_NON_HT;
+ r_cfg->giAndPreTypeTx = (idx & 1) << 1;
+ r_cfg->mcsIndexTx = idx / 2;
+ }
+ else if (idx < (N_CCK + N_OFDM))
+ {
+ r_cfg->formatModTx = FORMATMOD_NON_HT;
+ r_cfg->mcsIndexTx = idx - N_CCK + 4;
+ }
+ else if (idx < (N_CCK + N_OFDM + N_HT))
+ {
+ union ecrnx_mcs_index *r = (union ecrnx_mcs_index *)r_cfg;
+
+ idx -= (N_CCK + N_OFDM);
+ r_cfg->formatModTx = FORMATMOD_HT_MF;
+ r->ht.nss = idx / (8*2*2);
+ r->ht.mcs = (idx % (8*2*2)) / (2*2);
+ r_cfg->bwTx = ((idx % (8*2*2)) % (2*2)) / 2;
+ r_cfg->giAndPreTypeTx = idx & 1;
+ }
+ else if (idx < (N_CCK + N_OFDM + N_HT + N_VHT))
+ {
+ union ecrnx_mcs_index *r = (union ecrnx_mcs_index *)r_cfg;
+
+ idx -= (N_CCK + N_OFDM + N_HT);
+ r_cfg->formatModTx = FORMATMOD_VHT;
+ r->vht.nss = idx / (10*4*2);
+ r->vht.mcs = (idx % (10*4*2)) / (4*2);
+ r_cfg->bwTx = ((idx % (10*4*2)) % (4*2)) / 2;
+ r_cfg->giAndPreTypeTx = idx & 1;
+ }
+ else if (idx < (N_CCK + N_OFDM + N_HT + N_VHT + N_HE_SU))
+ {
+ union ecrnx_mcs_index *r = (union ecrnx_mcs_index *)r_cfg;
+
+ idx -= (N_CCK + N_OFDM + N_HT + N_VHT);
+ r_cfg->formatModTx = FORMATMOD_HE_SU;
+ r->vht.nss = idx / (12*4*3);
+ r->vht.mcs = (idx % (12*4*3)) / (4*3);
+ r_cfg->bwTx = ((idx % (12*4*3)) % (4*3)) / 3;
+ r_cfg->giAndPreTypeTx = idx % 3;
+ }
+ else
+ {
+ union ecrnx_mcs_index *r = (union ecrnx_mcs_index *)r_cfg;
+
+ BUG_ON(ru_size == NULL);
+
+ idx -= (N_CCK + N_OFDM + N_HT + N_VHT + N_HE_SU);
+ r_cfg->formatModTx = FORMATMOD_HE_MU;
+ r->vht.nss = idx / (12*6*3);
+ r->vht.mcs = (idx % (12*6*3)) / (6*3);
+ *ru_size = ((idx % (12*6*3)) % (6*3)) / 3;
+ r_cfg->giAndPreTypeTx = idx % 3;
+ r_cfg->bwTx = 0;
+ }
+}
+
+static struct ecrnx_sta* ecrnx_dbgfs_get_sta(struct ecrnx_hw *ecrnx_hw,
+ char* mac_addr)
+{
+ u8 mac[6];
+
+ if (sscanf(mac_addr, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
+ &mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5]) != 6)
+ return NULL;
+ return ecrnx_get_sta(ecrnx_hw, mac);
+}
+
+static ssize_t ecrnx_dbgfs_twt_request_read(struct file *file,
+ char __user *user_buf,
+ size_t count,
+ loff_t *ppos)
+{
+ char buf[750];
+ ssize_t read;
+ struct ecrnx_hw *priv = file->private_data;
+ struct ecrnx_sta *sta = NULL;
+ int len;
+
+ /* Get the station index from MAC address */
+ sta = ecrnx_dbgfs_get_sta(priv, file->f_path.dentry->d_parent->d_parent->d_iname);
+ if (sta == NULL)
+ return -EINVAL;
+ if (sta->twt_ind.sta_idx != ECRNX_INVALID_STA)
+ {
+ struct twt_conf_tag *conf = &sta->twt_ind.conf;
+ if (sta->twt_ind.resp_type == MAC_TWT_SETUP_ACCEPT)
+ len = scnprintf(buf, sizeof(buf) - 1, "Accepted configuration");
+ else if (sta->twt_ind.resp_type == MAC_TWT_SETUP_ALTERNATE)
+ len = scnprintf(buf, sizeof(buf) - 1, "Alternate configuration proposed by AP");
+ else if (sta->twt_ind.resp_type == MAC_TWT_SETUP_DICTATE)
+ len = scnprintf(buf, sizeof(buf) - 1, "AP dictates the following configuration");
+ else if (sta->twt_ind.resp_type == MAC_TWT_SETUP_REJECT)
+ len = scnprintf(buf, sizeof(buf) - 1, "AP rejects the following configuration");
+ else
+ {
+ len = scnprintf(buf, sizeof(buf) - 1, "Invalid response from the peer");
+ goto end;
+ }
+ len += scnprintf(&buf[len], sizeof(buf) - 1 - len,":\n"
+ "flow_type = %d\n"
+ "wake interval mantissa = %d\n"
+ "wake interval exponent = %d\n"
+ "wake interval = %d us\n"
+ "nominal minimum wake duration = %d us\n",
+ conf->flow_type, conf->wake_int_mantissa,
+ conf->wake_int_exp,
+ conf->wake_int_mantissa << conf->wake_int_exp,
+ conf->wake_dur_unit ?
+ conf->min_twt_wake_dur * 1024:
+ conf->min_twt_wake_dur * 256);
+ }
+ else
+ {
+ len = scnprintf(buf, min_t(size_t, sizeof(buf) - 1, count),
+ "setup_command = <0: request, 1: suggest, 2: demand>,"
+ "flow_type = <0: announced, 1: unannounced>,"
+ "wake_interval_mantissa = <0 if setup request and no constraints>,"
+ "wake_interval_exp = <0 if setup request and no constraints>,"
+ "nominal_min_wake_dur = <0 if setup request and no constraints>,"
+ "wake_dur_unit = <0: 256us, 1: tu>");
+ }
+ end:
+ read = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+ return read;
+}
+
+static ssize_t ecrnx_dbgfs_twt_request_write(struct file *file,
+ const char __user *user_buf,
+ size_t count,
+ loff_t *ppos)
+{
+ char *accepted_params[] = {"setup_command",
+ "flow_type",
+ "wake_interval_mantissa",
+ "wake_interval_exp",
+ "nominal_min_wake_dur",
+ "wake_dur_unit",
+ 0
+ };
+ struct twt_conf_tag twt_conf;
+ struct twt_setup_cfm twt_setup_cfm;
+ struct ecrnx_sta *sta = NULL;
+ struct ecrnx_hw *priv = file->private_data;
+ char param[30];
+ char *line;
+ int error = 1, i, val, setup_command = -1;
+ bool_l found;
+ char *buf = kmalloc(1024, GFP_ATOMIC);
+ size_t len = 1024 - 1;
+
+ if(!buf){
+ return 0;
+ }
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+ /* Get the station index from MAC address */
+ sta = ecrnx_dbgfs_get_sta(priv, file->f_path.dentry->d_parent->d_parent->d_iname);
+ if (sta == NULL){
+ kfree(buf);
+ return -EINVAL;
+ }
+
+ /* Get the content of the file */
+ if (copy_from_user(buf, user_buf, len)){
+ kfree(buf);
+ return -EFAULT;
+ }
+
+ buf[len] = '\0';
+ memset(&twt_conf, 0, sizeof(twt_conf));
+
+ line = buf;
+ /* Get the content of the file */
+ while (line != NULL)
+ {
+ if (sscanf(line, "%s = %d", param, &val) == 2)
+ {
+ i = 0;
+ found = false;
+ // Check if parameter is valid
+ while(accepted_params[i])
+ {
+ if (strcmp(accepted_params[i], param) == 0)
+ {
+ found = true;
+ break;
+ }
+ i++;
+ }
+
+ if (!found)
+ {
+ dev_err(priv->dev, "%s: parameter %s is not valid\n", __func__, param);
+ kfree(buf);
+ return -EINVAL;
+ }
+
+ if (!strcmp(param, "setup_command"))
+ {
+ setup_command = val;
+ }
+ else if (!strcmp(param, "flow_type"))
+ {
+ twt_conf.flow_type = val;
+ }
+ else if (!strcmp(param, "wake_interval_mantissa"))
+ {
+ twt_conf.wake_int_mantissa = val;
+ }
+ else if (!strcmp(param, "wake_interval_exp"))
+ {
+ twt_conf.wake_int_exp = val;
+ }
+ else if (!strcmp(param, "nominal_min_wake_dur"))
+ {
+ twt_conf.min_twt_wake_dur = val;
+ }
+ else if (!strcmp(param, "wake_dur_unit"))
+ {
+ twt_conf.wake_dur_unit = val;
+ }
+ }
+ else
+ {
+ dev_err(priv->dev, "%s: Impossible to read TWT configuration option\n", __func__);
+ kfree(buf);
+ return -EFAULT;
+ }
+ line = strchr(line, ',');
+ if(line == NULL)
+ break;
+ line++;
+ }
+
+ if (setup_command == -1)
+ {
+ dev_err(priv->dev, "%s: TWT missing setup command\n", __func__);
+ kfree(buf);
+ return -EFAULT;
+ }
+
+ // Forward the request to the LMAC
+ if ((error = ecrnx_send_twt_request(priv, setup_command, sta->vif_idx,
+ &twt_conf, &twt_setup_cfm)) != 0){
+ kfree(buf);
+ return error;
+ }
+
+ // Check the status
+ if (twt_setup_cfm.status != CO_OK){
+ kfree(buf);
+ return -EIO;
+ }
+
+ kfree(buf);
+ return count;
+}
+DEBUGFS_READ_WRITE_FILE_OPS(twt_request);
+
+static ssize_t ecrnx_dbgfs_twt_teardown_read(struct file *file,
+ char __user *user_buf,
+ size_t count,
+ loff_t *ppos)
+{
+ char buf[512];
+ int ret;
+ ssize_t read;
+
+
+ ret = scnprintf(buf, min_t(size_t, sizeof(buf) - 1, count),
+ "TWT teardown format:\n\n"
+ "flow_id = <ID>\n");
+ read = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
+
+ return read;
+}
+
+static ssize_t ecrnx_dbgfs_twt_teardown_write(struct file *file,
+ const char __user *user_buf,
+ size_t count,
+ loff_t *ppos)
+{
+ struct twt_teardown_req twt_teardown;
+ struct twt_teardown_cfm twt_teardown_cfm;
+ struct ecrnx_sta *sta = NULL;
+ struct ecrnx_hw *priv = file->private_data;
+ char buf[256];
+ char *line;
+ int error = 1;
+ size_t len = min_t(size_t, count, sizeof(buf) - 1);
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+ /* Get the station index from MAC address */
+ sta = ecrnx_dbgfs_get_sta(priv, file->f_path.dentry->d_parent->d_parent->d_iname);
+ if (sta == NULL)
+ return -EINVAL;
+
+ /* Get the content of the file */
+ if (copy_from_user(buf, user_buf, len))
+ return -EINVAL;
+
+ buf[len] = '\0';
+ memset(&twt_teardown, 0, sizeof(twt_teardown));
+
+ /* Get the content of the file */
+ line = buf;
+
+ if (sscanf(line, "flow_id = %d", (int *) &twt_teardown.id) != 1)
+ {
+ dev_err(priv->dev, "%s: Invalid TWT configuration\n", __func__);
+ return -EINVAL;
+ }
+
+ twt_teardown.neg_type = 0;
+ twt_teardown.all_twt = 0;
+ twt_teardown.vif_idx = sta->vif_idx;
+
+ // Forward the request to the LMAC
+ if ((error = ecrnx_send_twt_teardown(priv, &twt_teardown, &twt_teardown_cfm)) != 0)
+ return error;
+
+ // Check the status
+ if (twt_teardown_cfm.status != CO_OK)
+ return -EIO;
+
+ return count;
+}
+DEBUGFS_READ_WRITE_FILE_OPS(twt_teardown);
+
+static ssize_t ecrnx_dbgfs_rc_stats_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ecrnx_sta *sta = NULL;
+ struct ecrnx_hw *priv = file->private_data;
+ char *buf;
+ int bufsz, len = 0;
+ ssize_t read;
+ int i = 0;
+ int error = 0;
+ struct me_rc_stats_cfm me_rc_stats_cfm;
+ unsigned int no_samples;
+ struct st *st;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ /* everything should fit in one call */
+ if (*ppos)
+ return 0;
+
+ /* Get the station index from MAC address */
+ sta = ecrnx_dbgfs_get_sta(priv, file->f_path.dentry->d_parent->d_parent->d_iname);
+ if (sta == NULL)
+ return -EINVAL;
+
+ /* Forward the information to the LMAC */
+ if ((error = ecrnx_send_me_rc_stats(priv, sta->sta_idx, &me_rc_stats_cfm)))
+ return error;
+
+ no_samples = me_rc_stats_cfm.no_samples;
+ if (no_samples == 0)
+ return 0;
+
+ bufsz = no_samples * LINE_MAX_SZ + 500;
+
+ buf = kmalloc(bufsz + 1, GFP_ATOMIC);
+ if (buf == NULL)
+ return 0;
+
+ st = kmalloc(sizeof(struct st) * no_samples, GFP_ATOMIC);
+ if (st == NULL)
+ {
+ kfree(buf);
+ return 0;
+ }
+
+ for (i = 0; i < no_samples; i++)
+ {
+ unsigned int tp, eprob;
+ len = print_rate_from_cfg(st[i].line, LINE_MAX_SZ,
+ me_rc_stats_cfm.rate_stats[i].rate_config,
+ &st[i].r_idx, 0);
+
+ if (me_rc_stats_cfm.sw_retry_step != 0)
+ {
+ len += scnprintf(&st[i].line[len], LINE_MAX_SZ - len, "%c",
+ me_rc_stats_cfm.retry_step_idx[me_rc_stats_cfm.sw_retry_step] == i ? '*' : ' ');
+ }
+ else
+ {
+ len += scnprintf(&st[i].line[len], LINE_MAX_SZ - len, " ");
+ }
+ len += scnprintf(&st[i].line[len], LINE_MAX_SZ - len, "%c",
+ me_rc_stats_cfm.retry_step_idx[0] == i ? 'T' : ' ');
+ len += scnprintf(&st[i].line[len], LINE_MAX_SZ - len, "%c",
+ me_rc_stats_cfm.retry_step_idx[1] == i ? 't' : ' ');
+ len += scnprintf(&st[i].line[len], LINE_MAX_SZ - len, "%c ",
+ me_rc_stats_cfm.retry_step_idx[2] == i ? 'P' : ' ');
+
+ tp = me_rc_stats_cfm.tp[i] / 10;
+ len += scnprintf(&st[i].line[len], LINE_MAX_SZ - len, " %4u.%1u",
+ tp / 10, tp % 10);
+
+ eprob = ((me_rc_stats_cfm.rate_stats[i].probability * 1000) >> 16) + 1;
+ len += scnprintf(&st[i].line[len],LINE_MAX_SZ - len,
+ " %4u.%1u %5u(%6u) %6u",
+ eprob / 10, eprob % 10,
+ me_rc_stats_cfm.rate_stats[i].success,
+ me_rc_stats_cfm.rate_stats[i].attempts,
+ me_rc_stats_cfm.rate_stats[i].sample_skipped);
+ }
+ len = scnprintf(buf, bufsz ,
+ "\nTX rate info for %02X:%02X:%02X:%02X:%02X:%02X:\n",
+ sta->mac_addr[0], sta->mac_addr[1], sta->mac_addr[2],
+ sta->mac_addr[3], sta->mac_addr[4], sta->mac_addr[5]);
+
+ len += scnprintf(&buf[len], bufsz - len,
+ " # type rate tpt eprob ok( tot) skipped\n");
+
+ // add sorted statistics to the buffer
+ sort(st, no_samples, sizeof(st[0]), compare_idx, NULL);
+ for (i = 0; i < no_samples; i++)
+ {
+ len += scnprintf(&buf[len], bufsz - len, "%s\n", st[i].line);
+ }
+
+ // display HE TB statistics if any
+ if (me_rc_stats_cfm.rate_stats[RC_HE_STATS_IDX].rate_config != 0) {
+ unsigned int tp, eprob;
+ struct rc_rate_stats *rate_stats = &me_rc_stats_cfm.rate_stats[RC_HE_STATS_IDX];
+ int ru_index = rate_stats->ru_and_length & 0x07;
+ int ul_length = rate_stats->ru_and_length >> 3;
+
+ len += scnprintf(&buf[len], bufsz - len,
+ "\nHE TB rate info:\n");
+
+ len += scnprintf(&buf[len], bufsz - len,
+ " type rate tpt eprob ok( tot) ul_length\n ");
+ len += print_rate_from_cfg(&buf[len], bufsz - len, rate_stats->rate_config,
+ NULL, ru_index);
+
+ tp = me_rc_stats_cfm.tp[RC_HE_STATS_IDX] / 10;
+ len += scnprintf(&buf[len], bufsz - len, " %4u.%1u",
+ tp / 10, tp % 10);
+
+ eprob = ((rate_stats->probability * 1000) >> 16) + 1;
+ len += scnprintf(&buf[len],bufsz - len,
+ " %4u.%1u %5u(%6u) %6u\n",
+ eprob / 10, eprob % 10,
+ rate_stats->success,
+ rate_stats->attempts,
+ ul_length);
+ }
+
+ len += scnprintf(&buf[len], bufsz - len, "\n MPDUs AMPDUs AvLen trialP");
+ len += scnprintf(&buf[len], bufsz - len, "\n%6u %6u %3d.%1d %6u\n",
+ me_rc_stats_cfm.ampdu_len,
+ me_rc_stats_cfm.ampdu_packets,
+ me_rc_stats_cfm.avg_ampdu_len >> 16,
+ ((me_rc_stats_cfm.avg_ampdu_len * 10) >> 16) % 10,
+ me_rc_stats_cfm.sample_wait);
+
+ read = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+
+ kfree(buf);
+ kfree(st);
+
+ return read;
+}
+
+DEBUGFS_READ_FILE_OPS(rc_stats);
+
+static ssize_t ecrnx_dbgfs_rc_fixed_rate_idx_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ecrnx_sta *sta = NULL;
+ struct ecrnx_hw *priv = file->private_data;
+ char buf[10];
+ int fixed_rate_idx = -1;
+ union ecrnx_rate_ctrl_info rate_config;
+ int error = 0;
+ size_t len = min_t(size_t, count, sizeof(buf) - 1);
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ /* Get the station index from MAC address */
+ sta = ecrnx_dbgfs_get_sta(priv, file->f_path.dentry->d_parent->d_parent->d_iname);
+ if (sta == NULL)
+ return -EINVAL;
+
+ /* Get the content of the file */
+ if (copy_from_user(buf, user_buf, len))
+ return -EFAULT;
+ buf[len] = '\0';
+ sscanf(buf, "%i\n", &fixed_rate_idx);
+
+ /* Convert rate index into rate configuration */
+ if ((fixed_rate_idx < 0) || (fixed_rate_idx >= (N_CCK + N_OFDM + N_HT + N_VHT + N_HE_SU)))
+ {
+ // disable fixed rate
+ rate_config.value = (u32)-1;
+ }
+ else
+ {
+ idx_to_rate_cfg(fixed_rate_idx, &rate_config, NULL);
+ }
+
+ // Forward the request to the LMAC
+ if ((error = ecrnx_send_me_rc_set_rate(priv, sta->sta_idx,
+ (u16)rate_config.value)) != 0)
+ {
+ return error;
+ }
+
+ priv->debugfs.rc_config[sta->sta_idx] = (int)rate_config.value;
+ return len;
+}
+
+DEBUGFS_WRITE_FILE_OPS(rc_fixed_rate_idx);
+
+static ssize_t ecrnx_dbgfs_last_rx_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ecrnx_sta *sta = NULL;
+ struct ecrnx_hw *priv = file->private_data;
+ struct ecrnx_rx_rate_stats *rate_stats;
+ char *buf;
+ int bufsz, i, len = 0;
+ ssize_t read;
+ unsigned int fmt, pre, bw, nss, mcs, gi, dcm = 0;
+ struct rx_vector_1 *last_rx;
+ char hist[] = "##################################################";
+ int hist_len = sizeof(hist) - 1;
+ u8 nrx;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ /* everything should fit in one call */
+ if (*ppos)
+ return 0;
+
+ /* Get the station index from MAC address */
+ sta = ecrnx_dbgfs_get_sta(priv, file->f_path.dentry->d_parent->d_parent->d_iname);
+ if (sta == NULL)
+ return -EINVAL;
+
+ rate_stats = &sta->stats.rx_rate;
+ bufsz = (rate_stats->rate_cnt * ( 50 + hist_len) + 200);
+ buf = kmalloc(bufsz + 1, GFP_ATOMIC);
+ if (buf == NULL)
+ return 0;
+
+ // Get number of RX paths
+ nrx = (priv->version_cfm.version_phy_1 & MDM_NRX_MASK) >> MDM_NRX_LSB;
+
+ len += scnprintf(buf, bufsz,
+ "\nRX rate info for %02X:%02X:%02X:%02X:%02X:%02X:\n",
+ sta->mac_addr[0], sta->mac_addr[1], sta->mac_addr[2],
+ sta->mac_addr[3], sta->mac_addr[4], sta->mac_addr[5]);
+
+ // Display Statistics
+ for (i = 0 ; i < rate_stats->size ; i++ )
+ {
+ if (rate_stats->table[i]) {
+ union ecrnx_rate_ctrl_info rate_config;
+ int percent = ((/*(u64)*/rate_stats->table[i]) * 1000) / rate_stats->cpt;
+ int p;
+ int ru_size;
+
+ idx_to_rate_cfg(i, &rate_config, &ru_size);
+ len += print_rate_from_cfg(&buf[len], bufsz - len,
+ rate_config.value, NULL, ru_size);
+ p = (percent * hist_len) / 1000;
+ len += scnprintf(&buf[len], bufsz - len, ": %9d(%2d.%1d%%)%.*s\n",
+ rate_stats->table[i],
+ percent / 10, percent % 10, p, hist);
+ }
+ }
+
+ // Display detailed info of the last received rate
+ last_rx = &sta->stats.last_rx.rx_vect1;
+
+ len += scnprintf(&buf[len], bufsz - len,"\nLast received rate\n"
+ " type rate LDPC STBC BEAMFM DCM DOPPLER %s\n",
+ (nrx > 1) ? "rssi1(dBm) rssi2(dBm)" : "rssi(dBm)");
+
+ fmt = last_rx->format_mod;
+ bw = last_rx->ch_bw;
+ pre = last_rx->pre_type;
+ if (fmt >= FORMATMOD_HE_SU) {
+ mcs = last_rx->he.mcs;
+ nss = last_rx->he.nss;
+ gi = last_rx->he.gi_type;
+ if (fmt == FORMATMOD_HE_MU)
+ bw = last_rx->he.ru_size;
+ dcm = last_rx->he.dcm;
+ } else if (fmt == FORMATMOD_VHT) {
+ mcs = last_rx->vht.mcs;
+ nss = last_rx->vht.nss;
+ gi = last_rx->vht.short_gi;
+ } else if (fmt >= FORMATMOD_HT_MF) {
+ mcs = last_rx->ht.mcs % 8;
+ nss = last_rx->ht.mcs / 8;;
+ gi = last_rx->ht.short_gi;
+ } else {
+ BUG_ON((mcs = legrates_lut[last_rx->leg_rate].idx) == -1);
+ nss = 0;
+ gi = 0;
+ }
+
+ len += print_rate(&buf[len], bufsz - len, fmt, nss, mcs, bw, gi, pre, dcm, NULL);
+
+ /* flags for HT/VHT/HE */
+ if (fmt >= FORMATMOD_HE_SU) {
+ len += scnprintf(&buf[len], bufsz - len, " %c %c %c %c %c",
+ last_rx->he.fec ? 'L' : ' ',
+ last_rx->he.stbc ? 'S' : ' ',
+ last_rx->he.beamformed ? 'B' : ' ',
+ last_rx->he.dcm ? 'D' : ' ',
+ last_rx->he.doppler ? 'D' : ' ');
+ } else if (fmt == FORMATMOD_VHT) {
+ len += scnprintf(&buf[len], bufsz - len, " %c %c %c ",
+ last_rx->vht.fec ? 'L' : ' ',
+ last_rx->vht.stbc ? 'S' : ' ',
+ last_rx->vht.beamformed ? 'B' : ' ');
+ } else if (fmt >= FORMATMOD_HT_MF) {
+ len += scnprintf(&buf[len], bufsz - len, " %c %c ",
+ last_rx->ht.fec ? 'L' : ' ',
+ last_rx->ht.stbc ? 'S' : ' ');
+ } else {
+ len += scnprintf(&buf[len], bufsz - len, " ");
+ }
+ if (nrx > 1) {
+ len += scnprintf(&buf[len], bufsz - len, " %-4d %d\n",
+ last_rx->rssi1, last_rx->rssi1);
+ } else {
+ len += scnprintf(&buf[len], bufsz - len, " %d\n", last_rx->rssi1);
+ }
+
+ read = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+
+ kfree(buf);
+ return read;
+}
+
+static ssize_t ecrnx_dbgfs_last_rx_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ecrnx_sta *sta = NULL;
+ struct ecrnx_hw *priv = file->private_data;
+
+ /* Get the station index from MAC address */
+ sta = ecrnx_dbgfs_get_sta(priv, file->f_path.dentry->d_parent->d_parent->d_iname);
+ if (sta == NULL)
+ return -EINVAL;
+
+ /* Prevent from interrupt preemption as these statistics are updated under
+ * interrupt */
+ spin_lock_bh(&priv->tx_lock);
+ memset(sta->stats.rx_rate.table, 0,
+ sta->stats.rx_rate.size * sizeof(sta->stats.rx_rate.table[0]));
+ sta->stats.rx_rate.cpt = 0;
+ sta->stats.rx_rate.rate_cnt = 0;
+ spin_unlock_bh(&priv->tx_lock);
+
+ return count;
+}
+
+DEBUGFS_READ_WRITE_FILE_OPS(last_rx);
+
+#endif /* CONFIG_ECRNX_FULLMAC */
+
+/*
+ * trace helper
+ */
+void ecrnx_fw_trace_dump(struct ecrnx_hw *ecrnx_hw)
+{
+#ifndef CONFIG_ECRNX_ESWIN
+ /* may be called before ecrnx_dbgfs_register */
+ if (ecrnx_hw->plat->enabled && !ecrnx_hw->debugfs.fw_trace.buf.data) {
+ ecrnx_fw_trace_buf_init(&ecrnx_hw->debugfs.fw_trace.buf,
+ ecrnx_ipc_fw_trace_desc_get(ecrnx_hw));
+ }
+
+ if (!ecrnx_hw->debugfs.fw_trace.buf.data)
+ return;
+
+ _ecrnx_fw_trace_dump(&ecrnx_hw->debugfs.fw_trace.buf);
+#endif
+}
+
+void ecrnx_fw_trace_reset(struct ecrnx_hw *ecrnx_hw)
+{
+ _ecrnx_fw_trace_reset(&ecrnx_hw->debugfs.fw_trace, true);
+}
+
+void ecrnx_dbgfs_trigger_fw_dump(struct ecrnx_hw *ecrnx_hw, char *reason)
+{
+ ecrnx_send_dbg_trigger_req(ecrnx_hw, reason);
+}
+
+#ifdef CONFIG_ECRNX_FULLMAC
+static void _ecrnx_dbgfs_register_sta(struct ecrnx_debugfs *ecrnx_debugfs, struct ecrnx_sta *sta)
+{
+ struct ecrnx_hw *ecrnx_hw = container_of(ecrnx_debugfs, struct ecrnx_hw, debugfs);
+ struct dentry *dir_sta;
+ char sta_name[18];
+ struct dentry *dir_rc;
+ struct dentry *file;
+ struct ecrnx_rx_rate_stats *rate_stats = &sta->stats.rx_rate;
+ int nb_rx_rate = N_CCK + N_OFDM;
+ struct ecrnx_rc_config_save *rc_cfg, *next;
+
+ if (sta->sta_idx >= NX_REMOTE_STA_MAX) {
+ scnprintf(sta_name, sizeof(sta_name), "bc_mc");
+ } else {
+ scnprintf(sta_name, sizeof(sta_name), "%pM", sta->mac_addr);
+ }
+
+ if (!(dir_sta = debugfs_create_dir(sta_name, ecrnx_debugfs->dir_stas)))
+ goto error;
+ ecrnx_debugfs->dir_sta[sta->sta_idx] = dir_sta;
+
+ if (!(dir_rc = debugfs_create_dir("rc", ecrnx_debugfs->dir_sta[sta->sta_idx])))
+ goto error_after_dir;
+
+ ecrnx_debugfs->dir_rc_sta[sta->sta_idx] = dir_rc;
+
+ file = debugfs_create_file("stats", S_IRUSR, dir_rc, ecrnx_hw,
+ &ecrnx_dbgfs_rc_stats_ops);
+ if (IS_ERR_OR_NULL(file))
+ goto error_after_dir;
+
+ file = debugfs_create_file("fixed_rate_idx", S_IWUSR , dir_rc, ecrnx_hw,
+ &ecrnx_dbgfs_rc_fixed_rate_idx_ops);
+ if (IS_ERR_OR_NULL(file))
+ goto error_after_dir;
+
+ file = debugfs_create_file("rx_rate", S_IRUSR | S_IWUSR, dir_rc, ecrnx_hw,
+ &ecrnx_dbgfs_last_rx_ops);
+ if (IS_ERR_OR_NULL(file))
+ goto error_after_dir;
+
+ if (ecrnx_hw->mod_params->ht_on)
+ nb_rx_rate += N_HT;
+
+ if (ecrnx_hw->mod_params->vht_on)
+ nb_rx_rate += N_VHT;
+
+ if (ecrnx_hw->mod_params->he_on)
+ nb_rx_rate += N_HE_SU + N_HE_MU;
+
+ rate_stats->table = kzalloc(nb_rx_rate * sizeof(rate_stats->table[0]),
+ GFP_ATOMIC);
+ if (!rate_stats->table)
+ goto error_after_dir;
+
+ rate_stats->size = nb_rx_rate;
+ rate_stats->cpt = 0;
+ rate_stats->rate_cnt = 0;
+
+ /* By default enable rate contoller */
+ ecrnx_debugfs->rc_config[sta->sta_idx] = -1;
+
+ /* Unless we already fix the rate for this station */
+ list_for_each_entry_safe(rc_cfg, next, &ecrnx_debugfs->rc_config_save, list) {
+ if (jiffies_to_msecs(jiffies - rc_cfg->timestamp) > RC_CONFIG_DUR) {
+ list_del(&rc_cfg->list);
+ kfree(rc_cfg);
+ } else if (!memcmp(rc_cfg->mac_addr, sta->mac_addr, ETH_ALEN)) {
+ ecrnx_debugfs->rc_config[sta->sta_idx] = rc_cfg->rate;
+ list_del(&rc_cfg->list);
+ kfree(rc_cfg);
+ break;
+ }
+ }
+
+ if ((ecrnx_debugfs->rc_config[sta->sta_idx] >= 0) &&
+ ecrnx_send_me_rc_set_rate(ecrnx_hw, sta->sta_idx,
+ (u16)ecrnx_debugfs->rc_config[sta->sta_idx]))
+ ecrnx_debugfs->rc_config[sta->sta_idx] = -1;
+
+ if (ECRNX_VIF_TYPE(ecrnx_hw->vif_table[sta->vif_idx]) == NL80211_IFTYPE_STATION)
+ {
+ /* register the sta */
+ struct dentry *dir_twt;
+ struct dentry *file;
+
+ if (!(dir_twt = debugfs_create_dir("twt", ecrnx_debugfs->dir_sta[sta->sta_idx])))
+ goto error_after_dir;
+
+ ecrnx_debugfs->dir_twt_sta[sta->sta_idx] = dir_twt;
+
+ file = debugfs_create_file("request", S_IRUSR | S_IWUSR, dir_twt, ecrnx_hw,
+ &ecrnx_dbgfs_twt_request_ops);
+ if (IS_ERR_OR_NULL(file))
+ goto error_after_dir;
+
+ file = debugfs_create_file("teardown", S_IRUSR | S_IWUSR, dir_twt, ecrnx_hw,
+ &ecrnx_dbgfs_twt_teardown_ops);
+ if (IS_ERR_OR_NULL(file))
+ goto error_after_dir;
+
+ sta->twt_ind.sta_idx = ECRNX_INVALID_STA;
+ }
+ return;
+
+ error_after_dir:
+ debugfs_remove_recursive(ecrnx_debugfs->dir_sta[sta->sta_idx]);
+ ecrnx_debugfs->dir_sta[sta->sta_idx] = NULL;
+ ecrnx_debugfs->dir_rc_sta[sta->sta_idx] = NULL;
+ ecrnx_debugfs->dir_twt_sta[sta->sta_idx] = NULL;
+ error:
+ dev_err(ecrnx_hw->dev,
+ "Error while registering debug entry for sta %d\n", sta->sta_idx);
+}
+
+static void _ecrnx_dbgfs_unregister_sta(struct ecrnx_debugfs *ecrnx_debugfs, struct ecrnx_sta *sta)
+{
+ debugfs_remove_recursive(ecrnx_debugfs->dir_sta[sta->sta_idx]);
+ /* unregister the sta */
+ if (sta->stats.rx_rate.table) {
+ kfree(sta->stats.rx_rate.table);
+ sta->stats.rx_rate.table = NULL;
+ }
+ sta->stats.rx_rate.size = 0;
+ sta->stats.rx_rate.cpt = 0;
+ sta->stats.rx_rate.rate_cnt = 0;
+
+ /* If fix rate was set for this station, save the configuration in case
+ we reconnect to this station within RC_CONFIG_DUR msec */
+ if (ecrnx_debugfs->rc_config[sta->sta_idx] >= 0) {
+ struct ecrnx_rc_config_save *rc_cfg;
+ rc_cfg = kmalloc(sizeof(*rc_cfg), GFP_ATOMIC);
+ if (rc_cfg) {
+ rc_cfg->rate = ecrnx_debugfs->rc_config[sta->sta_idx];
+ rc_cfg->timestamp = jiffies;
+ memcpy(rc_cfg->mac_addr, sta->mac_addr, ETH_ALEN);
+ list_add_tail(&rc_cfg->list, &ecrnx_debugfs->rc_config_save);
+ }
+ }
+
+ ecrnx_debugfs->dir_sta[sta->sta_idx] = NULL;
+ ecrnx_debugfs->dir_rc_sta[sta->sta_idx] = NULL;
+ ecrnx_debugfs->dir_twt_sta[sta->sta_idx] = NULL;
+ sta->twt_ind.sta_idx = ECRNX_INVALID_STA;
+}
+
+static void ecrnx_sta_work(struct work_struct *ws)
+{
+ struct ecrnx_debugfs *ecrnx_debugfs = container_of(ws, struct ecrnx_debugfs, sta_work);
+ struct ecrnx_hw *ecrnx_hw = container_of(ecrnx_debugfs, struct ecrnx_hw, debugfs);
+ struct ecrnx_sta *sta;
+ uint8_t sta_idx;
+
+ sta_idx = ecrnx_debugfs->sta_idx;
+ if (sta_idx > (NX_REMOTE_STA_MAX + NX_VIRT_DEV_MAX)) {
+ WARN(1, "Invalid sta index %d", sta_idx);
+ return;
+ }
+
+ ecrnx_debugfs->sta_idx = ECRNX_INVALID_STA;
+ sta = &ecrnx_hw->sta_table[sta_idx];
+ if (!sta) {
+ WARN(1, "Invalid sta %d", sta_idx);
+ return;
+ }
+
+ if (ecrnx_debugfs->dir_sta[sta_idx] == NULL)
+ _ecrnx_dbgfs_register_sta(ecrnx_debugfs, sta);
+ else
+ _ecrnx_dbgfs_unregister_sta(ecrnx_debugfs, sta);
+
+ return;
+}
+
+void _ecrnx_dbgfs_sta_write(struct ecrnx_debugfs *ecrnx_debugfs, uint8_t sta_idx)
+{
+ if (ecrnx_debugfs->unregistering)
+ return;
+
+ ecrnx_debugfs->sta_idx = sta_idx;
+ schedule_work(&ecrnx_debugfs->sta_work);
+}
+
+void ecrnx_dbgfs_unregister_sta(struct ecrnx_hw *ecrnx_hw,
+ struct ecrnx_sta *sta)
+{
+ _ecrnx_dbgfs_sta_write(&ecrnx_hw->debugfs, sta->sta_idx);
+}
+
+void ecrnx_dbgfs_register_sta(struct ecrnx_hw *ecrnx_hw,
+ struct ecrnx_sta *sta)
+{
+ _ecrnx_dbgfs_sta_write(&ecrnx_hw->debugfs, sta->sta_idx);
+}
+#endif /* CONFIG_ECRNX_FULLMAC */
+
+int ecrnx_dbgfs_register(struct ecrnx_hw *ecrnx_hw, const char *name)
+{
+#ifdef CONFIG_ECRNX_SOFTMAC
+ struct dentry *phyd = ecrnx_hw->hw->wiphy->debugfsdir;
+#else
+ struct dentry *phyd = ecrnx_hw->wiphy->debugfsdir;
+#endif /* CONFIG_ECRNX_SOFTMAC */
+ struct ecrnx_debugfs *ecrnx_debugfs = &ecrnx_hw->debugfs;
+ struct dentry *dir_drv, *dir_diags, *dir_stas;
+
+ if (!(dir_drv = debugfs_create_dir(name, phyd)))
+ return -ENOMEM;
+
+ ecrnx_debugfs->dir = dir_drv;
+
+ if (!(dir_stas = debugfs_create_dir("stations", dir_drv)))
+ return -ENOMEM;
+
+ ecrnx_debugfs->dir_stas = dir_stas;
+ ecrnx_debugfs->unregistering = false;
+
+ if (!(dir_diags = debugfs_create_dir("diags", dir_drv)))
+ goto err;
+
+#ifdef CONFIG_ECRNX_FULLMAC
+ INIT_WORK(&ecrnx_debugfs->sta_work, ecrnx_sta_work);
+ INIT_LIST_HEAD(&ecrnx_debugfs->rc_config_save);
+ ecrnx_debugfs->sta_idx = ECRNX_INVALID_STA;
+#endif
+
+ DEBUGFS_ADD_U32(tcp_pacing_shift, dir_drv, &ecrnx_hw->tcp_pacing_shift,
+ S_IWUSR | S_IRUSR);
+ DEBUGFS_ADD_FILE(stats, dir_drv, S_IWUSR | S_IRUSR);
+#if 0
+ DEBUGFS_ADD_FILE(sys_stats, dir_drv, S_IRUSR);
+#endif
+#ifdef CONFIG_ECRNX_SOFTMAC
+ DEBUGFS_ADD_X64(rateidx, dir_drv, &ecrnx_hw->debugfs.rateidx);
+#endif
+ DEBUGFS_ADD_FILE(txq, dir_drv, S_IRUSR);
+ DEBUGFS_ADD_FILE(acsinfo, dir_drv, S_IRUSR);
+#ifdef CONFIG_ECRNX_MUMIMO_TX
+ DEBUGFS_ADD_FILE(mu_group, dir_drv, S_IRUSR);
+#endif
+
+#ifdef CONFIG_ECRNX_P2P_DEBUGFS
+ {
+ /* Create a p2p directory */
+ struct dentry *dir_p2p;
+ if (!(dir_p2p = debugfs_create_dir("p2p", dir_drv)))
+ goto err;
+
+ /* Add file allowing to control Opportunistic PS */
+ DEBUGFS_ADD_FILE(oppps, dir_p2p, S_IRUSR);
+ /* Add file allowing to control Notice of Absence */
+ DEBUGFS_ADD_FILE(noa, dir_p2p, S_IRUSR);
+ }
+#endif /* CONFIG_ECRNX_P2P_DEBUGFS */
+
+#if CONFIG_ECRNX_DBGFS_FW_TRACE
+ if (ecrnx_dbgfs_register_fw_dump(ecrnx_hw, dir_drv, dir_diags))
+ goto err;
+ DEBUGFS_ADD_FILE(fw_dbg, dir_diags, S_IWUSR | S_IRUSR);
+
+ if (!ecrnx_fw_trace_init(&ecrnx_hw->debugfs.fw_trace,
+ ecrnx_ipc_fw_trace_desc_get(ecrnx_hw))) {
+ DEBUGFS_ADD_FILE(fw_trace, dir_diags, S_IWUSR | S_IRUSR);
+ if (ecrnx_hw->debugfs.fw_trace.buf.nb_compo)
+ DEBUGFS_ADD_FILE(fw_trace_level, dir_diags, S_IWUSR | S_IRUSR);
+ } else {
+ ecrnx_debugfs->fw_trace.buf.data = NULL;
+ }
+#endif
+
+#ifdef CONFIG_ECRNX_RADAR
+ {
+ struct dentry *dir_radar, *dir_sec;
+ if (!(dir_radar = debugfs_create_dir("radar", dir_drv)))
+ goto err;
+
+ DEBUGFS_ADD_FILE(pulses_prim, dir_radar, S_IRUSR);
+ DEBUGFS_ADD_FILE(detected, dir_radar, S_IRUSR);
+ DEBUGFS_ADD_FILE(enable, dir_radar, S_IRUSR);
+
+ if (ecrnx_hw->phy.cnt == 2) {
+ DEBUGFS_ADD_FILE(pulses_sec, dir_radar, S_IRUSR);
+
+ if (!(dir_sec = debugfs_create_dir("sec", dir_radar)))
+ goto err;
+
+ DEBUGFS_ADD_FILE(band, dir_sec, S_IWUSR | S_IRUSR);
+ DEBUGFS_ADD_FILE(type, dir_sec, S_IWUSR | S_IRUSR);
+ DEBUGFS_ADD_FILE(prim20, dir_sec, S_IWUSR | S_IRUSR);
+ DEBUGFS_ADD_FILE(center1, dir_sec, S_IWUSR | S_IRUSR);
+ DEBUGFS_ADD_FILE(center2, dir_sec, S_IWUSR | S_IRUSR);
+ DEBUGFS_ADD_FILE(set, dir_sec, S_IWUSR | S_IRUSR);
+ }
+ }
+#endif /* CONFIG_ECRNX_RADAR */
+ return 0;
+
+err:
+ ecrnx_dbgfs_unregister(ecrnx_hw);
+ return -ENOMEM;
+}
+
+void ecrnx_dbgfs_unregister(struct ecrnx_hw *ecrnx_hw)
+{
+ struct ecrnx_debugfs *ecrnx_debugfs = &ecrnx_hw->debugfs;
+
+#ifdef CONFIG_ECRNX_FULLMAC
+ struct ecrnx_rc_config_save *cfg, *next;
+ list_for_each_entry_safe(cfg, next, &ecrnx_debugfs->rc_config_save, list) {
+ list_del(&cfg->list);
+ kfree(cfg);
+ }
+#endif /* CONFIG_ECRNX_FULLMAC */
+
+ if (!ecrnx_hw->debugfs.dir)
+ return;
+
+ spin_lock_bh(&ecrnx_debugfs->umh_lock);
+ ecrnx_debugfs->unregistering = true;
+ spin_unlock_bh(&ecrnx_debugfs->umh_lock);
+ ecrnx_wait_um_helper(ecrnx_hw);
+#if CONFIG_ECRNX_DBGFS_FW_TRACE
+ ecrnx_fw_trace_deinit(&ecrnx_hw->debugfs.fw_trace);
+#endif
+#ifdef CONFIG_ECRNX_FULLMAC
+ flush_work(&ecrnx_debugfs->sta_work);
+#endif
+ debugfs_remove_recursive(ecrnx_hw->debugfs.dir);
+ ecrnx_hw->debugfs.dir = NULL;
+}
+
diff --git a/drivers/net/wireless/eswin/ecrnx_debugfs.h b/drivers/net/wireless/eswin/ecrnx_debugfs.h
new file mode 100644
index 000000000000..da0338e12e2e
--- /dev/null
+++ b/drivers/net/wireless/eswin/ecrnx_debugfs.h
@@ -0,0 +1,198 @@
+/**
+ ******************************************************************************
+ *
+ * @file ecrnx_debugfs.h
+ *
+ * @brief Miscellaneous utility function definitions
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+
+
+#ifndef _ECRNX_DEBUGFS_H_
+#define _ECRNX_DEBUGFS_H_
+
+#include <linux/workqueue.h>
+#include <linux/if_ether.h>
+#include "ecrnx_fw_trace.h"
+
+struct ecrnx_hw;
+struct ecrnx_sta;
+
+#define DEBUGFS_ADD_FILE(name, parent, mode) do { \
+ struct dentry *__tmp; \
+ __tmp = debugfs_create_file(#name, mode, parent, ecrnx_hw, \
+ &ecrnx_dbgfs_##name##_ops); \
+ if (IS_ERR_OR_NULL(__tmp)) \
+ goto err; \
+ } while (0)
+
+#define DEBUGFS_ADD_BOOL(name, parent, ptr) do { \
+ struct dentry *__tmp; \
+ __tmp = debugfs_create_bool(#name, S_IWUSR | S_IRUSR, parent, ptr); \
+ if (IS_ERR_OR_NULL(__tmp)) \
+ goto err; \
+ } while (0)
+
+#define DEBUGFS_ADD_X64(name, parent, ptr) do { \
+ debugfs_create_x64(#name, S_IWUSR | S_IRUSR,parent, ptr); \
+ } while (0)
+
+#define DEBUGFS_ADD_U64(name, parent, ptr, mode) do { \
+ debugfs_create_u64(#name, mode, parent, ptr); \
+ } while (0)
+
+#define DEBUGFS_ADD_X32(name, parent, ptr) do { \
+ debugfs_create_x32(#name, S_IWUSR | S_IRUSR, parent, ptr); \
+ } while (0)
+
+#define DEBUGFS_ADD_U32(name, parent, ptr, mode) do { \
+ debugfs_create_u32(#name, mode, parent, ptr); \
+ } while (0)
+
+
+/* file operation */
+#define DEBUGFS_READ_FUNC(name) \
+ static ssize_t ecrnx_dbgfs_##name##_read(struct file *file, \
+ char __user *user_buf, \
+ size_t count, loff_t *ppos);
+
+#define DEBUGFS_WRITE_FUNC(name) \
+ static ssize_t ecrnx_dbgfs_##name##_write(struct file *file, \
+ const char __user *user_buf,\
+ size_t count, loff_t *ppos);
+
+#define DEBUGFS_OPEN_FUNC(name) \
+ static int ecrnx_dbgfs_##name##_open(struct inode *inode, \
+ struct file *file);
+
+#define DEBUGFS_RELEASE_FUNC(name) \
+ static int ecrnx_dbgfs_##name##_release(struct inode *inode, \
+ struct file *file);
+
+#define DEBUGFS_READ_FILE_OPS(name) \
+ DEBUGFS_READ_FUNC(name); \
+static const struct file_operations ecrnx_dbgfs_##name##_ops = { \
+ .read = ecrnx_dbgfs_##name##_read, \
+ .open = simple_open, \
+ .llseek = generic_file_llseek, \
+};
+
+#define DEBUGFS_WRITE_FILE_OPS(name) \
+ DEBUGFS_WRITE_FUNC(name); \
+static const struct file_operations ecrnx_dbgfs_##name##_ops = { \
+ .write = ecrnx_dbgfs_##name##_write, \
+ .open = simple_open, \
+ .llseek = generic_file_llseek, \
+};
+
+#define DEBUGFS_READ_WRITE_FILE_OPS(name) \
+ DEBUGFS_READ_FUNC(name); \
+ DEBUGFS_WRITE_FUNC(name); \
+static const struct file_operations ecrnx_dbgfs_##name##_ops = { \
+ .write = ecrnx_dbgfs_##name##_write, \
+ .read = ecrnx_dbgfs_##name##_read, \
+ .open = simple_open, \
+ .llseek = generic_file_llseek, \
+};
+
+#define DEBUGFS_READ_WRITE_OPEN_RELEASE_FILE_OPS(name) \
+ DEBUGFS_READ_FUNC(name); \
+ DEBUGFS_WRITE_FUNC(name); \
+ DEBUGFS_OPEN_FUNC(name); \
+ DEBUGFS_RELEASE_FUNC(name); \
+static const struct file_operations ecrnx_dbgfs_##name##_ops = { \
+ .write = ecrnx_dbgfs_##name##_write, \
+ .read = ecrnx_dbgfs_##name##_read, \
+ .open = ecrnx_dbgfs_##name##_open, \
+ .release = ecrnx_dbgfs_##name##_release, \
+ .llseek = generic_file_llseek, \
+};
+
+
+#ifdef CONFIG_ECRNX_DEBUGFS
+
+struct ecrnx_debugfs {
+ unsigned long long rateidx;
+ struct dentry *dir;
+ struct dentry *dir_stas;
+ bool trace_prst;
+
+ char helper_cmd[64];
+ struct work_struct helper_work;
+ bool helper_scheduled;
+ spinlock_t umh_lock;
+ bool unregistering;
+
+#ifndef CONFIG_ECRNX_FHOST
+ struct ecrnx_fw_trace fw_trace;
+#endif /* CONFIG_ECRNX_FHOST */
+
+#ifdef CONFIG_ECRNX_FULLMAC
+ struct work_struct sta_work;
+ struct dentry *dir_sta[NX_REMOTE_STA_MAX];
+ uint8_t sta_idx;
+ struct dentry *dir_rc;
+ struct dentry *dir_rc_sta[NX_REMOTE_STA_MAX];
+ int rc_config[NX_REMOTE_STA_MAX];
+ struct list_head rc_config_save;
+ struct dentry *dir_twt;
+ struct dentry *dir_twt_sta[NX_REMOTE_STA_MAX];
+#endif
+};
+
+#ifdef CONFIG_ECRNX_FULLMAC
+
+// Max duration in msecs to save rate config for a sta after disconnection
+#define RC_CONFIG_DUR 600000
+
+struct ecrnx_rc_config_save {
+ struct list_head list;
+ unsigned long timestamp;
+ int rate;
+ u8 mac_addr[ETH_ALEN];
+};
+#endif
+
+int ecrnx_dbgfs_register(struct ecrnx_hw *ecrnx_hw, const char *name);
+void ecrnx_dbgfs_unregister(struct ecrnx_hw *ecrnx_hw);
+int ecrnx_um_helper(struct ecrnx_debugfs *ecrnx_debugfs, const char *cmd);
+int ecrnx_trigger_um_helper(struct ecrnx_debugfs *ecrnx_debugfs);
+void ecrnx_wait_um_helper(struct ecrnx_hw *ecrnx_hw);
+#ifdef CONFIG_ECRNX_FULLMAC
+void ecrnx_dbgfs_register_sta(struct ecrnx_hw *ecrnx_hw, struct ecrnx_sta *sta);
+void ecrnx_dbgfs_unregister_sta(struct ecrnx_hw *ecrnx_hw, struct ecrnx_sta *sta);
+#endif
+
+int ecrnx_dbgfs_register_fw_dump(struct ecrnx_hw *ecrnx_hw,
+ struct dentry *dir_drv,
+ struct dentry *dir_diags);
+void ecrnx_dbgfs_trigger_fw_dump(struct ecrnx_hw *ecrnx_hw, char *reason);
+
+void ecrnx_fw_trace_dump(struct ecrnx_hw *ecrnx_hw);
+void ecrnx_fw_trace_reset(struct ecrnx_hw *ecrnx_hw);
+
+#else
+
+struct ecrnx_debugfs {
+};
+
+static inline int ecrnx_dbgfs_register(struct ecrnx_hw *ecrnx_hw, const char *name) { return 0; }
+static inline void ecrnx_dbgfs_unregister(struct ecrnx_hw *ecrnx_hw) {}
+static inline int ecrnx_um_helper(struct ecrnx_debugfs *ecrnx_debugfs, const char *cmd) { return 0; }
+static inline int ecrnx_trigger_um_helper(struct ecrnx_debugfs *ecrnx_debugfs) {return 0;}
+static inline void ecrnx_wait_um_helper(struct ecrnx_hw *ecrnx_hw) {}
+#ifdef CONFIG_ECRNX_FULLMAC
+static inline void ecrnx_dbgfs_register_sta(struct ecrnx_hw *ecrnx_hw, struct ecrnx_sta *sta) {}
+static inline void ecrnx_dbgfs_unregister_sta(struct ecrnx_hw *ecrnx_hw, struct ecrnx_sta *sta) {}
+#endif
+
+static inline void ecrnx_fw_trace_dump(struct ecrnx_hw *ecrnx_hw) {}
+static inline void ecrnx_fw_trace_reset(struct ecrnx_hw *ecrnx_hw) {}
+
+#endif /* CONFIG_ECRNX_DEBUGFS */
+
+
+#endif /* _ECRNX_DEBUGFS_H_ */
diff --git a/drivers/net/wireless/eswin/ecrnx_events.h b/drivers/net/wireless/eswin/ecrnx_events.h
new file mode 100644
index 000000000000..32f19f22e612
--- /dev/null
+++ b/drivers/net/wireless/eswin/ecrnx_events.h
@@ -0,0 +1,1308 @@
+/**
+ ******************************************************************************
+ *
+ * @file ecrnx_events.h
+ *
+ * @brief Trace events definition
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM ecrnx
+
+#if !defined(_ECRNX_EVENTS_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _ECRNX_EVENTS_H
+
+#include <linux/tracepoint.h>
+#ifndef CONFIG_ECRNX_FHOST
+#include "ecrnx_tx.h"
+#endif
+#include "ecrnx_compat.h"
+
+/*****************************************************************************
+ * TRACE function for MGMT TX (FULLMAC)
+ ****************************************************************************/
+#ifdef CONFIG_ECRNX_FULLMAC
+#include "linux/ieee80211.h"
+#if defined(CONFIG_TRACEPOINTS) && defined(CREATE_TRACE_POINTS)
+#include <linux/trace_seq.h>
+
+/* P2P Public Action Frames Definitions (see WiFi P2P Technical Specification, section 4.2.8) */
+/* IEEE 802.11 Public Action Usage Category - Define P2P public action frames */
+#define MGMT_ACTION_PUBLIC_CAT (0x04)
+/* Offset of OUI Subtype field in P2P Action Frame format */
+#define MGMT_ACTION_OUI_SUBTYPE_OFFSET (6)
+/* P2P Public Action Frame Types */
+enum p2p_action_type {
+ P2P_ACTION_GO_NEG_REQ = 0, /* GO Negociation Request */
+ P2P_ACTION_GO_NEG_RSP, /* GO Negociation Response */
+ P2P_ACTION_GO_NEG_CFM, /* GO Negociation Confirmation */
+ P2P_ACTION_INVIT_REQ, /* P2P Invitation Request */
+ P2P_ACTION_INVIT_RSP, /* P2P Invitation Response */
+ P2P_ACTION_DEV_DISC_REQ, /* Device Discoverability Request */
+ P2P_ACTION_DEV_DISC_RSP, /* Device Discoverability Response */
+ P2P_ACTION_PROV_DISC_REQ, /* Provision Discovery Request */
+ P2P_ACTION_PROV_DISC_RSP, /* Provision Discovery Response */
+};
+
+const char *ftrace_print_mgmt_info(struct trace_seq *p, u16 frame_control, u8 cat, u8 type, u8 p2p) {
+ const char *ret = trace_seq_buffer_ptr(p);
+
+ switch (frame_control & IEEE80211_FCTL_STYPE) {
+ case (IEEE80211_STYPE_ASSOC_REQ): trace_seq_printf(p, "Association Request"); break;
+ case (IEEE80211_STYPE_ASSOC_RESP): trace_seq_printf(p, "Association Response"); break;
+ case (IEEE80211_STYPE_REASSOC_REQ): trace_seq_printf(p, "Reassociation Request"); break;
+ case (IEEE80211_STYPE_REASSOC_RESP): trace_seq_printf(p, "Reassociation Response"); break;
+ case (IEEE80211_STYPE_PROBE_REQ): trace_seq_printf(p, "Probe Request"); break;
+ case (IEEE80211_STYPE_PROBE_RESP): trace_seq_printf(p, "Probe Response"); break;
+ case (IEEE80211_STYPE_BEACON): trace_seq_printf(p, "Beacon"); break;
+ case (IEEE80211_STYPE_ATIM): trace_seq_printf(p, "ATIM"); break;
+ case (IEEE80211_STYPE_DISASSOC): trace_seq_printf(p, "Disassociation"); break;
+ case (IEEE80211_STYPE_AUTH): trace_seq_printf(p, "Authentication"); break;
+ case (IEEE80211_STYPE_DEAUTH): trace_seq_printf(p, "Deauthentication"); break;
+ case (IEEE80211_STYPE_ACTION):
+ trace_seq_printf(p, "Action");
+ if (cat == MGMT_ACTION_PUBLIC_CAT && type == 0x9)
+ switch (p2p) {
+ case (P2P_ACTION_GO_NEG_REQ): trace_seq_printf(p, ": GO Negociation Request"); break;
+ case (P2P_ACTION_GO_NEG_RSP): trace_seq_printf(p, ": GO Negociation Response"); break;
+ case (P2P_ACTION_GO_NEG_CFM): trace_seq_printf(p, ": GO Negociation Confirmation"); break;
+ case (P2P_ACTION_INVIT_REQ): trace_seq_printf(p, ": P2P Invitation Request"); break;
+ case (P2P_ACTION_INVIT_RSP): trace_seq_printf(p, ": P2P Invitation Response"); break;
+ case (P2P_ACTION_DEV_DISC_REQ): trace_seq_printf(p, ": Device Discoverability Request"); break;
+ case (P2P_ACTION_DEV_DISC_RSP): trace_seq_printf(p, ": Device Discoverability Response"); break;
+ case (P2P_ACTION_PROV_DISC_REQ): trace_seq_printf(p, ": Provision Discovery Request"); break;
+ case (P2P_ACTION_PROV_DISC_RSP): trace_seq_printf(p, ": Provision Discovery Response"); break;
+ default: trace_seq_printf(p, "Unknown p2p %d", p2p); break;
+ }
+ else {
+ switch (cat) {
+ case 0: trace_seq_printf(p, ":Spectrum %d", type); break;
+ case 1: trace_seq_printf(p, ":QOS %d", type); break;
+ case 2: trace_seq_printf(p, ":DLS %d", type); break;
+ case 3: trace_seq_printf(p, ":BA %d", type); break;
+ case 4: trace_seq_printf(p, ":Public %d", type); break;
+ case 5: trace_seq_printf(p, ":Radio Measure %d", type); break;
+ case 6: trace_seq_printf(p, ":Fast BSS %d", type); break;
+ case 7: trace_seq_printf(p, ":HT Action %d", type); break;
+ case 8: trace_seq_printf(p, ":SA Query %d", type); break;
+ case 9: trace_seq_printf(p, ":Protected Public %d", type); break;
+ case 10: trace_seq_printf(p, ":WNM %d", type); break;
+ case 11: trace_seq_printf(p, ":Unprotected WNM %d", type); break;
+ case 12: trace_seq_printf(p, ":TDLS %d", type); break;
+ case 13: trace_seq_printf(p, ":Mesh %d", type); break;
+ case 14: trace_seq_printf(p, ":MultiHop %d", type); break;
+ case 15: trace_seq_printf(p, ":Self Protected %d", type); break;
+ case 126: trace_seq_printf(p, ":Vendor protected"); break;
+ case 127: trace_seq_printf(p, ":Vendor"); break;
+ default: trace_seq_printf(p, ":Unknown category %d", cat); break;
+ }
+ }
+ break;
+ default: trace_seq_printf(p, "Unknown subtype %d", frame_control & IEEE80211_FCTL_STYPE); break;
+ }
+
+ trace_seq_putc(p, 0);
+
+ return ret;
+}
+#endif /* defined(CONFIG_TRACEPOINTS) && defined(CREATE_TRACE_POINTS) */
+
+#undef __print_mgmt_info
+#define __print_mgmt_info(frame_control, cat, type, p2p) ftrace_print_mgmt_info(p, frame_control, cat, type, p2p)
+
+TRACE_EVENT(
+ roc,
+ TP_PROTO(u8 vif_idx, u16 freq, unsigned int duration),
+ TP_ARGS(vif_idx, freq, duration),
+ TP_STRUCT__entry(
+ __field(u8, vif_idx)
+ __field(u16, freq)
+ __field(unsigned int, duration)
+ ),
+ TP_fast_assign(
+ __entry->vif_idx = vif_idx;
+ __entry->freq = freq;
+ __entry->duration = duration;
+ ),
+ TP_printk("f=%d vif=%d dur=%d",
+ __entry->freq, __entry->vif_idx, __entry->duration)
+);
+
+TRACE_EVENT(
+ cancel_roc,
+ TP_PROTO(u8 vif_idx),
+ TP_ARGS(vif_idx),
+ TP_STRUCT__entry(
+ __field(u8, vif_idx)
+ ),
+ TP_fast_assign(
+ __entry->vif_idx = vif_idx;
+ ),
+ TP_printk("vif=%d", __entry->vif_idx)
+);
+
+TRACE_EVENT(
+ roc_exp,
+ TP_PROTO(u8 vif_idx),
+ TP_ARGS(vif_idx),
+ TP_STRUCT__entry(
+ __field(u8, vif_idx)
+ ),
+ TP_fast_assign(
+ __entry->vif_idx = vif_idx;
+ ),
+ TP_printk("vif=%d", __entry->vif_idx)
+);
+
+TRACE_EVENT(
+ switch_roc,
+ TP_PROTO(u8 vif_idx),
+ TP_ARGS(vif_idx),
+ TP_STRUCT__entry(
+ __field(u8, vif_idx)
+ ),
+ TP_fast_assign(
+ __entry->vif_idx = vif_idx;
+ ),
+ TP_printk("vif=%d", __entry->vif_idx)
+);
+
+DECLARE_EVENT_CLASS(
+ mgmt_template,
+ TP_PROTO(u16 freq, u8 vif_idx, u8 sta_idx, struct ieee80211_mgmt *mgmt),
+ TP_ARGS(freq, vif_idx, sta_idx, mgmt),
+ TP_STRUCT__entry(
+ __field(u16, freq)
+ __field(u8, vif_idx)
+ __field(u8, sta_idx)
+ __field(u16, frame_control)
+ __field(u8, action_cat)
+ __field(u8, action_type)
+ __field(u8, action_p2p)
+ ),
+ TP_fast_assign(
+ __entry->freq = freq;
+ __entry->vif_idx = vif_idx;
+ __entry->sta_idx = sta_idx;
+ __entry->frame_control = mgmt->frame_control;
+ __entry->action_cat = mgmt->u.action.category;
+ __entry->action_type = mgmt->u.action.u.wme_action.action_code;
+ __entry->action_p2p = *((u8 *)&mgmt->u.action.category
+ + MGMT_ACTION_OUI_SUBTYPE_OFFSET);
+ ),
+ TP_printk("f=%d vif=%d sta=%d -> %s",
+ __entry->freq, __entry->vif_idx, __entry->sta_idx,
+ __print_mgmt_info(__entry->frame_control, __entry->action_cat,
+ __entry->action_type, __entry->action_p2p))
+);
+
+DEFINE_EVENT(mgmt_template, mgmt_tx,
+ TP_PROTO(u16 freq, u8 vif_idx, u8 sta_idx, struct ieee80211_mgmt *mgmt),
+ TP_ARGS(freq, vif_idx, sta_idx, mgmt));
+
+DEFINE_EVENT(mgmt_template, mgmt_rx,
+ TP_PROTO(u16 freq, u8 vif_idx, u8 sta_idx, struct ieee80211_mgmt *mgmt),
+ TP_ARGS(freq, vif_idx, sta_idx, mgmt));
+
+TRACE_EVENT(
+ mgmt_cfm,
+ TP_PROTO(u8 vif_idx, u8 sta_idx, bool acked),
+ TP_ARGS(vif_idx, sta_idx, acked),
+ TP_STRUCT__entry(
+ __field(u8, vif_idx)
+ __field(u8, sta_idx)
+ __field(bool, acked)
+ ),
+ TP_fast_assign(
+ __entry->vif_idx = vif_idx;
+ __entry->sta_idx = sta_idx;
+ __entry->acked = acked;
+ ),
+ TP_printk("vif=%d sta=%d ack=%d",
+ __entry->vif_idx, __entry->sta_idx, __entry->acked)
+);
+#endif /* CONFIG_ECRNX_FULLMAC */
+
+/*****************************************************************************
+ * TRACE function for TXQ
+ ****************************************************************************/
+#ifndef CONFIG_ECRNX_FHOST
+#if defined(CONFIG_TRACEPOINTS) && defined(CREATE_TRACE_POINTS)
+
+#include <linux/trace_seq.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 2, 0)
+#include <linux/trace_events.h>
+#else
+#include <linux/ftrace_event.h>
+#endif
+
+const char *
+ftrace_print_txq(struct trace_seq *p, int txq_idx) {
+ const char *ret = trace_seq_buffer_ptr(p);
+
+ if (txq_idx == TXQ_INACTIVE) {
+ trace_seq_printf(p, "[INACTIVE]");
+ } else if (txq_idx < NX_FIRST_VIF_TXQ_IDX) {
+ trace_seq_printf(p, "[STA %d/%d]",
+ txq_idx / NX_NB_TXQ_PER_STA,
+ txq_idx % NX_NB_TXQ_PER_STA);
+#ifdef CONFIG_ECRNX_FULLMAC
+ } else if (txq_idx < NX_FIRST_UNK_TXQ_IDX) {
+ trace_seq_printf(p, "[BC/MC %d]",
+ txq_idx - NX_FIRST_BCMC_TXQ_IDX);
+ } else if (txq_idx < NX_OFF_CHAN_TXQ_IDX) {
+ trace_seq_printf(p, "[UNKNOWN %d]",
+ txq_idx - NX_FIRST_UNK_TXQ_IDX);
+ } else if (txq_idx == NX_OFF_CHAN_TXQ_IDX) {
+ trace_seq_printf(p, "[OFFCHAN]");
+#else
+ } else if (txq_idx < NX_NB_TXQ) {
+ txq_idx -= NX_FIRST_VIF_TXQ_IDX;
+ trace_seq_printf(p, "[VIF %d/%d]",
+ txq_idx / NX_NB_TXQ_PER_VIF,
+ txq_idx % NX_NB_TXQ_PER_VIF);
+#endif
+ } else {
+ trace_seq_printf(p, "[ERROR %d]", txq_idx);
+ }
+
+ trace_seq_putc(p, 0);
+
+ return ret;
+}
+
+const char *
+ftrace_print_sta(struct trace_seq *p, int sta_idx) {
+ const char *ret = trace_seq_buffer_ptr(p);
+
+ if (sta_idx < NX_REMOTE_STA_MAX) {
+ trace_seq_printf(p, "[STA %d]", sta_idx);
+ } else {
+ trace_seq_printf(p, "[BC/MC %d]", sta_idx - NX_REMOTE_STA_MAX);
+ }
+
+ trace_seq_putc(p, 0);
+
+ return ret;
+}
+
+const char *
+ftrace_print_hwq(struct trace_seq *p, int hwq_idx) {
+
+ static const struct trace_print_flags symbols[] =
+ {{ECRNX_HWQ_BK, "BK"},
+ {ECRNX_HWQ_BE, "BE"},
+ {ECRNX_HWQ_VI, "VI"},
+ {ECRNX_HWQ_VO, "VO"},
+#ifdef CONFIG_ECRNX_FULLMAC
+ {ECRNX_HWQ_BCMC, "BCMC"},
+#else
+ {ECRNX_HWQ_BCN, "BCN"},
+#endif
+ { -1, NULL }};
+ return trace_print_symbols_seq(p, hwq_idx, symbols);
+}
+
+const char *
+ftrace_print_hwq_cred(struct trace_seq *p, u8 *cred) {
+ const char *ret = trace_seq_buffer_ptr(p);
+
+#if CONFIG_USER_MAX == 1
+ trace_seq_printf(p, "%d", cred[0]);
+#else
+ int i;
+
+ for (i = 0; i < CONFIG_USER_MAX - 1; i++)
+ trace_seq_printf(p, "%d-", cred[i]);
+ trace_seq_printf(p, "%d", cred[i]);
+#endif
+
+ trace_seq_putc(p, 0);
+ return ret;
+}
+
+const char *
+ftrace_print_mu_info(struct trace_seq *p, u8 mu_info) {
+ const char *ret = trace_seq_buffer_ptr(p);
+
+ if (mu_info)
+ trace_seq_printf(p, "MU: %d-%d", (mu_info & 0x3f), (mu_info >> 6));
+
+ trace_seq_putc(p, 0);
+ return ret;
+}
+
+const char *
+ftrace_print_mu_group(struct trace_seq *p, int nb_user, u8 *users) {
+ const char *ret = trace_seq_buffer_ptr(p);
+ int i;
+
+ if (users[0] != 0xff)
+ trace_seq_printf(p, "(%d", users[0]);
+ else
+ trace_seq_printf(p, "(-");
+ for (i = 1; i < CONFIG_USER_MAX ; i++) {
+ if (users[i] != 0xff)
+ trace_seq_printf(p, ",%d", users[i]);
+ else
+ trace_seq_printf(p, ",-");
+ }
+
+ trace_seq_printf(p, ")");
+ trace_seq_putc(p, 0);
+ return ret;
+}
+
+const char *
+ftrace_print_amsdu(struct trace_seq *p, u16 nb_pkt) {
+ const char *ret = trace_seq_buffer_ptr(p);
+
+ if (nb_pkt > 1)
+ trace_seq_printf(p, "(AMSDU %d)", nb_pkt);
+
+ trace_seq_putc(p, 0);
+ return ret;
+}
+#endif /* defined(CONFIG_TRACEPOINTS) && defined(CREATE_TRACE_POINTS) */
+
+#undef __print_txq
+#define __print_txq(txq_idx) ftrace_print_txq(p, txq_idx)
+
+#undef __print_sta
+#define __print_sta(sta_idx) ftrace_print_sta(p, sta_idx)
+
+#undef __print_hwq
+#define __print_hwq(hwq) ftrace_print_hwq(p, hwq)
+
+#undef __print_hwq_cred
+#define __print_hwq_cred(cred) ftrace_print_hwq_cred(p, cred)
+
+#undef __print_mu_info
+#define __print_mu_info(mu_info) ftrace_print_mu_info(p, mu_info)
+
+#undef __print_mu_group
+#define __print_mu_group(nb, users) ftrace_print_mu_group(p, nb, users)
+
+#undef __print_amsdu
+#define __print_amsdu(nb_pkt) ftrace_print_amsdu(p, nb_pkt)
+
+#ifdef CONFIG_ECRNX_FULLMAC
+
+TRACE_EVENT(
+ txq_select,
+ TP_PROTO(int txq_idx, u16 pkt_ready_up, struct sk_buff *skb),
+ TP_ARGS(txq_idx, pkt_ready_up, skb),
+ TP_STRUCT__entry(
+ __field(u16, txq_idx)
+ __field(u16, pkt_ready)
+ __field(struct sk_buff *, skb)
+ ),
+ TP_fast_assign(
+ __entry->txq_idx = txq_idx;
+ __entry->pkt_ready = pkt_ready_up;
+ __entry->skb = skb;
+ ),
+ TP_printk("%s pkt_ready_up=%d skb=%p", __print_txq(__entry->txq_idx),
+ __entry->pkt_ready, __entry->skb)
+);
+
+#endif /* CONFIG_ECRNX_FULLMAC */
+
+DECLARE_EVENT_CLASS(
+ hwq_template,
+ TP_PROTO(u8 hwq_idx),
+ TP_ARGS(hwq_idx),
+ TP_STRUCT__entry(
+ __field(u8, hwq_idx)
+ ),
+ TP_fast_assign(
+ __entry->hwq_idx = hwq_idx;
+ ),
+ TP_printk("%s", __print_hwq(__entry->hwq_idx))
+);
+
+DEFINE_EVENT(hwq_template, hwq_flowctrl_stop,
+ TP_PROTO(u8 hwq_idx),
+ TP_ARGS(hwq_idx));
+
+DEFINE_EVENT(hwq_template, hwq_flowctrl_start,
+ TP_PROTO(u8 hwq_idx),
+ TP_ARGS(hwq_idx));
+
+
+DECLARE_EVENT_CLASS(
+ txq_template,
+ TP_PROTO(struct ecrnx_txq *txq),
+ TP_ARGS(txq),
+ TP_STRUCT__entry(
+ __field(u16, txq_idx)
+ ),
+ TP_fast_assign(
+ __entry->txq_idx = txq->idx;
+ ),
+ TP_printk("%s", __print_txq(__entry->txq_idx))
+);
+
+DEFINE_EVENT(txq_template, txq_add_to_hw,
+ TP_PROTO(struct ecrnx_txq *txq),
+ TP_ARGS(txq));
+
+DEFINE_EVENT(txq_template, txq_del_from_hw,
+ TP_PROTO(struct ecrnx_txq *txq),
+ TP_ARGS(txq));
+
+#ifdef CONFIG_ECRNX_FULLMAC
+
+DEFINE_EVENT(txq_template, txq_flowctrl_stop,
+ TP_PROTO(struct ecrnx_txq *txq),
+ TP_ARGS(txq));
+
+DEFINE_EVENT(txq_template, txq_flowctrl_restart,
+ TP_PROTO(struct ecrnx_txq *txq),
+ TP_ARGS(txq));
+
+#endif /* CONFIG_ECRNX_FULLMAC */
+
+TRACE_EVENT(
+ process_txq,
+ TP_PROTO(struct ecrnx_txq *txq),
+ TP_ARGS(txq),
+ TP_STRUCT__entry(
+ __field(u16, txq_idx)
+ __field(u16, len)
+ __field(u16, len_retry)
+ __field(s8, credit)
+ #ifdef CONFIG_ECRNX_FULLMAC
+ __field(u16, limit)
+ #endif /* CONFIG_ECRNX_FULLMAC*/
+ ),
+ TP_fast_assign(
+ __entry->txq_idx = txq->idx;
+ __entry->len = skb_queue_len(&txq->sk_list);
+ #ifdef CONFIG_MAC80211_TXQ
+ __entry->len += txq->nb_ready_mac80211;
+ #endif
+ __entry->len_retry = txq->nb_retry;
+ __entry->credit = txq->credits;
+ #ifdef CONFIG_ECRNX_FULLMAC
+ __entry->limit = txq->push_limit;
+ #endif /* CONFIG_ECRNX_FULLMAC*/
+ ),
+
+ #ifdef CONFIG_ECRNX_FULLMAC
+ TP_printk("%s txq_credits=%d, len=%d, retry_len=%d, push_limit=%d",
+ __print_txq(__entry->txq_idx), __entry->credit,
+ __entry->len, __entry->len_retry, __entry->limit)
+ #else
+ TP_printk("%s txq_credits=%d, len=%d, retry_len=%d",
+ __print_txq(__entry->txq_idx), __entry->credit,
+ __entry->len, __entry->len_retry)
+ #endif /* CONFIG_ECRNX_FULLMAC*/
+);
+
+DECLARE_EVENT_CLASS(
+ txq_reason_template,
+ TP_PROTO(struct ecrnx_txq *txq, u16 reason),
+ TP_ARGS(txq, reason),
+ TP_STRUCT__entry(
+ __field(u16, txq_idx)
+ __field(u16, reason)
+ __field(u16, status)
+ ),
+ TP_fast_assign(
+ __entry->txq_idx = txq->idx;
+ __entry->reason = reason;
+ __entry->status = txq->status;
+ ),
+ TP_printk("%s reason=%s status=%s",
+ __print_txq(__entry->txq_idx),
+ __print_symbolic(__entry->reason,
+ {ECRNX_TXQ_STOP_FULL, "FULL"},
+ {ECRNX_TXQ_STOP_CSA, "CSA"},
+ {ECRNX_TXQ_STOP_STA_PS, "PS"},
+ {ECRNX_TXQ_STOP_VIF_PS, "VPS"},
+ {ECRNX_TXQ_STOP_CHAN, "CHAN"},
+ {ECRNX_TXQ_STOP_MU_POS, "MU"}),
+ __print_flags(__entry->status, "|",
+ {ECRNX_TXQ_IN_HWQ_LIST, "IN LIST"},
+ {ECRNX_TXQ_STOP_FULL, "FULL"},
+ {ECRNX_TXQ_STOP_CSA, "CSA"},
+ {ECRNX_TXQ_STOP_STA_PS, "PS"},
+ {ECRNX_TXQ_STOP_VIF_PS, "VPS"},
+ {ECRNX_TXQ_STOP_CHAN, "CHAN"},
+ {ECRNX_TXQ_STOP_MU_POS, "MU"},
+ {ECRNX_TXQ_NDEV_FLOW_CTRL, "FLW_CTRL"}))
+);
+
+DEFINE_EVENT(txq_reason_template, txq_start,
+ TP_PROTO(struct ecrnx_txq *txq, u16 reason),
+ TP_ARGS(txq, reason));
+
+DEFINE_EVENT(txq_reason_template, txq_stop,
+ TP_PROTO(struct ecrnx_txq *txq, u16 reason),
+ TP_ARGS(txq, reason));
+
+
+TRACE_EVENT(
+ push_desc,
+ TP_PROTO(struct sk_buff *skb, struct ecrnx_sw_txhdr *sw_txhdr, int push_flags),
+
+ TP_ARGS(skb, sw_txhdr, push_flags),
+
+ TP_STRUCT__entry(
+ __field(struct sk_buff *, skb)
+ __field(unsigned int, len)
+ __field(u16, tx_queue)
+ __field(u8, hw_queue)
+ __field(u8, push_flag)
+ __field(u32, flag)
+ __field(s8, txq_cred)
+ __field(u8, hwq_cred)
+ __field(u8, txq_length)
+#ifdef CONFIG_ECRNX_SOFTMAC
+ __field(u16, sn)
+#endif
+ __field(u16, pkt_cnt)
+ __field(u8, mu_info)
+ ),
+ TP_fast_assign(
+ __entry->skb = skb;
+ __entry->tx_queue = sw_txhdr->txq->idx;
+ __entry->push_flag = push_flags;
+ __entry->hw_queue = sw_txhdr->txq->hwq->id;
+ __entry->txq_cred = sw_txhdr->txq->credits;
+ __entry->hwq_cred = sw_txhdr->txq->hwq->credits[ECRNX_TXQ_POS_ID(sw_txhdr->txq)];
+#ifdef CONFIG_ECRNX_SPLIT_TX_BUF
+ __entry->pkt_cnt = sw_txhdr->desc.host.packet_cnt;
+#endif
+ __entry->txq_length = skb_queue_len(&sw_txhdr->txq->sk_list);
+#ifdef CONFIG_ECRNX_FULLMAC
+ __entry->flag = sw_txhdr->desc.host.flags;
+#ifdef CONFIG_ECRNX_SPLIT_TX_BUF
+#ifdef CONFIG_ECRNX_AMSDUS_TX
+ if (sw_txhdr->amsdu.len)
+ __entry->len = sw_txhdr->amsdu.len;
+ else
+#endif /* CONFIG_ECRNX_AMSDUS_TX */
+ __entry->len = sw_txhdr->desc.host.packet_len[0];
+#else
+ __entry->len = sw_txhdr->desc.host.packet_len;
+#endif /* CONFIG_ECRNX_SPLIT_TX_BUF */
+
+#else /* !CONFIG_ECRNX_FULLMAC */
+ __entry->flag = sw_txhdr->desc.umac.flags;
+ __entry->len = sw_txhdr->frame_len;
+ __entry->sn = sw_txhdr->sn;
+#endif /* CONFIG_ECRNX_FULLMAC */
+#ifdef CONFIG_ECRNX_MUMIMO_TX
+ __entry->mu_info = sw_txhdr->desc.host.mumimo_info;
+#else
+ __entry->mu_info = 0;
+#endif
+ ),
+
+#ifdef CONFIG_ECRNX_FULLMAC
+ TP_printk("%s skb=%p (len=%d) hw_queue=%s txq_length=%d cred_txq=%d cred_hwq=%d %s flag=%s %s%s%s",
+ __print_txq(__entry->tx_queue), __entry->skb, __entry->len,
+ __print_hwq(__entry->hw_queue), __entry->txq_length,
+ __entry->txq_cred, __entry->hwq_cred,
+ __print_mu_info(__entry->mu_info),
+ __print_flags(__entry->flag, "|",
+ {TXU_CNTRL_RETRY, "RETRY"},
+ {TXU_CNTRL_MORE_DATA, "MOREDATA"},
+ {TXU_CNTRL_MGMT, "MGMT"},
+ {TXU_CNTRL_MGMT_NO_CCK, "NO_CCK"},
+ {TXU_CNTRL_MGMT_ROBUST, "ROBUST"},
+ {TXU_CNTRL_AMSDU, "AMSDU"},
+ {TXU_CNTRL_USE_4ADDR, "4ADDR"},
+ {TXU_CNTRL_EOSP, "EOSP"},
+ {TXU_CNTRL_MESH_FWD, "MESH_FWD"},
+ {TXU_CNTRL_TDLS, "TDLS"}),
+ (__entry->push_flag & ECRNX_PUSH_IMMEDIATE) ? "(IMMEDIATE)" : "",
+ (!(__entry->flag & TXU_CNTRL_RETRY) &&
+ (__entry->push_flag & ECRNX_PUSH_RETRY)) ? "(SW_RETRY)" : "",
+ __print_amsdu(__entry->pkt_cnt))
+#else
+ TP_printk("%s skb=%p (len=%d) hw_queue=%s txq_length=%d cred_txq=%d cred_hwq=%d %s flag=%x (%s) sn=%d %s",
+ __print_txq(__entry->tx_queue), __entry->skb, __entry->len,
+ __print_hwq(__entry->hw_queue), __entry->txq_length, __entry->txq_cred,
+ __entry->hwq_cred,
+ __print_mu_info(__entry->mu_info),
+ __entry->flag,
+ __print_flags(__entry->push_flag, "|",
+ {ECRNX_PUSH_RETRY, "RETRY"},
+ {ECRNX_PUSH_IMMEDIATE, "IMMEDIATE"}),
+ __entry->sn, __print_amsdu(__entry->pkt_cnt))
+#endif /* CONFIG_ECRNX_FULLMAC */
+);
+
+
+TRACE_EVENT(
+ txq_queue_skb,
+ TP_PROTO(struct sk_buff *skb, struct ecrnx_txq *txq, bool retry),
+ TP_ARGS(skb, txq, retry),
+ TP_STRUCT__entry(
+ __field(struct sk_buff *, skb)
+ __field(u16, txq_idx)
+ __field(s8, credit)
+ __field(u16, q_len)
+ __field(u16, q_len_retry)
+ __field(bool, retry)
+ ),
+ TP_fast_assign(
+ __entry->skb = skb;
+ __entry->txq_idx = txq->idx;
+ __entry->credit = txq->credits;
+ __entry->q_len = skb_queue_len(&txq->sk_list);
+ __entry->q_len_retry = txq->nb_retry;
+ __entry->retry = retry;
+ ),
+
+ TP_printk("%s skb=%p retry=%d txq_credits=%d queue_len=%d (retry = %d)",
+ __print_txq(__entry->txq_idx), __entry->skb, __entry->retry,
+ __entry->credit, __entry->q_len, __entry->q_len_retry)
+);
+TRACE_EVENT(
+ txq_drop_skb,
+ TP_PROTO(struct sk_buff *skb, struct ecrnx_txq *txq, unsigned long queued_time),
+ TP_ARGS(skb, txq, queued_time),
+ TP_STRUCT__entry(
+ __field(struct sk_buff *, skb)
+ __field(u16, txq_idx)
+ __field(unsigned long, queued_time)
+ __field(u16, q_len)
+ __field(u16, q_len_retry)
+ ),
+ TP_fast_assign(
+ __entry->skb = skb;
+ __entry->txq_idx = txq->idx;
+ __entry->q_len = skb_queue_len(&txq->sk_list);
+ __entry->q_len_retry = txq->nb_retry;
+ __entry->queued_time = queued_time;
+ ),
+ TP_printk("%s skb=%p time_queued=%dms queue_len=%d (retry = %d)",
+ __print_txq(__entry->txq_idx), __entry->skb,
+ jiffies_to_msecs(__entry->queued_time), __entry->q_len, __entry->q_len_retry)
+);
+
+#ifdef CONFIG_MAC80211_TXQ
+TRACE_EVENT(
+ txq_wake,
+ TP_PROTO(struct ecrnx_txq *txq),
+ TP_ARGS(txq),
+ TP_STRUCT__entry(
+ __field(u16, txq_idx)
+ __field(u16, q_len)
+ ),
+ TP_fast_assign(
+ __entry->txq_idx = txq->idx;
+ __entry->q_len = txq->nb_ready_mac80211;
+ ),
+
+ TP_printk("%s mac80211_queue_len=%d", __print_txq(__entry->txq_idx), __entry->q_len)
+);
+
+TRACE_EVENT(
+ txq_drop,
+ TP_PROTO(struct ecrnx_txq *txq, unsigned long nb_drop),
+ TP_ARGS(txq, nb_drop),
+ TP_STRUCT__entry(
+ __field(u16, txq_idx)
+ __field(u16, nb_drop)
+ ),
+ TP_fast_assign(
+ __entry->txq_idx = txq->idx;
+ __entry->nb_drop = nb_drop;
+ ),
+
+ TP_printk("%s %u pkt have been dropped by codel in mac80211 txq",
+ __print_txq(__entry->txq_idx), __entry->nb_drop)
+);
+
+#endif
+
+
+DECLARE_EVENT_CLASS(
+ idx_template,
+ TP_PROTO(u16 idx),
+ TP_ARGS(idx),
+ TP_STRUCT__entry(
+ __field(u16, idx)
+ ),
+ TP_fast_assign(
+ __entry->idx = idx;
+ ),
+ TP_printk("idx=%d", __entry->idx)
+);
+
+
+DEFINE_EVENT(idx_template, txq_vif_start,
+ TP_PROTO(u16 idx),
+ TP_ARGS(idx));
+
+DEFINE_EVENT(idx_template, txq_vif_stop,
+ TP_PROTO(u16 idx),
+ TP_ARGS(idx));
+
+TRACE_EVENT(
+ process_hw_queue,
+ TP_PROTO(struct ecrnx_hwq *hwq),
+ TP_ARGS(hwq),
+ TP_STRUCT__entry(
+ __field(u16, hwq)
+ __array(u8, credits, CONFIG_USER_MAX)
+ ),
+ TP_fast_assign(
+ int i;
+ __entry->hwq = hwq->id;
+ for (i=0; i < CONFIG_USER_MAX; i ++)
+ __entry->credits[i] = hwq->credits[i];
+ ),
+ TP_printk("hw_queue=%s hw_credits=%s",
+ __print_hwq(__entry->hwq), __print_hwq_cred(__entry->credits))
+);
+
+DECLARE_EVENT_CLASS(
+ sta_idx_template,
+ TP_PROTO(u16 idx),
+ TP_ARGS(idx),
+ TP_STRUCT__entry(
+ __field(u16, idx)
+ ),
+ TP_fast_assign(
+ __entry->idx = idx;
+ ),
+ TP_printk("%s", __print_sta(__entry->idx))
+);
+
+DEFINE_EVENT(sta_idx_template, txq_sta_start,
+ TP_PROTO(u16 idx),
+ TP_ARGS(idx));
+
+DEFINE_EVENT(sta_idx_template, txq_sta_stop,
+ TP_PROTO(u16 idx),
+ TP_ARGS(idx));
+
+#ifdef CONFIG_ECRNX_FULLMAC
+
+DEFINE_EVENT(sta_idx_template, ps_disable,
+ TP_PROTO(u16 idx),
+ TP_ARGS(idx));
+
+#endif /* CONFIG_ECRNX_FULLMAC */
+
+TRACE_EVENT(
+ skb_confirm,
+ TP_PROTO(struct sk_buff *skb, struct ecrnx_txq *txq, struct ecrnx_hwq *hwq,
+#ifdef CONFIG_ECRNX_FULLMAC
+ struct tx_cfm_tag *cfm
+#else
+ u8 cfm
+#endif
+ ),
+
+ TP_ARGS(skb, txq, hwq, cfm),
+
+ TP_STRUCT__entry(
+ __field(struct sk_buff *, skb)
+ __field(u16, txq_idx)
+ __field(u8, hw_queue)
+ __array(u8, hw_credit, CONFIG_USER_MAX)
+ __field(s8, sw_credit)
+ __field(s8, sw_credit_up)
+#ifdef CONFIG_ECRNX_FULLMAC
+ __field(u8, ampdu_size)
+#ifdef CONFIG_ECRNX_SPLIT_TX_BUF
+ __field(u16, amsdu)
+#endif /* CONFIG_ECRNX_SPLIT_TX_BUF */
+ __field(u16, sn)
+#endif /* CONFIG_ECRNX_FULLMAC*/
+ ),
+
+ TP_fast_assign(
+ int i;
+ __entry->skb = skb;
+ __entry->txq_idx = txq->idx;
+ __entry->hw_queue = hwq->id;
+ for (i = 0 ; i < CONFIG_USER_MAX ; i++)
+ __entry->hw_credit[i] = hwq->credits[i];
+ __entry->sw_credit = txq->credits;
+#if defined CONFIG_ECRNX_FULLMAC
+ __entry->sw_credit_up = cfm->credits;
+ __entry->ampdu_size = cfm->ampdu_size;
+#ifdef CONFIG_ECRNX_SPLIT_TX_BUF
+ __entry->amsdu = cfm->amsdu_size;
+ __entry->sn = cfm->sn;
+#endif
+#else
+ __entry->sw_credit_up = cfm
+#endif /* CONFIG_ECRNX_FULLMAC */
+ ),
+
+ TP_printk("%s skb=%p hw_queue=%s, hw_credits=%s, txq_credits=%d (+%d)"
+#ifdef CONFIG_ECRNX_FULLMAC
+ " sn=%u ampdu=%d"
+#ifdef CONFIG_ECRNX_SPLIT_TX_BUF
+ " amsdu=%u"
+#endif
+#endif
+ , __print_txq(__entry->txq_idx), __entry->skb,
+ __print_hwq(__entry->hw_queue),
+ __print_hwq_cred(__entry->hw_credit),
+ __entry->sw_credit, __entry->sw_credit_up
+#ifdef CONFIG_ECRNX_FULLMAC
+ , __entry->sn, __entry->ampdu_size
+#ifdef CONFIG_ECRNX_SPLIT_TX_BUF
+ , __entry->amsdu
+#endif
+#endif
+ )
+);
+
+TRACE_EVENT(
+ credit_update,
+ TP_PROTO(struct ecrnx_txq *txq, s8_l cred_up),
+
+ TP_ARGS(txq, cred_up),
+
+ TP_STRUCT__entry(
+ __field(struct sk_buff *, skb)
+ __field(u16, txq_idx)
+ __field(s8, sw_credit)
+ __field(s8, sw_credit_up)
+ ),
+
+ TP_fast_assign(
+ __entry->txq_idx = txq->idx;
+ __entry->sw_credit = txq->credits;
+ __entry->sw_credit_up = cred_up;
+ ),
+
+ TP_printk("%s txq_credits=%d (%+d)", __print_txq(__entry->txq_idx),
+ __entry->sw_credit, __entry->sw_credit_up)
+)
+
+#ifdef CONFIG_ECRNX_FULLMAC
+
+DECLARE_EVENT_CLASS(
+ ps_template,
+ TP_PROTO(struct ecrnx_sta *sta),
+ TP_ARGS(sta),
+ TP_STRUCT__entry(
+ __field(u16, idx)
+ __field(u16, ready_ps)
+ __field(u16, sp_ps)
+ __field(u16, ready_uapsd)
+ __field(u16, sp_uapsd)
+ ),
+ TP_fast_assign(
+ __entry->idx = sta->sta_idx;
+ __entry->ready_ps = sta->ps.pkt_ready[LEGACY_PS_ID];
+ __entry->sp_ps = sta->ps.sp_cnt[LEGACY_PS_ID];
+ __entry->ready_uapsd = sta->ps.pkt_ready[UAPSD_ID];
+ __entry->sp_uapsd = sta->ps.sp_cnt[UAPSD_ID];
+ ),
+
+ TP_printk("%s [PS] ready=%d sp=%d [UAPSD] ready=%d sp=%d",
+ __print_sta(__entry->idx), __entry->ready_ps, __entry->sp_ps,
+ __entry->ready_uapsd, __entry->sp_uapsd)
+);
+
+DEFINE_EVENT(ps_template, ps_queue,
+ TP_PROTO(struct ecrnx_sta *sta),
+ TP_ARGS(sta));
+
+DEFINE_EVENT(ps_template, ps_drop,
+ TP_PROTO(struct ecrnx_sta *sta),
+ TP_ARGS(sta));
+DEFINE_EVENT(ps_template, ps_push,
+ TP_PROTO(struct ecrnx_sta *sta),
+ TP_ARGS(sta));
+
+DEFINE_EVENT(ps_template, ps_enable,
+ TP_PROTO(struct ecrnx_sta *sta),
+ TP_ARGS(sta));
+
+TRACE_EVENT(
+ ps_traffic_update,
+ TP_PROTO(u16 sta_idx, u8 traffic, bool uapsd),
+
+ TP_ARGS(sta_idx, traffic, uapsd),
+
+ TP_STRUCT__entry(
+ __field(u16, sta_idx)
+ __field(u8, traffic)
+ __field(bool, uapsd)
+ ),
+
+ TP_fast_assign(
+ __entry->sta_idx = sta_idx;
+ __entry->traffic = traffic;
+ __entry->uapsd = uapsd;
+ ),
+
+ TP_printk("%s %s%s traffic available ", __print_sta(__entry->sta_idx),
+ __entry->traffic ? "" : "no more ",
+ __entry->uapsd ? "U-APSD" : "legacy PS")
+);
+
+TRACE_EVENT(
+ ps_traffic_req,
+ TP_PROTO(struct ecrnx_sta *sta, u16 pkt_req, u8 ps_id),
+ TP_ARGS(sta, pkt_req, ps_id),
+ TP_STRUCT__entry(
+ __field(u16, idx)
+ __field(u16, pkt_req)
+ __field(u8, ps_id)
+ __field(u16, ready)
+ __field(u16, sp)
+ ),
+ TP_fast_assign(
+ __entry->idx = sta->sta_idx;
+ __entry->pkt_req = pkt_req;
+ __entry->ps_id = ps_id;
+ __entry->ready = sta->ps.pkt_ready[ps_id];
+ __entry->sp = sta->ps.sp_cnt[ps_id];
+ ),
+
+ TP_printk("%s %s traffic request %d pkt (ready=%d, sp=%d)",
+ __print_sta(__entry->idx),
+ __entry->ps_id == UAPSD_ID ? "U-APSD" : "legacy PS" ,
+ __entry->pkt_req, __entry->ready, __entry->sp)
+);
+
+
+#ifdef CONFIG_ECRNX_AMSDUS_TX
+TRACE_EVENT(
+ amsdu_subframe,
+ TP_PROTO(struct ecrnx_sw_txhdr *sw_txhdr),
+ TP_ARGS(sw_txhdr),
+ TP_STRUCT__entry(
+ __field(struct sk_buff *, skb)
+ __field(u16, txq_idx)
+ __field(u8, nb)
+ __field(u32, len)
+ ),
+ TP_fast_assign(
+ __entry->skb = sw_txhdr->skb;
+ __entry->nb = sw_txhdr->amsdu.nb;
+ __entry->len = sw_txhdr->amsdu.len;
+ __entry->txq_idx = sw_txhdr->txq->idx;
+ ),
+
+ TP_printk("%s skb=%p %s nb_subframe=%d, len=%u",
+ __print_txq(__entry->txq_idx), __entry->skb,
+ (__entry->nb == 2) ? "Start new AMSDU" : "Add subframe",
+ __entry->nb, __entry->len)
+);
+TRACE_EVENT(
+ amsdu_dismantle,
+ TP_PROTO(struct ecrnx_sw_txhdr *sw_txhdr),
+ TP_ARGS(sw_txhdr),
+ TP_STRUCT__entry(
+ __field(struct sk_buff *, skb)
+ __field(u16, txq_idx)
+ __field(u8, nb)
+ __field(u32, len)
+ ),
+ TP_fast_assign(
+ __entry->skb = sw_txhdr->skb;
+ __entry->nb = sw_txhdr->amsdu.nb;
+ __entry->len = sw_txhdr->amsdu.len;
+ __entry->txq_idx = sw_txhdr->txq->idx;
+ ),
+ TP_printk("%s skb=%p nb_subframe=%d, len=%u",
+ __print_txq(__entry->txq_idx), __entry->skb,
+ __entry->nb, __entry->len)
+);
+TRACE_EVENT(
+ amsdu_len_update,
+ TP_PROTO(struct ecrnx_sta *sta, int amsdu_len),
+ TP_ARGS(sta, amsdu_len),
+ TP_STRUCT__entry(
+ __field(u8, sta_idx)
+ __field(u16, amsdu_len)
+ ),
+ TP_fast_assign(
+ __entry->sta_idx = sta->sta_idx;
+ __entry->amsdu_len = amsdu_len;
+ ),
+ TP_printk("[Sta %d] A-MSDU len = %d", __entry->sta_idx, __entry->amsdu_len)
+);
+#endif
+
+#endif /* CONFIG_ECRNX_FULLMAC */
+
+#ifdef CONFIG_ECRNX_MUMIMO_TX
+TRACE_EVENT(
+ mu_group_update,
+ TP_PROTO(struct ecrnx_mu_group *group),
+ TP_ARGS(group),
+ TP_STRUCT__entry(
+ __field(u8, nb_user)
+ __field(u8, group_id)
+ __array(u8, users, CONFIG_USER_MAX)
+ ),
+ TP_fast_assign(
+ int i;
+ __entry->nb_user = group->user_cnt;
+ for (i = 0; i < CONFIG_USER_MAX ; i++) {
+ if (group->users[i]) {
+ __entry->users[i] = group->users[i]->sta_idx;
+ } else {
+ __entry->users[i] = 0xff;
+ }
+ }
+
+ __entry->group_id = group->group_id;
+ ),
+
+ TP_printk("Group-id = %d, Users = %s",
+ __entry->group_id,
+ __print_mu_group(__entry->nb_user, __entry->users))
+);
+
+TRACE_EVENT(
+ mu_group_delete,
+ TP_PROTO(int group_id),
+ TP_ARGS(group_id),
+ TP_STRUCT__entry(
+ __field(u8, group_id)
+ ),
+ TP_fast_assign(
+ __entry->group_id = group_id;
+ ),
+
+ TP_printk("Group-id = %d", __entry->group_id)
+);
+
+TRACE_EVENT(
+ mu_group_selection,
+ TP_PROTO(struct ecrnx_sta *sta, int group_id),
+ TP_ARGS(sta, group_id),
+ TP_STRUCT__entry(
+ __field(u8, sta_idx)
+ __field(u8, group_id)
+ ),
+ TP_fast_assign(
+ __entry->sta_idx = sta->sta_idx;
+ __entry->group_id = group_id;
+ ),
+
+ TP_printk("[Sta %d] Group-id = %d", __entry->sta_idx, __entry->group_id)
+);
+
+TRACE_EVENT(
+ txq_select_mu_group,
+ TP_PROTO(struct ecrnx_txq *txq, int group_id, int pos),
+
+ TP_ARGS(txq, group_id, pos),
+
+ TP_STRUCT__entry(
+ __field(u16, txq_idx)
+ __field(u8, group_id)
+ __field(u8, pos)
+ ),
+ TP_fast_assign(
+ __entry->txq_idx = txq->idx;
+ __entry->group_id = group_id;
+ __entry->pos = pos;
+ ),
+
+ TP_printk("%s: group=%d pos=%d", __print_txq(__entry->txq_idx),
+ __entry->group_id, __entry->pos)
+);
+
+#endif /* CONFIG_ECRNX_MUMIMO_TX */
+#endif /* ! CONFIG_ECRNX_FHOST */
+
+/*****************************************************************************
+ * TRACE functions for MESH
+ ****************************************************************************/
+#ifdef CONFIG_ECRNX_FULLMAC
+DECLARE_EVENT_CLASS(
+ mesh_path_template,
+ TP_PROTO(struct ecrnx_mesh_path *mesh_path),
+ TP_ARGS(mesh_path),
+ TP_STRUCT__entry(
+ __field(u8, idx)
+ __field(u8, next_hop_sta)
+ __array(u8, tgt_mac, ETH_ALEN)
+ ),
+
+ TP_fast_assign(
+ __entry->idx = mesh_path->path_idx;
+ memcpy(__entry->tgt_mac, &mesh_path->tgt_mac_addr, ETH_ALEN);
+ if (mesh_path->nhop_sta)
+ __entry->next_hop_sta = mesh_path->nhop_sta->sta_idx;
+ else
+ __entry->next_hop_sta = 0xff;
+ ),
+
+ TP_printk("Mpath(%d): target=%pM next_hop=STA-%d",
+ __entry->idx, __entry->tgt_mac, __entry->next_hop_sta)
+);
+
+DEFINE_EVENT(mesh_path_template, mesh_create_path,
+ TP_PROTO(struct ecrnx_mesh_path *mesh_path),
+ TP_ARGS(mesh_path));
+
+DEFINE_EVENT(mesh_path_template, mesh_delete_path,
+ TP_PROTO(struct ecrnx_mesh_path *mesh_path),
+ TP_ARGS(mesh_path));
+
+DEFINE_EVENT(mesh_path_template, mesh_update_path,
+ TP_PROTO(struct ecrnx_mesh_path *mesh_path),
+ TP_ARGS(mesh_path));
+
+#endif /* CONFIG_ECRNX_FULLMAC */
+
+/*****************************************************************************
+ * TRACE functions for RADAR
+ ****************************************************************************/
+#ifdef CONFIG_ECRNX_RADAR
+TRACE_EVENT(
+ radar_pulse,
+ TP_PROTO(u8 chain, struct radar_pulse *pulse),
+ TP_ARGS(chain, pulse),
+ TP_STRUCT__entry(
+ __field(u8, chain)
+ __field(s16, freq)
+ __field(u16, pri)
+ __field(u8, len)
+ __field(u8, fom)
+ ),
+ TP_fast_assign(
+ __entry->freq = pulse->freq * 2;
+ __entry->len = pulse->len * 2;
+ __entry->fom = pulse->fom * 6;
+ __entry->pri = pulse->rep;
+ __entry->chain = chain;
+ ),
+
+ TP_printk("%s: PRI=%.5d LEN=%.3d FOM=%.2d%% freq=%dMHz ",
+ __print_symbolic(__entry->chain,
+ {ECRNX_RADAR_RIU, "RIU"},
+ {ECRNX_RADAR_FCU, "FCU"}),
+ __entry->pri, __entry->len, __entry->fom, __entry->freq)
+ );
+
+TRACE_EVENT(
+ radar_detected,
+ TP_PROTO(u8 chain, u8 region, s16 freq, u8 type, u16 pri),
+ TP_ARGS(chain, region, freq, type, pri),
+ TP_STRUCT__entry(
+ __field(u8, chain)
+ __field(u8, region)
+ __field(s16, freq)
+ __field(u8, type)
+ __field(u16, pri)
+ ),
+ TP_fast_assign(
+ __entry->chain = chain;
+ __entry->region = region;
+ __entry->freq = freq;
+ __entry->type = type;
+ __entry->pri = pri;
+ ),
+ TP_printk("%s: region=%s type=%d freq=%dMHz (pri=%dus)",
+ __print_symbolic(__entry->chain,
+ {ECRNX_RADAR_RIU, "RIU"},
+ {ECRNX_RADAR_FCU, "FCU"}),
+ __print_symbolic(__entry->region,
+ {NL80211_DFS_UNSET, "UNSET"},
+ {NL80211_DFS_FCC, "FCC"},
+ {NL80211_DFS_ETSI, "ETSI"},
+ {NL80211_DFS_JP, "JP"}),
+ __entry->type, __entry->freq, __entry->pri)
+);
+
+TRACE_EVENT(
+ radar_set_region,
+ TP_PROTO(u8 region),
+ TP_ARGS(region),
+ TP_STRUCT__entry(
+ __field(u8, region)
+ ),
+ TP_fast_assign(
+ __entry->region = region;
+ ),
+ TP_printk("region=%s",
+ __print_symbolic(__entry->region,
+ {NL80211_DFS_UNSET, "UNSET"},
+ {NL80211_DFS_FCC, "FCC"},
+ {NL80211_DFS_ETSI, "ETSI"},
+ {NL80211_DFS_JP, "JP"}))
+);
+
+TRACE_EVENT(
+ radar_enable_detection,
+ TP_PROTO(u8 region, u8 enable, u8 chain),
+ TP_ARGS(region, enable, chain),
+ TP_STRUCT__entry(
+ __field(u8, region)
+ __field(u8, chain)
+ __field(u8, enable)
+ ),
+ TP_fast_assign(
+ __entry->chain = chain;
+ __entry->enable = enable;
+ __entry->region = region;
+ ),
+ TP_printk("%s: %s radar detection %s",
+ __print_symbolic(__entry->chain,
+ {ECRNX_RADAR_RIU, "RIU"},
+ {ECRNX_RADAR_FCU, "FCU"}),
+ __print_symbolic(__entry->enable,
+ {ECRNX_RADAR_DETECT_DISABLE, "Disable"},
+ {ECRNX_RADAR_DETECT_ENABLE, "Enable (no report)"},
+ {ECRNX_RADAR_DETECT_REPORT, "Enable"}),
+ __entry->enable == ECRNX_RADAR_DETECT_DISABLE ? "" :
+ __print_symbolic(__entry->region,
+ {NL80211_DFS_UNSET, "UNSET"},
+ {NL80211_DFS_FCC, "FCC"},
+ {NL80211_DFS_ETSI, "ETSI"},
+ {NL80211_DFS_JP, "JP"}))
+);
+#endif /* CONFIG_ECRNX_RADAR */
+
+/*****************************************************************************
+ * TRACE functions for IPC message
+ ****************************************************************************/
+#include "ecrnx_strs.h"
+
+DECLARE_EVENT_CLASS(
+ ipc_msg_template,
+ TP_PROTO(u16 id),
+ TP_ARGS(id),
+ TP_STRUCT__entry(
+ __field(u16, id)
+ ),
+ TP_fast_assign(
+ __entry->id = id;
+ ),
+
+ TP_printk("%s (%d - %d)", ECRNX_ID2STR(__entry->id),
+ MSG_T(__entry->id), MSG_I(__entry->id))
+);
+
+DEFINE_EVENT(ipc_msg_template, msg_send,
+ TP_PROTO(u16 id),
+ TP_ARGS(id));
+
+DEFINE_EVENT(ipc_msg_template, msg_recv,
+ TP_PROTO(u16 id),
+ TP_ARGS(id));
+
+
+
+#endif /* !defined(_ECRNX_EVENTS_H) || defined(TRACE_HEADER_MULTI_READ) */
+
+#undef TRACE_INCLUDE_PATH
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_PATH .
+#define TRACE_INCLUDE_FILE ecrnx_events
+#include <trace/define_trace.h>
diff --git a/drivers/net/wireless/eswin/ecrnx_fw_dump.c b/drivers/net/wireless/eswin/ecrnx_fw_dump.c
new file mode 100644
index 000000000000..97ca610accb4
--- /dev/null
+++ b/drivers/net/wireless/eswin/ecrnx_fw_dump.c
@@ -0,0 +1,573 @@
+/**
+ ******************************************************************************
+ *
+ * @file ecrnx_fw_dump.c
+ *
+ * @brief Definition of debug fs entries to process fw dump
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+
+
+#include <linux/kmod.h>
+#include <linux/debugfs.h>
+
+#include "ecrnx_defs.h"
+#include "ecrnx_debugfs.h"
+
+static ssize_t ecrnx_dbgfs_rhd_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ecrnx_hw *priv = file->private_data;
+ struct dbg_debug_dump_tag *dump = priv->dbgdump_elem.buf.addr;
+ ssize_t read;
+
+ mutex_lock(&priv->dbgdump_elem.mutex);
+ if (!priv->debugfs.trace_prst) {
+ mutex_unlock(&priv->dbgdump_elem.mutex);
+ return 0;
+ }
+
+ read = simple_read_from_buffer(user_buf, count, ppos,
+ dump->rhd_mem,
+ dump->dbg_info.rhd_len);
+
+ mutex_unlock(&priv->dbgdump_elem.mutex);
+ return read;
+}
+
+DEBUGFS_READ_FILE_OPS(rhd);
+
+static ssize_t ecrnx_dbgfs_rbd_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ecrnx_hw *priv = file->private_data;
+ struct dbg_debug_dump_tag *dump = priv->dbgdump_elem.buf.addr;
+ ssize_t read;
+
+ mutex_lock(&priv->dbgdump_elem.mutex);
+ if (!priv->debugfs.trace_prst) {
+ mutex_unlock(&priv->dbgdump_elem.mutex);
+ return 0;
+ }
+
+ read = simple_read_from_buffer(user_buf, count, ppos,
+ dump->rbd_mem,
+ dump->dbg_info.rbd_len);
+
+ mutex_unlock(&priv->dbgdump_elem.mutex);
+ return read;
+}
+
+DEBUGFS_READ_FILE_OPS(rbd);
+
+static ssize_t ecrnx_dbgfs_thdx_read(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos, int idx)
+{
+ struct ecrnx_hw *priv = file->private_data;
+ struct dbg_debug_dump_tag *dump = priv->dbgdump_elem.buf.addr;
+ ssize_t read;
+
+ mutex_lock(&priv->dbgdump_elem.mutex);
+ if (!priv->debugfs.trace_prst) {
+ mutex_unlock(&priv->dbgdump_elem.mutex);
+ return 0;
+ }
+
+ read = simple_read_from_buffer(user_buf, count, ppos,
+ &dump->thd_mem[idx],
+ dump->dbg_info.thd_len[idx]);
+
+ mutex_unlock(&priv->dbgdump_elem.mutex);
+ return read;
+}
+
+static ssize_t ecrnx_dbgfs_thd0_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ return ecrnx_dbgfs_thdx_read(file, user_buf, count, ppos, 0);
+}
+DEBUGFS_READ_FILE_OPS(thd0);
+
+static ssize_t ecrnx_dbgfs_thd1_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ return ecrnx_dbgfs_thdx_read(file, user_buf, count, ppos, 1);
+}
+DEBUGFS_READ_FILE_OPS(thd1);
+
+static ssize_t ecrnx_dbgfs_thd2_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ return ecrnx_dbgfs_thdx_read(file, user_buf, count, ppos, 2);
+}
+DEBUGFS_READ_FILE_OPS(thd2);
+
+static ssize_t ecrnx_dbgfs_thd3_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ return ecrnx_dbgfs_thdx_read(file, user_buf, count, ppos, 3);
+}
+DEBUGFS_READ_FILE_OPS(thd3);
+
+#if (NX_TXQ_CNT == 5)
+static ssize_t ecrnx_dbgfs_thd4_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ return ecrnx_dbgfs_thdx_read(file, user_buf, count, ppos, 4);
+}
+DEBUGFS_READ_FILE_OPS(thd4);
+#endif
+
+static ssize_t ecrnx_dbgfs_mactrace_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ecrnx_hw *priv = file->private_data;
+ struct dbg_debug_dump_tag *dump = priv->dbgdump_elem.buf.addr;
+ ssize_t read;
+
+ mutex_lock(&priv->dbgdump_elem.mutex);
+ if (!priv->debugfs.trace_prst) {
+ char msg[64];
+ mutex_unlock(&priv->dbgdump_elem.mutex);
+ scnprintf(msg, sizeof(msg), "Force trigger\n");
+ ecrnx_dbgfs_trigger_fw_dump(priv, msg);
+
+ return 0;
+ }
+
+ read = simple_read_from_buffer(user_buf, count, ppos,
+ dump->la_mem,
+ dump->dbg_info.la_conf.trace_len);
+
+ mutex_unlock(&priv->dbgdump_elem.mutex);
+
+ return read;
+}
+DEBUGFS_READ_FILE_OPS(mactrace);
+
+static ssize_t ecrnx_dbgfs_macdiags_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ecrnx_hw *priv = file->private_data;
+ struct dbg_debug_dump_tag *dump = priv->dbgdump_elem.buf.addr;
+ ssize_t read;
+
+ mutex_lock(&priv->dbgdump_elem.mutex);
+ if (!priv->debugfs.trace_prst) {
+ mutex_unlock(&priv->dbgdump_elem.mutex);
+ return 0;
+ }
+
+ read = simple_read_from_buffer(user_buf, count, ppos,
+ dump->dbg_info.diags_mac,
+ DBG_DIAGS_MAC_MAX * 2);
+
+ mutex_unlock(&priv->dbgdump_elem.mutex);
+ return read;
+}
+
+DEBUGFS_READ_FILE_OPS(macdiags);
+
+static ssize_t ecrnx_dbgfs_phydiags_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ecrnx_hw *priv = file->private_data;
+ struct dbg_debug_dump_tag *dump = priv->dbgdump_elem.buf.addr;
+ ssize_t read;
+
+ mutex_lock(&priv->dbgdump_elem.mutex);
+ if (!priv->debugfs.trace_prst) {
+ mutex_unlock(&priv->dbgdump_elem.mutex);
+ return 0;
+ }
+
+ read = simple_read_from_buffer(user_buf, count, ppos,
+ dump->dbg_info.diags_phy,
+ DBG_DIAGS_PHY_MAX * 2);
+
+ mutex_unlock(&priv->dbgdump_elem.mutex);
+ return read;
+}
+
+DEBUGFS_READ_FILE_OPS(phydiags);
+
+static ssize_t ecrnx_dbgfs_hwdiags_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ecrnx_hw *priv = file->private_data;
+ struct dbg_debug_dump_tag *dump = priv->dbgdump_elem.buf.addr;
+ char buf[16];
+ int ret;
+
+ mutex_lock(&priv->dbgdump_elem.mutex);
+ if (!priv->debugfs.trace_prst) {
+ mutex_unlock(&priv->dbgdump_elem.mutex);
+ return 0;
+ }
+
+ ret = scnprintf(buf, min_t(size_t, sizeof(buf) - 1, count),
+ "%08X\n", dump->dbg_info.hw_diag);
+
+ mutex_unlock(&priv->dbgdump_elem.mutex);
+ return simple_read_from_buffer(user_buf, count, ppos, buf, ret);
+}
+
+DEBUGFS_READ_FILE_OPS(hwdiags);
+
+static ssize_t ecrnx_dbgfs_plfdiags_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ecrnx_hw *priv = file->private_data;
+ struct dbg_debug_dump_tag *dump = priv->dbgdump_elem.buf.addr;
+ char buf[16];
+ int ret;
+
+ mutex_lock(&priv->dbgdump_elem.mutex);
+ if (!priv->debugfs.trace_prst) {
+ mutex_unlock(&priv->dbgdump_elem.mutex);
+ return 0;
+ }
+
+ ret = scnprintf(buf, min_t(size_t, sizeof(buf) - 1, count),
+ "%08X\n", dump->dbg_info.la_conf.diag_conf);
+
+ mutex_unlock(&priv->dbgdump_elem.mutex);
+ return simple_read_from_buffer(user_buf, count, ppos, buf, ret);
+}
+
+DEBUGFS_READ_FILE_OPS(plfdiags);
+
+static ssize_t ecrnx_dbgfs_swdiags_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ecrnx_hw *priv = file->private_data;
+ struct dbg_debug_dump_tag *dump = priv->dbgdump_elem.buf.addr;
+ ssize_t read;
+
+ mutex_lock(&priv->dbgdump_elem.mutex);
+ if (!priv->debugfs.trace_prst) {
+ mutex_unlock(&priv->dbgdump_elem.mutex);
+ return 0;
+ }
+
+ read = simple_read_from_buffer(user_buf, count, ppos,
+ &dump->dbg_info.sw_diag,
+ dump->dbg_info.sw_diag_len);
+
+ mutex_unlock(&priv->dbgdump_elem.mutex);
+ return read;
+}
+
+DEBUGFS_READ_FILE_OPS(swdiags);
+
+static ssize_t ecrnx_dbgfs_error_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ecrnx_hw *priv = file->private_data;
+ struct dbg_debug_dump_tag *dump = priv->dbgdump_elem.buf.addr;
+ ssize_t read;
+
+ mutex_lock(&priv->dbgdump_elem.mutex);
+ if (!priv->debugfs.trace_prst) {
+ mutex_unlock(&priv->dbgdump_elem.mutex);
+ return 0;
+ }
+
+ read = simple_read_from_buffer(user_buf, count, ppos,
+ dump->dbg_info.error,
+ strlen((char *)dump->dbg_info.error));
+
+ mutex_unlock(&priv->dbgdump_elem.mutex);
+ return read;
+}
+
+DEBUGFS_READ_FILE_OPS(error);
+
+static ssize_t ecrnx_dbgfs_rxdesc_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ecrnx_hw *priv = file->private_data;
+ struct dbg_debug_dump_tag *dump = priv->dbgdump_elem.buf.addr;
+ char buf[32];
+ int ret;
+ ssize_t read;
+
+ mutex_lock(&priv->dbgdump_elem.mutex);
+ if (!priv->debugfs.trace_prst) {
+ mutex_unlock(&priv->dbgdump_elem.mutex);
+ return 0;
+ }
+
+ ret = scnprintf(buf, min_t(size_t, sizeof(buf) - 1, count),
+ "%08X\n%08X\n", dump->dbg_info.rhd,
+ dump->dbg_info.rbd);
+ read = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
+
+ mutex_unlock(&priv->dbgdump_elem.mutex);
+ return read;
+}
+
+DEBUGFS_READ_FILE_OPS(rxdesc);
+
+static ssize_t ecrnx_dbgfs_txdesc_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ecrnx_hw *priv = file->private_data;
+ struct dbg_debug_dump_tag *dump = priv->dbgdump_elem.buf.addr;
+ char buf[64];
+ int len = 0;
+ int i;
+
+ mutex_lock(&priv->dbgdump_elem.mutex);
+ if (!priv->debugfs.trace_prst) {
+ mutex_unlock(&priv->dbgdump_elem.mutex);
+ return 0;
+ }
+
+ for (i = 0; i < NX_TXQ_CNT; i++) {
+ len += scnprintf(&buf[len], min_t(size_t, sizeof(buf) - len - 1, count),
+ "%08X\n", dump->dbg_info.thd[i]);
+ }
+
+ mutex_unlock(&priv->dbgdump_elem.mutex);
+ return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+DEBUGFS_READ_FILE_OPS(txdesc);
+
+static ssize_t ecrnx_dbgfs_macrxptr_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ecrnx_hw *priv = file->private_data;
+ struct dbg_debug_dump_tag *dump = priv->dbgdump_elem.buf.addr;
+ ssize_t read;
+
+ mutex_lock(&priv->dbgdump_elem.mutex);
+ if (!priv->debugfs.trace_prst) {
+ mutex_unlock(&priv->dbgdump_elem.mutex);
+ return 0;
+ }
+
+ read = simple_read_from_buffer(user_buf, count, ppos,
+ &dump->dbg_info.rhd_hw_ptr,
+ 2 * sizeof(dump->dbg_info.rhd_hw_ptr));
+
+ mutex_unlock(&priv->dbgdump_elem.mutex);
+ return read;
+}
+
+DEBUGFS_READ_FILE_OPS(macrxptr);
+
+static ssize_t ecrnx_dbgfs_lamacconf_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ecrnx_hw *priv = file->private_data;
+ struct dbg_debug_dump_tag *dump = priv->dbgdump_elem.buf.addr;
+ ssize_t read;
+
+ mutex_lock(&priv->dbgdump_elem.mutex);
+ if (!priv->debugfs.trace_prst) {
+ mutex_unlock(&priv->dbgdump_elem.mutex);
+ return 0;
+ }
+
+ read = simple_read_from_buffer(user_buf, count, ppos,
+ dump->dbg_info.la_conf.conf,
+ LA_CONF_LEN * 4);
+
+ mutex_unlock(&priv->dbgdump_elem.mutex);
+ return read;
+}
+DEBUGFS_READ_FILE_OPS(lamacconf);
+
+static ssize_t ecrnx_dbgfs_chaninfo_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ecrnx_hw *priv = file->private_data;
+ struct dbg_debug_dump_tag *dump = priv->dbgdump_elem.buf.addr;
+ char buf[4 * 32];
+ int ret;
+
+ mutex_lock(&priv->dbgdump_elem.mutex);
+ if (!priv->debugfs.trace_prst) {
+ mutex_unlock(&priv->dbgdump_elem.mutex);
+ return 0;
+ }
+
+ ret = scnprintf(buf, min_t(size_t, sizeof(buf) - 1, count),
+ "type: %d\n"
+ "prim20_freq: %d MHz\n"
+ "center1_freq: %d MHz\n"
+ "center2_freq: %d MHz\n",
+ (dump->dbg_info.chan_info.info1 >> 8) & 0xFF,
+ (dump->dbg_info.chan_info.info1 >> 16) & 0xFFFF,
+ (dump->dbg_info.chan_info.info2 >> 0) & 0xFFFF,
+ (dump->dbg_info.chan_info.info2 >> 16) & 0xFFFF);
+
+ mutex_unlock(&priv->dbgdump_elem.mutex);
+ return simple_read_from_buffer(user_buf, count, ppos, buf, ret);
+}
+
+DEBUGFS_READ_FILE_OPS(chaninfo);
+
+static ssize_t ecrnx_dbgfs_um_helper_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ecrnx_hw *priv = file->private_data;
+ char buf[sizeof(priv->debugfs.helper_cmd)];
+ int ret;
+
+ ret = scnprintf(buf, min_t(size_t, sizeof(buf) - 1, count),
+ "%s", priv->debugfs.helper_cmd);
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, ret);
+}
+
+static ssize_t ecrnx_dbgfs_um_helper_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ecrnx_hw *priv = file->private_data;
+ int eobuf = min_t(size_t, sizeof(priv->debugfs.helper_cmd) - 1, count);
+
+ priv->debugfs.helper_cmd[eobuf] = '\0';
+ if (copy_from_user(priv->debugfs.helper_cmd, user_buf, eobuf))
+ return -EFAULT;
+
+ return count;
+}
+
+DEBUGFS_READ_WRITE_FILE_OPS(um_helper);
+
+/*
+ * Calls a userspace pgm
+ */
+int ecrnx_um_helper(struct ecrnx_debugfs *ecrnx_debugfs, const char *cmd)
+{
+ struct ecrnx_hw *ecrnx_hw = container_of(ecrnx_debugfs, struct ecrnx_hw,
+ debugfs);
+ char *envp[] = { "PATH=/sbin:/usr/sbin:/bin:/usr/bin", NULL };
+ char **argv;
+ int argc, ret;
+
+ if (!ecrnx_debugfs->dir ||
+ !strlen((cmd = cmd ? cmd : ecrnx_debugfs->helper_cmd)))
+ return 0;
+ argv = argv_split(in_interrupt() ? GFP_ATOMIC : GFP_KERNEL, cmd, &argc);
+ if (!argc)
+ return PTR_ERR(argv);
+
+ if ((ret = call_usermodehelper(argv[0], argv, envp,
+ UMH_WAIT_PROC | UMH_KILLABLE)))
+ dev_err(ecrnx_hw->dev, "Failed to call %s (%s returned %d)\n",
+ argv[0], cmd, ret);
+ argv_free(argv);
+
+ return ret;
+}
+
+static void ecrnx_um_helper_work(struct work_struct *ws)
+{
+ struct ecrnx_debugfs *ecrnx_debugfs = container_of(ws, struct ecrnx_debugfs,
+ helper_work);
+ struct ecrnx_hw *ecrnx_hw = container_of(ecrnx_debugfs, struct ecrnx_hw,
+ debugfs);
+ ecrnx_um_helper(ecrnx_debugfs, NULL);
+ if (!ecrnx_debugfs->unregistering)
+ ecrnx_umh_done(ecrnx_hw);
+ ecrnx_debugfs->helper_scheduled = false;
+}
+
+int ecrnx_trigger_um_helper(struct ecrnx_debugfs *ecrnx_debugfs)
+{
+ struct ecrnx_hw *ecrnx_hw = container_of(ecrnx_debugfs, struct ecrnx_hw,
+ debugfs);
+
+ if (ecrnx_debugfs->helper_scheduled == true) {
+ dev_err(ecrnx_hw->dev, "%s: Already scheduled\n", __func__);
+ return -EBUSY;
+ }
+
+ spin_lock_bh(&ecrnx_debugfs->umh_lock);
+ if (ecrnx_debugfs->unregistering) {
+ spin_unlock_bh(&ecrnx_debugfs->umh_lock);
+ dev_err(ecrnx_hw->dev, "%s: unregistering\n", __func__);
+ return -ENOENT;
+ }
+ ecrnx_debugfs->helper_scheduled = true;
+ schedule_work(&ecrnx_debugfs->helper_work);
+ spin_unlock_bh(&ecrnx_debugfs->umh_lock);
+
+ return 0;
+}
+void ecrnx_wait_um_helper(struct ecrnx_hw *ecrnx_hw)
+{
+ flush_work(&ecrnx_hw->debugfs.helper_work);
+}
+
+int ecrnx_dbgfs_register_fw_dump(struct ecrnx_hw *ecrnx_hw,
+ struct dentry *dir_drv,
+ struct dentry *dir_diags)
+{
+
+ struct ecrnx_debugfs *ecrnx_debugfs = &ecrnx_hw->debugfs;
+
+ BUILD_BUG_ON(sizeof(CONFIG_ECRNX_UM_HELPER_DFLT) >=
+ sizeof(ecrnx_debugfs->helper_cmd));
+ strncpy(ecrnx_debugfs->helper_cmd,
+ CONFIG_ECRNX_UM_HELPER_DFLT, sizeof(ecrnx_debugfs->helper_cmd));
+ INIT_WORK(&ecrnx_debugfs->helper_work, ecrnx_um_helper_work);
+ DEBUGFS_ADD_FILE(um_helper, dir_drv, S_IWUSR | S_IRUSR);
+
+ ecrnx_debugfs->trace_prst = ecrnx_debugfs->helper_scheduled = false;
+ spin_lock_init(&ecrnx_debugfs->umh_lock);
+ DEBUGFS_ADD_FILE(rhd, dir_diags, S_IRUSR);
+ DEBUGFS_ADD_FILE(rbd, dir_diags, S_IRUSR);
+ DEBUGFS_ADD_FILE(thd0, dir_diags, S_IRUSR);
+ DEBUGFS_ADD_FILE(thd1, dir_diags, S_IRUSR);
+ DEBUGFS_ADD_FILE(thd2, dir_diags, S_IRUSR);
+ DEBUGFS_ADD_FILE(thd3, dir_diags, S_IRUSR);
+#if (NX_TXQ_CNT == 5)
+ DEBUGFS_ADD_FILE(thd4, dir_diags, S_IRUSR);
+#endif
+ DEBUGFS_ADD_FILE(mactrace, dir_diags, S_IRUSR);
+ DEBUGFS_ADD_FILE(macdiags, dir_diags, S_IRUSR);
+ DEBUGFS_ADD_FILE(phydiags, dir_diags, S_IRUSR);
+ DEBUGFS_ADD_FILE(plfdiags, dir_diags, S_IRUSR);
+ DEBUGFS_ADD_FILE(hwdiags, dir_diags, S_IRUSR);
+ DEBUGFS_ADD_FILE(swdiags, dir_diags, S_IRUSR);
+ DEBUGFS_ADD_FILE(error, dir_diags, S_IRUSR);
+ DEBUGFS_ADD_FILE(rxdesc, dir_diags, S_IRUSR);
+ DEBUGFS_ADD_FILE(txdesc, dir_diags, S_IRUSR);
+ DEBUGFS_ADD_FILE(macrxptr, dir_diags, S_IRUSR);
+ DEBUGFS_ADD_FILE(lamacconf, dir_diags, S_IRUSR);
+ DEBUGFS_ADD_FILE(chaninfo, dir_diags, S_IRUSR);
+
+ return 0;
+
+ err:
+ return -1;
+}
diff --git a/drivers/net/wireless/eswin/ecrnx_fw_trace.c b/drivers/net/wireless/eswin/ecrnx_fw_trace.c
new file mode 100644
index 000000000000..a6428e565358
--- /dev/null
+++ b/drivers/net/wireless/eswin/ecrnx_fw_trace.c
@@ -0,0 +1,895 @@
+/**
+ ******************************************************************************
+ *
+ * @file ecrnx_fw_trace.c
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/delay.h>
+#include "ecrnx_fw_trace.h"
+#include "ecrnx_defs.h"
+
+#define ECRNX_FW_TRACE_HEADER_LEN 4
+#define ECRNX_FW_TRACE_HEADER_FMT "ts=%12u ID=%8d"
+#define ECRNX_FW_TRACE_HEADER_ASCII_LEN (3 + 12 + 4 + 8)
+#define ECRNX_FW_TRACE_PARAM_FMT ", %5d"
+#define ECRNX_FW_TRACE_PARAM_ASCII_LEN (7)
+
+#define ECRNX_FW_TRACE_NB_PARAM(a) ((*a >> 8) & 0xff)
+#define ECRNX_FW_TRACE_ID(a) (uint32_t)(((a[0] & 0xff) << 16) + a[1])
+#define ECRNX_FW_TRACE_ENTRY_SIZE(a) (ECRNX_FW_TRACE_NB_PARAM(a) + \
+ ECRNX_FW_TRACE_HEADER_LEN)
+
+#define ECRNX_FW_TRACE_READY 0x1234
+#define ECRNX_FW_TRACE_LOCKED 0xdead
+#define ECRNX_FW_TRACE_LOCKED_HOST 0x0230
+#define ECRNX_FW_TRACE_LAST_ENTRY 0xffff
+
+#define ECRNX_FW_TRACE_RESET "*** RESET ***\n"
+#define ECRNX_FW_TRACE_RESET_SIZE sizeof(ECRNX_FW_TRACE_RESET) - 1 // don't count '\0'
+
+static int trace_last_reset=0;
+
+static const int startup_max_to = 500;
+
+static uint32_t *saved_filters = NULL;
+static int saved_filters_cnt = 0;
+
+#define ECRNX_FW_TRACE_CHECK_INT_MS 1000
+
+
+/**
+ * ecrnx_fw_trace_work() - Work function to check for new traces
+ * process function for &struct ecrnx_fw_trace.work
+ *
+ * @ws: work structure
+ *
+ * Check if new traces are available in the shared buffer, by comparing current
+ * end index with end index in the last check. If so wake up pending threads,
+ * otherwise re-schedule the work is there are still some pending readers.
+ *
+ * Note: If between two check firmware exactly write one buffer of trace then
+ * those traces will be lost. Fortunately this is very unlikely to happen.
+ *
+ * Note: Even if wake_up doesn't actually wake up threads (because condition
+ * failed), calling waitqueue_active just after will still return false.
+ * Fortunately this should never happen (new trace should always trigger the
+ * waiting condition) otherwise it may be needed to re-schedule the work after
+ * wake_up.
+ */
+static void ecrnx_fw_trace_work(struct work_struct *ws)
+{
+ struct delayed_work *dw = container_of(ws, struct delayed_work, work);
+ struct ecrnx_fw_trace *trace = container_of(dw, struct ecrnx_fw_trace, work);
+
+ if (trace->closing ||
+ (!ecrnx_fw_trace_empty(&trace->buf) &&
+ trace->last_read_index != *trace->buf.end)) {
+ trace->last_read_index = *trace->buf.end;
+ wake_up_interruptible(&trace->queue);
+ return;
+ }
+
+ if (waitqueue_active(&trace->queue) && !delayed_work_pending(dw)) {
+ schedule_delayed_work(dw, msecs_to_jiffies(ECRNX_FW_TRACE_CHECK_INT_MS));
+ }
+}
+
+/**
+ * ecrnx_fw_trace_buf_lock() - Lock trace buffer for firmware
+ *
+ * @shared_buf: Pointer to shared buffer
+ *
+ * Very basic synchro mechanism so that fw do not update trace buffer while host
+ * is reading it. Not safe to race condition if host and fw read lock value at
+ * the "same" time.
+ */
+static void ecrnx_fw_trace_buf_lock(struct ecrnx_fw_trace_buf *shared_buf)
+{
+ wait:
+ while(*shared_buf->lock == ECRNX_FW_TRACE_LOCKED) {}
+ *shared_buf->lock &= ECRNX_FW_TRACE_LOCKED_HOST;
+
+ /* re-read to reduce race condition window */
+ if (*shared_buf->lock == ECRNX_FW_TRACE_LOCKED)
+ goto wait;
+}
+
+/**
+ * ecrnx_fw_trace_buf_unlock() - Unlock trace buffer for firmware
+ *
+ * @shared_buf: Pointer to shared buffer
+ *
+ */
+static void ecrnx_fw_trace_buf_unlock(struct ecrnx_fw_trace_buf *shared_buf)
+{
+ *shared_buf->lock = ECRNX_FW_TRACE_READY;
+}
+
+/**
+ * ecrnx_fw_trace_buf_init() - Initialize ecrnx_fw_trace_buf structure
+ *
+ * @shared_buf: Structure to initialize
+ * @ipc: Pointer to IPC shard structure that contains trace buffer info
+ *
+ *
+ * Return: 0 if initialization succeed, <0 otherwise. It can only fail if
+ * trace feature is not enabled in the firmware (or buffer is corrupted).
+ */
+int ecrnx_fw_trace_buf_init(struct ecrnx_fw_trace_buf *shared_buf,
+ struct ecrnx_fw_trace_ipc_desc *ipc)
+{
+ uint16_t lock_status = ipc->pattern;
+
+ if ((lock_status != ECRNX_FW_TRACE_READY &&
+ lock_status != ECRNX_FW_TRACE_LOCKED)) {
+ shared_buf->data = NULL;
+ return -ENOENT;
+ }
+
+ /* Buffer starts <offset> bytes from the location of ipc->offset */
+ shared_buf->data = (uint16_t *)((uint8_t *)(&ipc->offset) + ipc->offset);
+ shared_buf->lock = &ipc->pattern;
+ shared_buf->size = ipc->size;
+ shared_buf->start = &ipc->start;
+ shared_buf->end = &ipc->end;
+ shared_buf->reset_idx = ++trace_last_reset;
+
+ /* backward compatibilty with firmware without trace activation */
+ if ((ipc->nb_compo >> 16) == ECRNX_FW_TRACE_READY) {
+ shared_buf->nb_compo = ipc->nb_compo & 0xffff;
+ shared_buf->compo_table = (uint32_t *)((uint8_t *)(&ipc->offset_compo)
+ + ipc->offset_compo);
+ } else {
+ shared_buf->nb_compo = 0;
+ shared_buf->compo_table = NULL;
+ }
+
+ return 0;
+}
+
+/**
+ * ecrnx_fw_trace_init() - Initialize ecrnx_fw_trace structure
+ *
+ * @trace: Structure to initialize
+ * @ipc: Pointer to IPC shard structure that contains trace buffer info
+ *
+ * Return: 0 if initialization succeed, <0 otherwise. It can only fail if
+ * trace feature is not enabled in the firmware (or buffer is corrupted).
+ */
+int ecrnx_fw_trace_init(struct ecrnx_fw_trace *trace,
+ struct ecrnx_fw_trace_ipc_desc *ipc)
+{
+ if (ecrnx_fw_trace_buf_init(&trace->buf, ipc))
+ return -ENOENT;
+
+ INIT_DELAYED_WORK(&trace->work, ecrnx_fw_trace_work);
+ init_waitqueue_head(&trace->queue);
+ mutex_init(&trace->mutex);
+ trace->closing = false;
+ return 0;
+}
+
+/**
+ * ecrnx_fw_trace_deinit() - De-initialization before releasing ecrnx_fw_trace
+ *
+ * @trace: fw trace control structure
+ */
+void ecrnx_fw_trace_deinit(struct ecrnx_fw_trace *trace)
+{
+ trace->closing = true;
+ flush_delayed_work(&trace->work);
+ trace->buf.data = NULL;
+}
+
+/**
+ * ecrnx_fw_trace_reset_local() - Reset local buffer pointer/status
+ *
+ * @local_buf: structure to reset
+ */
+static void ecrnx_fw_trace_reset_local(struct ecrnx_fw_trace_local_buf *local_buf)
+{
+ local_buf->read = local_buf->data;
+ local_buf->write = local_buf->data;
+ local_buf->nb_entries = 0;
+ local_buf->free_space = local_buf->size;
+ local_buf->last_read = NULL;
+ local_buf->reset_idx = 0;
+ local_buf->show_reset = NULL;
+}
+
+/**
+ * ecrnx_fw_trace_alloc_local() - Allocate a local buffer and initialize
+ * ecrnx_fw_trace_local_buf structure
+ *
+ * @local_buf: structure to initialize
+ * @size: Size of the buffer to allocate
+ *
+ * @local structure is initialized to use the allocated buffer.
+ *
+ * Return: 0 if allocation succeed and <0 otherwise.
+ */
+int ecrnx_fw_trace_alloc_local(struct ecrnx_fw_trace_local_buf *local_buf,
+ int size)
+{
+ local_buf->data = kmalloc(size * sizeof(uint16_t), GFP_KERNEL);
+ if (!local_buf->data) {
+ return -ENOMEM;
+ }
+
+ local_buf->data_end = local_buf->data + size;
+ local_buf->size = size;
+ ecrnx_fw_trace_reset_local(local_buf);
+ return 0;
+}
+
+/**
+ * ecrnx_fw_trace_free_local() - Free local buffer
+ *
+ * @local_buf: structure containing buffer pointer to free.
+ */
+void ecrnx_fw_trace_free_local(struct ecrnx_fw_trace_local_buf *local_buf)
+{
+ if (local_buf->data)
+ kfree(local_buf->data);
+ local_buf->data = NULL;
+}
+
+/**
+ * ecrnx_fw_trace_strlen() - Return buffer size needed convert a trace entry into
+ * string
+ *
+ * @entry: Pointer on trace entry
+ *
+ */
+static inline int ecrnx_fw_trace_strlen(uint16_t *entry)
+{
+ return (ECRNX_FW_TRACE_HEADER_ASCII_LEN +
+ (ECRNX_FW_TRACE_NB_PARAM(entry) * ECRNX_FW_TRACE_PARAM_ASCII_LEN) +
+ 1); /* for \n */
+}
+
+/**
+ * ecrnx_fw_trace_to_str() - Convert one trace entry to a string
+ *
+ * @trace: Poitner to the trace entry
+ * @buf: Buffer for the string
+ * @size: Size of the string buffer, updated with the actual string size
+ *
+ * Return: pointer to the next tag entry.
+ */
+static uint16_t *ecrnx_fw_trace_to_str(uint16_t *trace, char *buf, size_t *size)
+{
+ uint32_t ts, id;
+ int nb_param;
+ int res, buf_idx = 0, left = *size;
+
+ id = ECRNX_FW_TRACE_ID(trace);
+ nb_param = ECRNX_FW_TRACE_NB_PARAM(trace);
+
+ trace +=2;
+ ts = *trace++;
+ ts <<= 16;
+ ts += *trace++;
+
+ res = scnprintf(&buf[buf_idx], left, ECRNX_FW_TRACE_HEADER_FMT, ts, id);
+ buf_idx += res;
+ left -= res;
+
+ while (nb_param > 0) {
+ res = scnprintf(&buf[buf_idx], left, ECRNX_FW_TRACE_PARAM_FMT, *trace++);
+ buf_idx += res;
+ left -= res;
+ nb_param--;
+ }
+
+ res = scnprintf(&buf[buf_idx], left, "\n");
+ left -= res;
+ *size = (*size - left);
+
+ return trace;
+}
+
+/**
+ * ecrnx_fw_trace_copy_entry() - Copy one trace entry in a local buffer
+ *
+ * @local_buf: Local buffer to copy trace into
+ * @trace_entry: Pointer to the trace entry (in shared memory) to copy
+ * @size: Size, in 16bits words, of the trace entry
+ *
+ * It is assumed that local has enough contiguous free-space available in
+ * local buffer (i.e. from local_buf->write) to copy this trace.
+ */
+static void ecrnx_fw_trace_copy_entry(struct ecrnx_fw_trace_local_buf *local_buf,
+ uint16_t *trace_entry, int size)
+{
+ uint16_t *write = local_buf->write;
+ uint16_t *read = trace_entry;
+ int i;
+
+ for (i = 0; i < size; i++) {
+ *write++ = *read++;
+ }
+
+ if (write >= local_buf->data_end)
+ local_buf->write = local_buf->data;
+ else
+ local_buf->write = write;
+
+ local_buf->free_space -= size;
+ local_buf->last_read = trace_entry;
+ local_buf->last_read_value = *trace_entry;
+ local_buf->nb_entries++;
+}
+
+/**
+ * ecrnx_fw_trace_copy() - Copy trace entries from shared to local buffer
+ *
+ * @trace_buf: Pointer to shard buffer
+ * @local_buf: Pointer to local buffer
+ *
+ * Copy has many trace entry as possible from shared buffer to local buffer
+ * without overwriting traces in local buffer.
+ *
+ * Return: number of trace entries copied to local buffer
+ */
+static int ecrnx_fw_trace_copy(struct ecrnx_fw_trace *trace,
+ struct ecrnx_fw_trace_local_buf *local_buf)
+{
+ struct ecrnx_fw_trace_buf *trace_buf = &trace->buf;
+ uint16_t *ptr, *ptr_end, *ptr_limit;
+ int entry_size, ret = 0;
+
+ if (mutex_lock_interruptible(&trace->mutex))
+ return 0;
+
+ /* reset last_read ptr if shared buffer has been reset */
+ if (local_buf->reset_idx != trace_buf->reset_idx) {
+ local_buf->show_reset = local_buf->write;
+ local_buf->reset_idx = trace_buf->reset_idx;
+ local_buf->last_read = NULL;
+ }
+
+ ecrnx_fw_trace_buf_lock(trace_buf);
+
+ ptr_end = trace_buf->data + *trace_buf->end;
+ if (ecrnx_fw_trace_empty(trace_buf) || (ptr_end == local_buf->last_read))
+ goto end;
+ ptr_limit = trace_buf->data + trace_buf->size;
+
+ if (local_buf->last_read &&
+ (local_buf->last_read_value == *local_buf->last_read)) {
+ ptr = local_buf->last_read;
+ ptr += ECRNX_FW_TRACE_ENTRY_SIZE(ptr);
+ } else {
+ ptr = trace_buf->data + *trace_buf->start;
+ }
+
+ while (1) {
+
+ if ((ptr == ptr_limit) || (*ptr == ECRNX_FW_TRACE_LAST_ENTRY))
+ ptr = trace_buf->data;
+
+ entry_size = ECRNX_FW_TRACE_ENTRY_SIZE(ptr);
+
+ if ((ptr + entry_size) > ptr_limit) {
+ ECRNX_ERR("Corrupted trace buffer\n");
+ _ecrnx_fw_trace_reset(trace, false);
+ break;
+ } else if (entry_size > local_buf->size) {
+ ECRNX_ERR("FW_TRACE local buffer too small, trace skipped");
+ goto next_entry;
+ }
+
+ if (local_buf->free_space >= entry_size) {
+ int contiguous = local_buf->data_end - local_buf->write;
+
+ if ((local_buf->write < local_buf->read) || contiguous >= entry_size) {
+ /* enough contiguous memory from local_buf->write */
+ ecrnx_fw_trace_copy_entry(local_buf, ptr, entry_size);
+ ret++;
+ } else if ((local_buf->free_space - contiguous) >= entry_size) {
+ /* not enough contiguous from local_buf->write but enough
+ from local_buf->data */
+ *local_buf->write = ECRNX_FW_TRACE_LAST_ENTRY;
+ if (local_buf->show_reset == local_buf->write)
+ local_buf->show_reset = local_buf->data;
+ local_buf->write = local_buf->data;
+ local_buf->free_space -= contiguous;
+ ecrnx_fw_trace_copy_entry(local_buf, ptr, entry_size);
+ ret++;
+ } else {
+ /* not enough contiguous memory */
+ goto end;
+ }
+ } else {
+ goto end;
+ }
+
+ if (ptr == ptr_end)
+ break;
+
+ next_entry:
+ ptr += entry_size;
+ }
+
+ end:
+ ecrnx_fw_trace_buf_unlock(trace_buf);
+ mutex_unlock(&trace->mutex);
+ return ret;
+}
+
+/**
+ * ecrnx_fw_trace_read_local() - Read trace from local buffer and convert it to
+ * string in a user buffer
+ *
+ * @local_buf: Pointer to local buffer
+ * @user_buf: Pointer to user buffer
+ * @size: Size of the user buffer
+ *
+ * Read traces from shared buffer to write them in the user buffer after string
+ * conversion. Stop when no more space in user buffer or no more trace to read.
+ *
+ * Return: The size written in the user buffer.
+ */
+static size_t ecrnx_fw_trace_read_local(struct ecrnx_fw_trace_local_buf *local_buf,
+ char __user *user_buf, size_t size)
+{
+ uint16_t *ptr;
+ char *str = NULL; // worst case 255 params
+ size_t str_size;
+ int entry_size;
+ size_t res = 0 , remain = size, not_cpy = 0;
+
+ if (!local_buf->nb_entries)
+ return res;
+
+ str = kmalloc(FW_TRACE_READ_DUMP_max_SIZE, GFP_ATOMIC);
+ if(!str){
+ return 0;
+ }
+
+ ptr = local_buf->read;
+ while(local_buf->nb_entries && !not_cpy) {
+
+ if (local_buf->show_reset == ptr) {
+ if (remain < ECRNX_FW_TRACE_RESET_SIZE)
+ break;
+
+ local_buf->show_reset = NULL;
+ not_cpy = copy_to_user(user_buf + res, ECRNX_FW_TRACE_RESET,
+ ECRNX_FW_TRACE_RESET_SIZE);
+ res += (ECRNX_FW_TRACE_RESET_SIZE - not_cpy);
+ remain -= (ECRNX_FW_TRACE_RESET_SIZE - not_cpy);
+ }
+
+ if (remain < ecrnx_fw_trace_strlen(ptr))
+ break;
+
+ entry_size = ECRNX_FW_TRACE_ENTRY_SIZE(ptr);
+ str_size = sizeof(str);
+ ptr = ecrnx_fw_trace_to_str(ptr, str, &str_size);
+ not_cpy = copy_to_user(user_buf + res, str, str_size);
+ str_size -= not_cpy;
+ res += str_size;
+ remain -= str_size;
+
+ local_buf->nb_entries--;
+ local_buf->free_space += entry_size;
+ if (ptr >= local_buf->data_end) {
+ ptr = local_buf->data;
+ } else if (*ptr == ECRNX_FW_TRACE_LAST_ENTRY) {
+ local_buf->free_space += local_buf->data_end - ptr;
+ ptr = local_buf->data;
+ }
+ local_buf->read = ptr;
+ }
+
+ /* read all entries reset pointer */
+ if ( !local_buf->nb_entries) {
+
+ local_buf->write = local_buf->read = local_buf->data;
+ local_buf->free_space = local_buf->size;
+ }
+
+ kfree(str);
+ return res;
+}
+
+/**
+ * ecrnx_fw_trace_read() - Update local buffer from shared buffer and convert
+ * local buffer to string in user buffer
+ *
+ * @trace: Fw trace control structure
+ * @local_buf: Local buffer to update and read from
+ * @dont_wait: Indicate whether function should wait or not for traces before
+ * returning
+ * @user_buf: Pointer to user buffer
+ * @size: Size of the user buffer
+ *
+ * Read traces from shared buffer to write them in the user buffer after string
+ * conversion. Stop when no more space in user buffer or no more trace to read.
+ *
+ * Return: The size written in the user buffer if > 0, -EAGAIN if there is no
+ * new traces and dont_wait is set and -ERESTARTSYS if signal has been
+ * received while waiting for new traces.
+ */
+size_t ecrnx_fw_trace_read(struct ecrnx_fw_trace *trace,
+ struct ecrnx_fw_trace_local_buf *local_buf,
+ bool dont_wait, char __user *user_buf, size_t size)
+{
+ size_t res = 0;
+
+ ecrnx_fw_trace_copy(trace, local_buf);
+
+ while(!local_buf->nb_entries) {
+ int last_index;
+
+ if (dont_wait)
+ return -EAGAIN;
+
+ /* no trace, schedule work to periodically check trace buffer */
+ if (!delayed_work_pending(&trace->work)) {
+ trace->last_read_index = *trace->buf.end;
+ schedule_delayed_work(&trace->work,
+ msecs_to_jiffies(ECRNX_FW_TRACE_CHECK_INT_MS));
+ }
+
+ /* and wait for traces */
+ last_index = *trace->buf.end;
+ if (wait_event_interruptible(trace->queue,
+ (trace->closing ||
+ (last_index != *trace->buf.end)))) {
+ return -ERESTARTSYS;
+ }
+
+ if (trace->closing)
+ return 0;
+
+ ecrnx_fw_trace_copy(trace, local_buf);
+ }
+
+ /* copy as many traces as possible in user buffer */
+ while (1) {
+ size_t read;
+ read = ecrnx_fw_trace_read_local(local_buf, user_buf + res, size - res);
+ res += read;
+ ecrnx_fw_trace_copy(trace, local_buf);
+ if (!read)
+ break;
+ }
+
+ return res;
+}
+
+
+/**
+ * _ecrnx_fw_trace_dump() - Dump shared trace buffer in kernel buffer
+ *
+ * @trace_buf: Pointer to shared trace buffer;
+ *
+ * Called when error is detected, output trace on dmesg directly read from
+ * shared memory
+ */
+void _ecrnx_fw_trace_dump(struct ecrnx_fw_trace_buf *trace_buf)
+{
+ uint16_t *ptr, *ptr_end, *ptr_limit, *next_ptr;
+ char *buf = NULL; // worst case 255 params
+ size_t size;
+
+ if (!trace_buf->data || ecrnx_fw_trace_empty(trace_buf))
+ return;
+
+ ecrnx_fw_trace_buf_lock(trace_buf);
+
+ ptr = trace_buf->data + *trace_buf->start;
+ ptr_end = trace_buf->data + *trace_buf->end;
+ ptr_limit = trace_buf->data + trace_buf->size;
+
+ buf = kmalloc(FW_TRACE_READ_DUMP_max_SIZE, GFP_ATOMIC);
+ while (1) {
+ size = FW_TRACE_READ_DUMP_max_SIZE;
+ next_ptr = ecrnx_fw_trace_to_str(ptr, buf, &size);
+ ECRNX_PRINT("%s", buf);
+
+ if (ptr == ptr_end) {
+ break;
+ } else if ((next_ptr == ptr_limit) ||
+ (*next_ptr == ECRNX_FW_TRACE_LAST_ENTRY)) {
+ ptr = trace_buf->data;
+ } else if (next_ptr > ptr_limit) {
+ ECRNX_ERR("Corrupted trace buffer\n");
+ break;
+ } else {
+ ptr = next_ptr;
+ }
+ }
+
+ ecrnx_fw_trace_buf_unlock(trace_buf);
+ kfree(buf);
+}
+
+/**
+ * _ecrnx_fw_trace_reset() - Reset trace buffer at firmware level
+ *
+ * @trace: Pointer to shared trace buffer;
+ * @bool: Indicate if mutex must be aquired before
+ */
+int _ecrnx_fw_trace_reset(struct ecrnx_fw_trace *trace, bool lock)
+{
+ struct ecrnx_fw_trace_buf *trace_buf = &trace->buf;
+
+ if (lock && mutex_lock_interruptible(&trace->mutex))
+ return -ERESTARTSYS;
+
+ if (trace->buf.data) {
+ ecrnx_fw_trace_buf_lock(trace_buf);
+ *trace_buf->start = 0;
+ *trace_buf->end = trace_buf->size + 1;
+ trace_buf->reset_idx = ++trace_last_reset;
+ ecrnx_fw_trace_buf_unlock(trace_buf);
+ }
+
+ if (lock)
+ mutex_unlock(&trace->mutex);
+ return 0;
+}
+
+/**
+ * ecrnx_fw_trace_get_trace_level() - Get trace level for a given component
+ *
+ * @trace: Pointer to shared trace buffer;
+ * @compo_id: Index of the componetn in the table
+ *
+ * Return: The trace level set for the given component, 0 if component index
+ * is invalid.
+ */
+static uint32_t ecrnx_fw_trace_get_trace_level(struct ecrnx_fw_trace_buf *trace_buf,
+ unsigned int compo_id)
+{
+ if (compo_id >= trace_buf->nb_compo)
+ return 0;
+ return trace_buf->compo_table[compo_id];
+}
+
+/**
+ * ecrnx_fw_trace_set_trace_level() - Set trace level for a given component
+ *
+ * @trace_buf: Pointer to shared trace buffer;
+ * @compo_id: Index of the componetn in the table
+ * @level: Trace level to set
+ *
+ * Set all components if compo_id is equals to the number of component and
+ * does nothing if it is greater.
+ */
+static void ecrnx_fw_trace_set_trace_level(struct ecrnx_fw_trace_buf *trace_buf,
+ unsigned int compo_id, uint32_t level)
+{
+ if (compo_id > trace_buf->nb_compo)
+ return;
+
+ if (compo_id == trace_buf->nb_compo) {
+ int i;
+ for (i = 0; i < trace_buf->nb_compo; i++) {
+ trace_buf->compo_table[i] = level;
+ }
+ } else {
+ trace_buf->compo_table[compo_id] = level;
+ }
+}
+
+/**
+ * ecrnx_fw_trace_level_read() - Write current trace level in a user buffer
+ * as a string
+ *
+ * @trace: Fw trace control structure
+ * @user_buf: Pointer to user buffer
+ * @len: Size of the user buffer
+ * @ppos: position offset
+ *
+ * Return: Number of bytes written in user buffer if > 0, error otherwise
+ */
+size_t ecrnx_fw_trace_level_read(struct ecrnx_fw_trace *trace,
+ char __user *user_buf, size_t len, loff_t *ppos)
+{
+ struct ecrnx_fw_trace_buf *trace_buf = &trace->buf;
+ size_t res = 0;
+ int i, size;
+ char *buf;
+
+ size = trace_buf->nb_compo * 16;
+ buf = kmalloc(size, GFP_KERNEL);
+ if (buf == NULL)
+ return 0;
+
+ if (mutex_lock_interruptible(&trace->mutex)) {
+ kfree(buf);
+ return -ERESTARTSYS;
+ }
+
+ for (i = 0 ; i < trace_buf->nb_compo ; i ++) {
+ res += scnprintf(&buf[res], size - res, "%3d:0x%08x\n", i,
+ ecrnx_fw_trace_get_trace_level(trace_buf, i));
+ }
+ mutex_unlock(&trace->mutex);
+
+ res = simple_read_from_buffer(user_buf, len, ppos, buf, res);
+
+ kfree(buf);
+ return res;
+}
+
+/**
+ * ecrnx_fw_trace_level_write() - Read trace level from a user buffer provided
+ * as a string and applyt them.
+ *
+ * @trace: Fw trace control structure
+ * @user_buf: Pointer to user buffer
+ * @len: Size of the user buffer
+ *
+ * trace level must be provided in the following form:
+ * <compo_id>:<trace_level> where <compo_id> is in decimal notation and
+ * <trace_level> in decical or hexadecimal notation.
+ * Several trace level can be provided, separated by space,tab or new line.
+ *
+ * Return: Number of bytes read form user buffer if > 0, error otherwise
+ */
+size_t ecrnx_fw_trace_level_write(struct ecrnx_fw_trace *trace,
+ const char __user *user_buf, size_t len)
+{
+ struct ecrnx_fw_trace_buf *trace_buf = &trace->buf;
+ char *buf, *token, *next;
+
+ buf = kmalloc(len + 1, GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ if (copy_from_user(buf, user_buf, len)) {
+ kfree(buf);
+ return -EFAULT;
+ }
+ buf[len] = '\0';
+
+ if (mutex_lock_interruptible(&trace->mutex)) {
+ kfree(buf);
+ return -ERESTARTSYS;
+ }
+
+ next = buf;
+ token = strsep(&next, " \t\n");
+ while (token) {
+ unsigned int compo, level;
+ if ((sscanf(token, "%d:0x%x", &compo, &level) == 2)||
+ (sscanf(token, "%d:%d", &compo, &level) == 2)) {
+ ecrnx_fw_trace_set_trace_level(trace_buf, compo, level);
+ }
+
+ token = strsep(&next, " \t");
+ }
+ mutex_unlock(&trace->mutex);
+
+ kfree(buf);
+ return len;
+}
+
+/**
+ * ecrnx_fw_trace_config_filters() - Update FW trace filters
+ *
+ * @trace_buf: Pointer to shared buffer
+ * @ipc: Pointer to IPC shared structure that contains trace buffer info
+ * @ftl: Firmware trace level
+ *
+ * Return: 0 if the trace filters are successfully updated, <0 otherwise.
+ */
+int ecrnx_fw_trace_config_filters(struct ecrnx_fw_trace_buf *trace_buf,
+ struct ecrnx_fw_trace_ipc_desc *ipc, char *ftl)
+{
+ int to;
+ char *next, *token;
+
+ to = 0;
+ while((ipc->pattern != ECRNX_FW_TRACE_READY) && (to < startup_max_to))
+ {
+ msleep(50);
+ to += 50;
+ }
+
+ if (ecrnx_fw_trace_buf_init(trace_buf, ipc))
+ return -ENOENT;
+
+ next = ftl;
+ token = strsep(&next, " ");
+ while(token)
+ {
+ unsigned int compo, ret, id, level = 0;
+ char action;
+
+ if ((sscanf(token, "%d%c0x%x", &compo, &action, &id) == 3)||
+ (sscanf(token, "%d%c%d", &compo, &action, &id) == 3))
+ {
+ if(action == '=')
+ {
+ level = id;
+ }
+ else
+ {
+ ret = ecrnx_fw_trace_get_trace_level(trace_buf, compo);
+ if(action == '+')
+ level = (ret | id);
+ else if (action == '-')
+ level = (ret & ~id);
+ }
+ ecrnx_fw_trace_set_trace_level(trace_buf, compo, level);
+ }
+
+ token = strsep(&next, " ");
+ }
+
+ return 0;
+}
+
+/**
+ * ecrnx_fw_trace_save_filters() - Save filters currently configured so that
+ * they can be restored with ecrnx_fw_trace_restore_filters()
+ *
+ * @trace: Fw trace control structure
+ * @return 0 if filters have been saved and != 0 in case on error
+ */
+int ecrnx_fw_trace_save_filters(struct ecrnx_fw_trace *trace)
+{
+ int i;
+
+ if (saved_filters)
+ kfree(saved_filters);
+
+ saved_filters_cnt = trace->buf.nb_compo;
+ saved_filters = kmalloc(saved_filters_cnt * sizeof(uint32_t), GFP_KERNEL);
+ if (!saved_filters)
+ return -1;
+
+ for (i = 0; i < saved_filters_cnt; i++) {
+ saved_filters[i] = ecrnx_fw_trace_get_trace_level(&trace->buf, i);
+ }
+
+ return 0;
+}
+
+/**
+ * ecrnx_fw_trace_restore_filters() - Restore filters previoulsy saved
+ * by ecrnx_fw_trace_save_filters()
+ *
+ * @trace: Fw trace control structure
+ * @return 0 if filters have been restored and != 0 in case on error
+ */
+int ecrnx_fw_trace_restore_filters(struct ecrnx_fw_trace *trace)
+{
+ int i;
+
+ if (!saved_filters || (trace->buf.data == NULL))
+ return -1;
+
+ if (saved_filters_cnt != trace->buf.nb_compo) {
+ pr_warn("Number of trace components change between saved and restore\n");
+ if (saved_filters_cnt > trace->buf.nb_compo) {
+ saved_filters_cnt = trace->buf.nb_compo;
+ }
+ }
+
+ for (i = 0; i < saved_filters_cnt; i++) {
+ ecrnx_fw_trace_set_trace_level(&trace->buf, i, saved_filters[i]);
+ }
+
+ kfree(saved_filters);
+ saved_filters = NULL;
+ saved_filters_cnt = 0;
+
+ return 0;
+}
diff --git a/drivers/net/wireless/eswin/ecrnx_fw_trace.h b/drivers/net/wireless/eswin/ecrnx_fw_trace.h
new file mode 100644
index 000000000000..e7df3ad46a09
--- /dev/null
+++ b/drivers/net/wireless/eswin/ecrnx_fw_trace.h
@@ -0,0 +1,161 @@
+/**
+ ******************************************************************************
+ *
+ * ecrnx_fw_trace.h
+ *
+ * Copyright (C) RESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+
+#ifndef _ECRNX_FW_TRACE_H_
+#define _ECRNX_FW_TRACE_H_
+
+#include <linux/mutex.h>
+#include <linux/wait.h>
+#include <linux/workqueue.h>
+
+#define FW_TRACE_READ_DUMP_max_SIZE 1824
+/**
+ * struct ecrnx_fw_trace_desc - Trace buffer info as provided by fw in ipc
+ *
+ * @pattern: Synchro pattern
+ * @start: Index of first entry in trace buffer
+ * @end: Index of last entry in trace buffer
+ * @size: Size, in 16bits words, od the trace buffer
+ * @offset: Offset, in bytest, to the start of the buffer from the address of
+ * this field.
+ * @nb_compo: Number of filterable component (16LSB) synchro pattern (16 MSB)
+ * @offset_compo: Offset, in bytest, to the start of the component activation
+ * table from the address of this field.
+ */
+struct ecrnx_fw_trace_ipc_desc {
+ volatile uint16_t pattern;
+ volatile uint32_t start;
+ volatile uint32_t end;
+ volatile uint32_t size;
+ volatile uint32_t offset;
+ volatile uint32_t nb_compo;
+ volatile uint32_t offset_compo;
+};
+
+/**
+ * struct ecrnx_fw_trace_buf - Info for trace buffer in shared memory
+ *
+ * @lock: Address of the synchro word
+ * @data: Address of the trace buffer
+ * @size: Size, in 16bits words, of the trace buffer
+ * @start: Address on the current index (in 16 bits words) of the first trace
+ * entry.
+ * @end: Address on the current index (in 16 bits words) of the last trace
+ * entry. If *end > size, it means that the trace buffer contains no traces.
+ * @reset_idx: Increased each time the trace buffer is reset
+ * (by calling _ecrnx_fw_trace_reset())
+ * @nb_compo: Size of the compo_table
+ * @compo_table: Table containing component filter status.
+ */
+struct ecrnx_fw_trace_buf {
+ volatile uint16_t *lock;
+ uint16_t *data;
+ uint32_t size;
+ volatile uint32_t *start;
+ volatile uint32_t *end;
+ int reset_idx;
+ unsigned int nb_compo;
+ uint32_t *compo_table;
+};
+
+/**
+ * struct ecrnx_fw_trace_local_buf - Info for a local trace buffer
+ *
+ * @data: Address of the local buffer
+ * @data_end: Address after the end of the local buffer
+ * @size: Size, in 16bits words, oth the local buffer
+ * @read: Pointer to the next trace entry to read
+ * @write: Pointer to the next entry to write
+ * @nb_entries: Number of trace entries ready to be read
+ * @free_space: Free space, in 16bits words, in the buffer.
+ * @last_read: Address of the last entry read in the shared buffer
+ * @last_read_value: First word of the last trace entry read.
+ * @reset_idx: Reset index. If it doesn't match share buffer index then it
+ * means that share buffer has been resetted since last read.
+ */
+struct ecrnx_fw_trace_local_buf {
+ uint16_t *data;
+ uint16_t *data_end;
+ uint32_t size;
+ uint16_t *read;
+ uint16_t *write;
+ uint16_t nb_entries;
+ uint32_t free_space;
+ uint16_t *last_read;
+ uint16_t last_read_value;
+ int reset_idx;
+ uint16_t * show_reset;
+};
+
+
+/**
+ * struct ecrnx_fw_trace - info to handle several reader of the shared buffer
+ *
+ * @buf: shared trace buffer.
+ * @mutex: mutex, used to prevent several reader updating shared buffer at the
+ * same time.
+ * @queue: queue, used to delay reader.
+ * @work: work used to periodically check for new traces in shared buffer.
+ * @last_read_index: Last read index from shared buffer
+ * @closing: Indicate whether is driver is being removed, meaning that reader
+ * should no longer wait no new traces
+ */
+struct ecrnx_fw_trace {
+ struct ecrnx_fw_trace_buf buf;
+ struct mutex mutex;
+ wait_queue_head_t queue;
+ struct delayed_work work;
+ int last_read_index;
+ bool closing;
+};
+
+int ecrnx_fw_trace_init(struct ecrnx_fw_trace *trace,
+ struct ecrnx_fw_trace_ipc_desc *ipc);
+void ecrnx_fw_trace_deinit(struct ecrnx_fw_trace *trace);
+
+int ecrnx_fw_trace_buf_init(struct ecrnx_fw_trace_buf *shared_buf,
+ struct ecrnx_fw_trace_ipc_desc *ipc);
+
+int _ecrnx_fw_trace_reset(struct ecrnx_fw_trace *trace, bool lock);
+void _ecrnx_fw_trace_dump(struct ecrnx_fw_trace_buf *trace);
+
+int ecrnx_fw_trace_alloc_local(struct ecrnx_fw_trace_local_buf *local,
+ int size);
+void ecrnx_fw_trace_free_local(struct ecrnx_fw_trace_local_buf *local);
+
+
+size_t ecrnx_fw_trace_read(struct ecrnx_fw_trace *trace,
+ struct ecrnx_fw_trace_local_buf *local_buf,
+ bool dont_wait, char __user *user_buf, size_t size);
+
+
+size_t ecrnx_fw_trace_level_read(struct ecrnx_fw_trace *trace,
+ char __user *user_buf, size_t len, loff_t *ppos);
+size_t ecrnx_fw_trace_level_write(struct ecrnx_fw_trace *trace,
+ const char __user *user_buf, size_t len);
+
+
+int ecrnx_fw_trace_config_filters(struct ecrnx_fw_trace_buf *trace_buf,
+ struct ecrnx_fw_trace_ipc_desc *ipc, char *ftl);
+
+int ecrnx_fw_trace_save_filters(struct ecrnx_fw_trace *trace);
+int ecrnx_fw_trace_restore_filters(struct ecrnx_fw_trace *trace);
+
+/**
+ * ecrnx_fw_trace_empty() - Check if shared buffer is empty
+ *
+ * @shared_buf: Pointer to shared buffer
+ */
+static inline bool ecrnx_fw_trace_empty(struct ecrnx_fw_trace_buf *shared_buf)
+{
+ return (*shared_buf->end >= shared_buf->size);
+}
+
+#endif /* _ECRNX_FW_TRACE_H_ */
diff --git a/drivers/net/wireless/eswin/ecrnx_iwpriv.c b/drivers/net/wireless/eswin/ecrnx_iwpriv.c
new file mode 100644
index 000000000000..037fe56246a3
--- /dev/null
+++ b/drivers/net/wireless/eswin/ecrnx_iwpriv.c
@@ -0,0 +1,396 @@
+/**
+ ******************************************************************************
+ *
+ * @file ecrnx_iwpriv.c
+ *
+ * @brief iwpriv function definitions
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+
+/**
+ * INCLUDE FILES
+ ******************************************************************************
+ */
+#include <net/cfg80211.h>
+#include <net/iw_handler.h>
+#include "ecrnx_defs.h"
+#include "eswin_utils.h"
+
+#ifdef CONFIG_ECRNX_WIFO_CAIL
+#include "ecrnx_amt.h"
+#include "core.h"
+#endif
+
+#ifdef CONFIG_WIRELESS_EXT
+/**
+ * FUNCTION DEFINITIONS
+ ******************************************************************************
+ */
+#define IN
+#define OUT
+
+#ifdef CONFIG_WEXT_PRIV
+ /* This may be wrong. When using the new SIOCIWFIRSTPRIV range, we probably
+ * should use only "GET" ioctls (last bit set to 1). "SET" ioctls are root
+ * only and don't return the modified struct ifreq to the application which
+ * is usually a problem. - Jean II */
+#ifdef CONFIG_ECRNX_WIFO_CAIL
+#define IOCTL_IWPRIV_AMT (SIOCIWFIRSTPRIV + 1)
+#endif
+#define IOCTL_IWPRIV_WD (SIOCIWFIRSTPRIV + 3)
+
+#if 0
+static int priv_set_int(IN struct net_device *prNetDev,
+ IN struct iw_request_info *prIwReqInfo,
+ IN union iwreq_data *prIwReqData, IN OUT char *pcExtra)
+{
+ int *flag = (int*)pcExtra;
+ ECRNX_PRINT("cmd=%x, flags=%x\n",
+ prIwReqInfo->cmd, prIwReqInfo->flags);
+ ECRNX_PRINT("mode=%x, flags=%x\n",
+ prIwReqData->mode, prIwReqData->data.flags);
+ *flag = 0x1234;
+ prIwReqData->param.value = 0x1230;
+
+ return 1;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+* \brief Private ioctl get int handler.
+*
+* \param[in] pDev Net device requested.
+* \param[out] pIwReq Pointer to iwreq structure.
+* \param[in] prIwReqData The ioctl req structure, use the field of sub-command.
+* \param[out] pcExtra The buffer with put the return value
+*
+* \retval 0 For success.
+* \retval -EOPNOTSUPP If cmd is not supported.
+* \retval -EFAULT For fail.
+*
+*/
+/*----------------------------------------------------------------------------*/
+static int priv_get_int(IN struct net_device *prNetDev,
+ IN struct iw_request_info *prIwReqInfo,
+ IN union iwreq_data *prIwReqData, IN OUT char *pcExtra)
+{
+ int status = 0;
+ prIwReqData->mode = 0xabcd;
+ return status;
+} /* __priv_get_int */
+
+static int priv_set_struct(IN struct net_device *prNetDev,
+ IN struct iw_request_info *prIwReqInfo,
+ IN union iwreq_data *prIwReqData, IN OUT char *pcExtra)
+{
+ ECRNX_PRINT("cmd=%x, flags=%x\n",
+ prIwReqInfo->cmd, prIwReqInfo->flags);
+ ECRNX_PRINT("mode=%x, flags=%x\n",
+ prIwReqData->mode, prIwReqData->data.flags);
+
+ return 0;
+ //return compat_priv(prNetDev, prIwReqInfo,
+ // prIwReqData, pcExtra, __priv_set_struct);
+}
+
+static int priv_get_struct(IN struct net_device *prNetDev,
+ IN struct iw_request_info *prIwReqInfo,
+ IN union iwreq_data *prIwReqData, IN OUT char *pcExtra)
+{
+ ECRNX_PRINT("cmd=%x, flags=%x\n",
+ prIwReqInfo->cmd, prIwReqInfo->flags);
+ ECRNX_PRINT("mode=%x, flags=%x\n",
+ prIwReqData->mode, prIwReqData->data.flags);
+
+ prIwReqData->data.length = 6;
+ memcpy(pcExtra, "ecrnx", 6);
+ return 0;
+
+}
+
+static int priv_get_mac(IN struct net_device *prNetDev,
+ IN struct iw_request_info *prIwReqInfo,
+ IN union iwreq_data *prIwReqData, IN OUT char *pcExtra)
+{
+ struct sockaddr *dst = (struct sockaddr *) pcExtra;
+ struct ecrnx_vif *vif;
+ dbg_req_t req;
+
+ req.dbg_level = DBG_TYPE_D;
+ req.direct = 0;
+
+ vif = netdev_priv(prNetDev);
+ //send cmd to slave
+ ECRNX_PRINT("priv_get_mac: send cmd to slave \n");
+ host_send(&req, sizeof(dbg_req_t), TX_FLAG_IWPRIV_IE);
+ //wait for slave confirm
+ vif->rxdatas = 0;
+ wait_event_interruptible_timeout(vif->rxdataq, vif->rxdatas, 2*HZ);
+
+ ECRNX_PRINT("priv_get_mac: rx_len:%d \n", vif->rxlen);
+ if (!vif->rxlen)
+ return -1;
+
+ prIwReqData->data.length = vif->rxlen;
+ memcpy(dst->sa_data, vif->rxdata, vif->rxlen);
+ dst->sa_family = 1;
+ prIwReqData->data.length = 1;
+
+ return 0;
+}
+
+static int priv_get_vers(IN struct net_device *prNetDev,
+ IN struct iw_request_info *prIwReqInfo,
+ IN union iwreq_data *prIwReqData, IN OUT char *pcExtra)
+{
+ ECRNX_PRINT("get vers cmd=%x, flags=%x\n", prIwReqInfo->cmd, prIwReqInfo->flags);
+ ECRNX_PRINT("mode=%x, flags=%x\n", prIwReqData->mode, prIwReqData->data.flags);
+
+ memcpy(pcExtra, "1.0.1", 6);
+ prIwReqData->data.length = 6;
+
+ return 0;
+}
+
+
+static int priv_set_debug_level(IN struct net_device *prNetDev,
+ IN struct iw_request_info *prIwReqInfo,
+ IN union iwreq_data *prIwReqData, IN OUT char *pcExtra)
+{
+ ECRNX_PRINT("priv_set_debug_level cmd=%x, flags=%x\n",
+ prIwReqInfo->cmd, prIwReqInfo->flags);
+ ECRNX_PRINT("mode=%x, flags=%x\n",
+ prIwReqData->mode, prIwReqData->data.flags);
+
+ ECRNX_PRINT("param_value:%d \n", prIwReqData->param.value);
+
+ ecrnx_dbg_level = prIwReqData->param.value;
+ return 0;
+}
+#endif
+
+#ifdef CONFIG_ECRNX_WIFO_CAIL
+static int priv_amt(IN struct net_device *prNetDev,
+ IN struct iw_request_info *prIwReqInfo,
+ IN union iwreq_data *prIwReqData, IN OUT char *pcExtra)
+{
+ struct sockaddr *dst = (struct sockaddr *) pcExtra;
+
+ if (amt_mode == false) {
+ ECRNX_ERR(" The current mode does not support the AMT commands!!\n");
+ return -1;
+ }
+// printk("buff:%s, len:%d\n", prIwReqData->data.pointer, prIwReqData->data.length);
+ //send cmd to slave
+ char *reqdata = kzalloc(prIwReqData->data.length, GFP_KERNEL);
+ if (!reqdata){
+ return 0;
+ }
+ if (copy_from_user(reqdata, prIwReqData->data.pointer, prIwReqData->data.length)) {
+ return 0;
+ }
+ host_send(reqdata, prIwReqData->data.length, TX_FLAG_AMT_IWPRIV_IE);
+ kfree(reqdata);
+
+ //wait for slave confirm
+ amt_vif.rxdatas = 0;
+ amt_vif.rxlen = 0;
+
+ wait_event_interruptible_timeout(amt_vif.rxdataq, amt_vif.rxdatas, 2*HZ);
+
+ ECRNX_PRINT("rxdatas: rx_len:%d, rxdata:[%s]\n", amt_vif.rxlen,amt_vif.rxdata);
+ if (!amt_vif.rxdatas){
+ return -1;
+ }
+
+ prIwReqData->data.length = amt_vif.rxlen;
+ memcpy(dst->sa_data, amt_vif.rxdata, amt_vif.rxlen);
+ dst->sa_family = 1;
+ memcpy(pcExtra, amt_vif.rxdata, amt_vif.rxlen);
+
+ return 0;
+}
+#endif
+
+static struct ecrnx_vif *get_priv_vif(struct ecrnx_hw *ecrnx_hw)
+{
+ return ecrnx_hw->vif_table[0];
+}
+
+void priv_copy_data_wakeup(struct ecrnx_hw *ecrnx_hw, struct sk_buff *skb)
+{
+ struct ecrnx_vif* ecrnx_vif = get_priv_vif(ecrnx_hw);
+
+ ECRNX_PRINT("iw_cfm vif_start:%d, vif_monitor:%d \n", ecrnx_hw->vif_started, ecrnx_hw->monitor_vif);
+ //print_hex_dump(KERN_INFO, DBG_PREFIX_IW_CFM, DUMP_PREFIX_ADDRESS, 32, 1, skb->data, skb->len, false);
+ if (ECRNX_RXSIZE > skb->len) {
+ ecrnx_vif->rxlen = skb->len;
+ } else {
+ ecrnx_vif->rxlen = ECRNX_RXSIZE;
+ }
+
+ memcpy(ecrnx_vif->rxdata, skb->data, ecrnx_vif->rxlen);
+ ecrnx_vif->rxdatas = 1;
+ wake_up(&ecrnx_vif->rxdataq);
+}
+
+static int priv_wd(IN struct net_device *prNetDev,
+ IN struct iw_request_info *prIwReqInfo,
+ IN union iwreq_data *prIwReqData, IN OUT char *pcExtra)
+{
+ struct sockaddr *dst = (struct sockaddr *) pcExtra;
+ struct ecrnx_vif *vif;
+
+ //printk("priv_wd:%s, len:%d\n", prIwReqData->data.pointer, prIwReqData->data.length);
+ //send cmd to slave
+ char *reqdata = kzalloc(prIwReqData->data.length, GFP_KERNEL);
+ if (!reqdata){
+ return 0;
+ }
+
+ if (copy_from_user(reqdata, prIwReqData->data.pointer, prIwReqData->data.length)) {
+ return 0;
+ }
+ host_send(reqdata, prIwReqData->data.length, TX_FLAG_IWPRIV_IE);
+ kfree(reqdata);
+
+ //wait for slave confirm
+ vif = netdev_priv(prNetDev);
+ vif = get_priv_vif(vif->ecrnx_hw);
+ vif->rxdatas = 0;
+ wait_event_interruptible_timeout(vif->rxdataq, vif->rxdatas, 2*HZ);
+
+ if (!vif->rxdatas)
+ return -1;
+
+ ECRNX_PRINT("priv_wd: rx_len:%d rxdata:[%s]\n", vif->rxlen, vif->rxdata);
+ prIwReqData->data.length = vif->rxlen;
+ memcpy(dst->sa_data, vif->rxdata, vif->rxlen);
+ dst->sa_family = 1;
+ memcpy(pcExtra, vif->rxdata, vif->rxlen);
+
+ return 0;
+}
+
+/*
+ * Structures to export the Wireless Handlers
+ */
+static const struct iw_priv_args ecrnx_wext_private_args[] = {
+#ifdef CONFIG_ECRNX_WIFO_CAIL
+ {IOCTL_IWPRIV_AMT, IW_PRIV_TYPE_CHAR | 2000, IW_PRIV_TYPE_CHAR | 2000, "amt"},
+#endif
+ {IOCTL_IWPRIV_WD, IW_PRIV_TYPE_CHAR | 2000, IW_PRIV_TYPE_CHAR | 2000, "wd"},
+};
+
+const iw_handler ecrnx_wext_private_handler[] = {
+#ifdef CONFIG_ECRNX_WIFO_CAIL
+ [IOCTL_IWPRIV_AMT - SIOCIWFIRSTPRIV] = priv_amt,
+#endif
+ [IOCTL_IWPRIV_WD - SIOCIWFIRSTPRIV] = priv_wd,
+};
+
+#endif
+
+/*------------------------------------------------------------------*/
+/*
+* Commit handler : called after a bunch of SET operations
+*/
+static int ecrnx_wext_config_commit(struct net_device *dev,
+ struct iw_request_info *info, /* NULL */
+ void *zwrq, /* NULL */
+ char *extra) /* NULL */
+{
+ return 1;
+}
+
+static int ecrnx_wext_get_name(struct net_device *dev,
+ struct iw_request_info *info,
+ char *cwrq,
+ char *extra)
+{
+ strcpy(cwrq, "IEEE 802.11");
+ return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Wireless Handler : set frequency
+ */
+static int ecrnx_wext_set_freq(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_freq *fwrq,
+ char *extra)
+{
+ int rc = -EINPROGRESS; /* Call commit handler */
+ ECRNX_PRINT("fwrq->e:%d, fwrq->m:%d \n", fwrq->e, fwrq->m);
+ return rc;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Wireless Handler : get frequency
+ */
+static int ecrnx_wext_get_freq(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_freq *fwrq,
+ char *extra)
+{
+ fwrq->m = 100000 *
+ 2.412;
+ fwrq->e = 1;
+ return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Wireless Handler : set Mode of Operation
+ */
+static int ecrnx_wext_set_mode(struct net_device *dev,
+ struct iw_request_info *info,
+ __u32 *uwrq,
+ char *extra)
+{
+ ECRNX_PRINT("*uwrq:%d \n", *uwrq);
+ return -EINPROGRESS; /* Call commit handler */
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Wireless Handler : get Mode of Operation
+ */
+static int ecrnx_wext_get_mode(struct net_device *dev,
+ struct iw_request_info *info,
+ __u32 *uwrq,
+ char *extra)
+{
+ *uwrq = 0xFFEE;
+ return 0;
+}
+
+static const iw_handler ecrnx_wext_handler[] =
+{
+ (iw_handler) ecrnx_wext_config_commit, /* SIOCSIWCOMMIT */
+ (iw_handler) ecrnx_wext_get_name, /* SIOCGIWNAME */
+ (iw_handler) NULL, /* SIOCSIWNWID */
+ (iw_handler) NULL, /* SIOCGIWNWID */
+ (iw_handler) ecrnx_wext_set_freq, /* SIOCSIWFREQ */
+ (iw_handler) ecrnx_wext_get_freq, /* SIOCGIWFREQ */
+ (iw_handler) ecrnx_wext_set_mode, /* SIOCSIWMODE */
+ (iw_handler) ecrnx_wext_get_mode, /* SIOCGIWMODE */
+};
+
+const struct iw_handler_def ecrnx_wext_handler_def =
+{
+ .num_standard = ARRAY_SIZE(ecrnx_wext_handler),
+ .standard = ecrnx_wext_handler,
+#ifdef CONFIG_WEXT_PRIV
+ .num_private = ARRAY_SIZE(ecrnx_wext_private_handler),
+ .num_private_args = ARRAY_SIZE(ecrnx_wext_private_args),
+ .private = ecrnx_wext_private_handler,
+ .private_args = ecrnx_wext_private_args,
+#endif
+};
+#endif
diff --git a/drivers/net/wireless/eswin/ecrnx_mod_params.c b/drivers/net/wireless/eswin/ecrnx_mod_params.c
new file mode 100644
index 000000000000..0fe6b669f7b9
--- /dev/null
+++ b/drivers/net/wireless/eswin/ecrnx_mod_params.c
@@ -0,0 +1,1408 @@
+/**
+******************************************************************************
+*
+* @file ecrnx_mod_params.c
+*
+* @brief Set configuration according to modules parameters
+*
+* Copyright (C) ESWIN 2015-2020
+*
+******************************************************************************
+*/
+#include <linux/module.h>
+#include <linux/rtnetlink.h>
+
+#include "ecrnx_defs.h"
+#include "ecrnx_tx.h"
+#include "hal_desc.h"
+#include "ecrnx_cfgfile.h"
+#include "reg_access.h"
+#include "ecrnx_compat.h"
+#include "fullmac/ecrnx_defs.h"
+
+#ifdef CONFIG_ECRNX_SOFTMAC
+#define COMMON_PARAM(name, default_softmac, default_fullmac) \
+ .name = default_softmac,
+#define SOFTMAC_PARAM(name, default) .name = default,
+#define FULLMAC_PARAM(name, default)
+#else
+#define COMMON_PARAM(name, default_softmac, default_fullmac) \
+ .name = default_fullmac,
+#define SOFTMAC_PARAM(name, default)
+#define FULLMAC_PARAM(name, default) .name = default,
+#endif /* CONFIG_ECRNX_SOFTMAC */
+
+struct ecrnx_mod_params ecrnx_mod_params = {
+ /* common parameters */
+ COMMON_PARAM(ht_on, true, true)
+ COMMON_PARAM(vht_on, false, false)
+ COMMON_PARAM(he_on, true, true)
+#ifdef CONFIG_6600_HAL
+ COMMON_PARAM(he_mcs_map, IEEE80211_HE_MCS_SUPPORT_0_7, IEEE80211_HE_MCS_SUPPORT_0_7)
+#else
+ COMMON_PARAM(he_mcs_map, IEEE80211_HE_MCS_SUPPORT_0_9, IEEE80211_HE_MCS_SUPPORT_0_9)
+#endif
+ COMMON_PARAM(he_ul_on, false, false)
+ COMMON_PARAM(ldpc_on, false, false)
+ COMMON_PARAM(stbc_on, false, false)
+ COMMON_PARAM(gf_rx_on, true, true)
+ COMMON_PARAM(phy_cfg, 0, 0)
+ COMMON_PARAM(uapsd_timeout, 300, 300)
+ COMMON_PARAM(ap_uapsd_on, true, true)
+ COMMON_PARAM(sgi, true, true)
+ COMMON_PARAM(sgi80, true, true)
+ COMMON_PARAM(use_2040, 1, 1)
+ COMMON_PARAM(nss, 1, 1)
+ COMMON_PARAM(amsdu_rx_max, 2, 2)
+ COMMON_PARAM(bfmee, true, true)
+ COMMON_PARAM(bfmer, true, true)
+ COMMON_PARAM(mesh, true, true)
+ COMMON_PARAM(murx, true, true)
+ COMMON_PARAM(mutx, true, true)
+ COMMON_PARAM(mutx_on, true, true)
+ COMMON_PARAM(use_80, false, false)
+ COMMON_PARAM(custregd, false, false)
+ COMMON_PARAM(custchan, false, false)
+ COMMON_PARAM(roc_dur_max, 500, 500)
+ COMMON_PARAM(listen_itv, 0, 0)
+ COMMON_PARAM(listen_bcmc, true, true)
+ COMMON_PARAM(lp_clk_ppm, 20, 20)
+ COMMON_PARAM(ps_on, true, true)
+ COMMON_PARAM(tx_lft, ECRNX_TX_LIFETIME_MS, ECRNX_TX_LIFETIME_MS)
+ COMMON_PARAM(amsdu_maxnb, NX_TX_PAYLOAD_MAX, NX_TX_PAYLOAD_MAX)
+ // By default, only enable UAPSD for Voice queue (see IEEE80211_DEFAULT_UAPSD_QUEUE comment)
+ COMMON_PARAM(uapsd_queues, IEEE80211_WMM_IE_STA_QOSINFO_AC_VO, IEEE80211_WMM_IE_STA_QOSINFO_AC_VO)
+ COMMON_PARAM(tdls, true, true)
+ COMMON_PARAM(uf, true, true)
+ COMMON_PARAM(ftl, "", "")
+ COMMON_PARAM(dpsm, true, true)
+ COMMON_PARAM(tx_to_bk, 0, 0)
+ COMMON_PARAM(tx_to_be, 0, 0)
+ COMMON_PARAM(tx_to_vi, 0, 0)
+ COMMON_PARAM(tx_to_vo, 0, 0)
+
+ /* SOFTMAC only parameters */
+ SOFTMAC_PARAM(mfp_on, false)
+ SOFTMAC_PARAM(gf_on, false)
+ SOFTMAC_PARAM(bwsig_on, true)
+ SOFTMAC_PARAM(dynbw_on, true)
+ SOFTMAC_PARAM(agg_tx, true)
+ SOFTMAC_PARAM(amsdu_force, 0)
+ SOFTMAC_PARAM(rc_probes_on, false)
+ SOFTMAC_PARAM(cmon, true)
+ SOFTMAC_PARAM(hwscan, true)
+ SOFTMAC_PARAM(autobcn, true)
+
+ /* FULLMAC only parameters */
+ FULLMAC_PARAM(ant_div, false)
+};
+
+#ifdef CONFIG_ECRNX_SOFTMAC
+/* SOFTMAC specific parameters */
+module_param_named(mfp_on, ecrnx_mod_params.mfp_on, bool, S_IRUGO);
+MODULE_PARM_DESC(mfp_on, "Enable MFP (11w) (Default: 0)");
+
+module_param_named(gf_on, ecrnx_mod_params.gf_on, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(gf_on, "Try TXing Green Field if peer supports it (Default: 0)");
+
+module_param_named(bwsig_on, ecrnx_mod_params.bwsig_on, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(bwsig_on, "Enable bandwidth signaling (VHT tx) (Default: 1)");
+
+module_param_named(dynbw_on, ecrnx_mod_params.dynbw_on, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(dynbw_on, "Enable dynamic bandwidth (VHT tx) (Default: 1)");
+
+module_param_named(agg_tx, ecrnx_mod_params.agg_tx, bool, S_IRUGO);
+MODULE_PARM_DESC(agg_tx, "Use A-MPDU in TX (Default: 1)");
+
+module_param_named(amsdu_force, ecrnx_mod_params.amsdu_force, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(amsdu_force, "Use A-MSDU in TX: 0-if advertised, 1-yes, 2-no (Default: 0)");
+
+module_param_named(rc_probes_on, ecrnx_mod_params.rc_probes_on, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(rc_probes_on, "IEEE80211_TX_CTL_RATE_CTRL_PROBE is 1st in AMPDU (Default: 0)");
+
+module_param_named(cmon, ecrnx_mod_params.cmon, bool, S_IRUGO);
+MODULE_PARM_DESC(cmon, "Connection monitoring work handled by the FW (Default: 1)");
+
+module_param_named(hwscan, ecrnx_mod_params.hwscan, bool, S_IRUGO);
+MODULE_PARM_DESC(hwscan, "Scan work handled by the FW (Default: 1)");
+
+module_param_named(autobcn, ecrnx_mod_params.autobcn, bool, S_IRUGO);
+MODULE_PARM_DESC(autobcn, "Beacon transmission done autonomously by the FW (Default: 1)");
+
+#else
+/* FULLMAC specific parameters*/
+module_param_named(ant_div, ecrnx_mod_params.ant_div, bool, S_IRUGO);
+MODULE_PARM_DESC(ant_div, "Enable Antenna Diversity (Default: 0)");
+#endif /* CONFIG_ECRNX_SOFTMAC */
+
+module_param_named(ht_on, ecrnx_mod_params.ht_on, bool, S_IRUGO);
+MODULE_PARM_DESC(ht_on, "Enable HT (Default: 1)");
+
+module_param_named(vht_on, ecrnx_mod_params.vht_on, bool, S_IRUGO);
+MODULE_PARM_DESC(vht_on, "Enable VHT (Default: 1)");
+
+module_param_named(he_on, ecrnx_mod_params.he_on, bool, S_IRUGO);
+MODULE_PARM_DESC(he_on, "Enable HE (Default: 1)");
+
+module_param_named(mcs_map, ecrnx_mod_params.mcs_map, int, S_IRUGO);
+MODULE_PARM_DESC(mcs_map, "VHT MCS map value 0: MCS0_7, 1: MCS0_8, 2: MCS0_9"
+ " (Default: 2)");
+
+module_param_named(he_mcs_map, ecrnx_mod_params.he_mcs_map, int, S_IRUGO);
+MODULE_PARM_DESC(he_mcs_map, "HE MCS map value 0: MCS0_7, 1: MCS0_9, 2: MCS0_11"
+ " (Default: 2)");
+
+module_param_named(he_ul_on, ecrnx_mod_params.he_ul_on, bool, S_IRUGO);
+MODULE_PARM_DESC(he_ul_on, "Enable HE OFDMA UL (Default: 0)");
+
+module_param_named(amsdu_maxnb, ecrnx_mod_params.amsdu_maxnb, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(amsdu_maxnb, "Maximum number of MSDUs inside an A-MSDU in TX: (Default: NX_TX_PAYLOAD_MAX)");
+
+module_param_named(ps_on, ecrnx_mod_params.ps_on, bool, S_IRUGO);
+MODULE_PARM_DESC(ps_on, "Enable PowerSaving (Default: 1-Enabled)");
+
+module_param_named(tx_lft, ecrnx_mod_params.tx_lft, int, 0644);
+MODULE_PARM_DESC(tx_lft, "Tx lifetime (ms) - setting it to 0 disables retries "
+ "(Default: "__stringify(ECRNX_TX_LIFETIME_MS)")");
+
+module_param_named(ldpc_on, ecrnx_mod_params.ldpc_on, bool, S_IRUGO);
+MODULE_PARM_DESC(ldpc_on, "Enable LDPC (Default: 1)");
+
+module_param_named(stbc_on, ecrnx_mod_params.stbc_on, bool, S_IRUGO);
+MODULE_PARM_DESC(stbc_on, "Enable STBC in RX (Default: 1)");
+
+module_param_named(gf_rx_on, ecrnx_mod_params.gf_rx_on, bool, S_IRUGO);
+MODULE_PARM_DESC(gf_rx_on, "Enable HT greenfield in reception (Default: 1)");
+
+module_param_named(phycfg, ecrnx_mod_params.phy_cfg, int, S_IRUGO);
+MODULE_PARM_DESC(phycfg, "Main RF Path (Default: 0)");
+
+module_param_named(uapsd_timeout, ecrnx_mod_params.uapsd_timeout, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(uapsd_timeout,
+ "UAPSD Timer timeout, in ms (Default: 300). If 0, UAPSD is disabled");
+
+module_param_named(uapsd_queues, ecrnx_mod_params.uapsd_queues, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(uapsd_queues, "UAPSD Queues, integer value, must be seen as a bitfield\n"
+ " Bit 0 = VO\n"
+ " Bit 1 = VI\n"
+ " Bit 2 = BK\n"
+ " Bit 3 = BE\n"
+ " -> uapsd_queues=7 will enable uapsd for VO, VI and BK queues");
+
+module_param_named(ap_uapsd_on, ecrnx_mod_params.ap_uapsd_on, bool, S_IRUGO);
+MODULE_PARM_DESC(ap_uapsd_on, "Enable UAPSD in AP mode (Default: 1)");
+
+module_param_named(sgi, ecrnx_mod_params.sgi, bool, S_IRUGO);
+MODULE_PARM_DESC(sgi, "Advertise Short Guard Interval support (Default: 1)");
+
+module_param_named(sgi80, ecrnx_mod_params.sgi80, bool, S_IRUGO);
+MODULE_PARM_DESC(sgi80, "Advertise Short Guard Interval support for 80MHz (Default: 1)");
+
+module_param_named(use_2040, ecrnx_mod_params.use_2040, bool, S_IRUGO);
+MODULE_PARM_DESC(use_2040, "Enable 40MHz (Default: 1)");
+
+module_param_named(use_80, ecrnx_mod_params.use_80, bool, S_IRUGO);
+MODULE_PARM_DESC(use_80, "Enable 80MHz (Default: 1)");
+
+module_param_named(custregd, ecrnx_mod_params.custregd, bool, S_IRUGO);
+MODULE_PARM_DESC(custregd,
+ "Use permissive custom regulatory rules (for testing ONLY) (Default: 0)");
+
+module_param_named(custchan, ecrnx_mod_params.custchan, bool, S_IRUGO);
+MODULE_PARM_DESC(custchan,
+ "Extend channel set to non-standard channels (for testing ONLY) (Default: 0)");
+
+module_param_named(nss, ecrnx_mod_params.nss, int, S_IRUGO);
+MODULE_PARM_DESC(nss, "1 <= nss <= 2 : Supported number of Spatial Streams (Default: 2)");
+
+module_param_named(amsdu_rx_max, ecrnx_mod_params.amsdu_rx_max, int, S_IRUGO);
+MODULE_PARM_DESC(amsdu_rx_max, "0 <= amsdu_rx_max <= 2 : Maximum A-MSDU size supported in RX\n"
+ " 0: 3895 bytes\n"
+ " 1: 7991 bytes\n"
+ " 2: 11454 bytes\n"
+ " This value might be reduced according to the FW capabilities.\n"
+ " Default: 2");
+
+module_param_named(bfmee, ecrnx_mod_params.bfmee, bool, S_IRUGO);
+MODULE_PARM_DESC(bfmee, "Enable Beamformee Capability (Default: 1-Enabled)");
+
+module_param_named(bfmer, ecrnx_mod_params.bfmer, bool, S_IRUGO);
+MODULE_PARM_DESC(bfmer, "Enable Beamformer Capability (Default: 1-Enabled)");
+
+module_param_named(mesh, ecrnx_mod_params.mesh, bool, S_IRUGO);
+MODULE_PARM_DESC(mesh, "Enable Meshing Capability (Default: 1-Enabled)");
+
+module_param_named(murx, ecrnx_mod_params.murx, bool, S_IRUGO);
+MODULE_PARM_DESC(murx, "Enable MU-MIMO RX Capability (Default: 1-Enabled)");
+
+module_param_named(mutx, ecrnx_mod_params.mutx, bool, S_IRUGO);
+MODULE_PARM_DESC(mutx, "Enable MU-MIMO TX Capability (Default: 1-Enabled)");
+
+module_param_named(mutx_on, ecrnx_mod_params.mutx_on, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(mutx_on, "Enable MU-MIMO transmissions (Default: 1-Enabled)");
+
+module_param_named(roc_dur_max, ecrnx_mod_params.roc_dur_max, int, S_IRUGO);
+MODULE_PARM_DESC(roc_dur_max, "Maximum Remain on Channel duration");
+
+module_param_named(listen_itv, ecrnx_mod_params.listen_itv, int, S_IRUGO);
+MODULE_PARM_DESC(listen_itv, "Maximum listen interval");
+
+module_param_named(listen_bcmc, ecrnx_mod_params.listen_bcmc, bool, S_IRUGO);
+MODULE_PARM_DESC(listen_bcmc, "Wait for BC/MC traffic following DTIM beacon");
+
+module_param_named(lp_clk_ppm, ecrnx_mod_params.lp_clk_ppm, int, S_IRUGO);
+MODULE_PARM_DESC(lp_clk_ppm, "Low Power Clock accuracy of the local device");
+
+module_param_named(tdls, ecrnx_mod_params.tdls, bool, S_IRUGO);
+MODULE_PARM_DESC(tdls, "Enable TDLS (Default: 1-Enabled)");
+
+module_param_named(uf, ecrnx_mod_params.uf, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(uf, "Enable Unsupported HT Frame Logging (Default: 1-Enabled)");
+
+module_param_named(ftl, ecrnx_mod_params.ftl, charp, S_IRUGO);
+MODULE_PARM_DESC(ftl, "Firmware trace level (Default: \"\")");
+
+module_param_named(dpsm, ecrnx_mod_params.dpsm, bool, S_IRUGO);
+MODULE_PARM_DESC(dpsm, "Enable Dynamic PowerSaving (Default: 1-Enabled)");
+module_param_named(tx_to_bk, ecrnx_mod_params.tx_to_bk, int, S_IRUGO);
+MODULE_PARM_DESC(tx_to_bk,
+ "TX timeout for BK, in ms (Default: 0, Max: 65535). If 0, default value is applied");
+module_param_named(tx_to_be, ecrnx_mod_params.tx_to_be, int, S_IRUGO);
+MODULE_PARM_DESC(tx_to_be,
+ "TX timeout for BE, in ms (Default: 0, Max: 65535). If 0, default value is applied");
+module_param_named(tx_to_vi, ecrnx_mod_params.tx_to_vi, int, S_IRUGO);
+MODULE_PARM_DESC(tx_to_vi,
+ "TX timeout for VI, in ms (Default: 0, Max: 65535). If 0, default value is applied");
+module_param_named(tx_to_vo, ecrnx_mod_params.tx_to_vo, int, S_IRUGO);
+MODULE_PARM_DESC(tx_to_vo,
+ "TX timeout for VO, in ms (Default: 0, Max: 65535). If 0, default value is applied");
+
+/* Regulatory rules */
+static struct ieee80211_regdomain ecrnx_regdom = {
+ .n_reg_rules = 2,
+ .alpha2 = "99",
+ .reg_rules = {
+ REG_RULE(2390 - 10, 2510 + 10, 40, 0, 1000, 0),
+ REG_RULE(5150 - 10, 5970 + 10, 80, 0, 1000, 0),
+ }
+};
+
+static const int mcs_map_to_rate[4][3] = {
+ [PHY_CHNL_BW_20][IEEE80211_VHT_MCS_SUPPORT_0_7] = 65,
+ [PHY_CHNL_BW_20][IEEE80211_VHT_MCS_SUPPORT_0_8] = 78,
+ [PHY_CHNL_BW_20][IEEE80211_VHT_MCS_SUPPORT_0_9] = 78,
+ [PHY_CHNL_BW_40][IEEE80211_VHT_MCS_SUPPORT_0_7] = 135,
+ [PHY_CHNL_BW_40][IEEE80211_VHT_MCS_SUPPORT_0_8] = 162,
+ [PHY_CHNL_BW_40][IEEE80211_VHT_MCS_SUPPORT_0_9] = 180,
+ [PHY_CHNL_BW_80][IEEE80211_VHT_MCS_SUPPORT_0_7] = 292,
+ [PHY_CHNL_BW_80][IEEE80211_VHT_MCS_SUPPORT_0_8] = 351,
+ [PHY_CHNL_BW_80][IEEE80211_VHT_MCS_SUPPORT_0_9] = 390,
+ [PHY_CHNL_BW_160][IEEE80211_VHT_MCS_SUPPORT_0_7] = 585,
+ [PHY_CHNL_BW_160][IEEE80211_VHT_MCS_SUPPORT_0_8] = 702,
+ [PHY_CHNL_BW_160][IEEE80211_VHT_MCS_SUPPORT_0_9] = 780,
+};
+
+#define MAX_VHT_RATE(map, nss, bw) (mcs_map_to_rate[bw][map] * (nss))
+
+#if defined(CONFIG_ECRNX_HE)
+struct ieee80211_sta_he_cap ecrnx_he_cap;
+#endif
+
+/**
+ * Do some sanity check
+ *
+ */
+static int ecrnx_check_fw_hw_feature(struct ecrnx_hw *ecrnx_hw,
+ struct wiphy *wiphy)
+{
+ u32_l sys_feat = ecrnx_hw->version_cfm.features;
+ u32_l mac_feat = ecrnx_hw->version_cfm.version_machw_1;
+ u32_l phy_feat = ecrnx_hw->version_cfm.version_phy_1;
+ u32_l phy_vers = ecrnx_hw->version_cfm.version_phy_2;
+ u16_l max_sta_nb = ecrnx_hw->version_cfm.max_sta_nb;
+ u8_l max_vif_nb = ecrnx_hw->version_cfm.max_vif_nb;
+ int bw, res = 0;
+ int amsdu_rx;
+
+ if (!ecrnx_hw->mod_params->custregd)
+ ecrnx_hw->mod_params->custchan = false;
+
+ if (ecrnx_hw->mod_params->custchan) {
+#ifdef CONFIG_ECRNX_SOFTMAC
+ ecrnx_hw->mod_params->autobcn = false;
+ ecrnx_hw->mod_params->hwscan = false;
+ sys_feat &= ~BIT(MM_FEAT_CMON_BIT);
+#endif
+ ecrnx_hw->mod_params->mesh = false;
+ ecrnx_hw->mod_params->tdls = false;
+ }
+
+#ifdef CONFIG_ECRNX_SOFTMAC
+ if (sys_feat & BIT(MM_FEAT_UMAC_BIT)) {
+ wiphy_err(wiphy, "Loading fullmac firmware with softmac driver\n");
+ res = -1;
+ }
+
+ if (sys_feat & BIT(MM_FEAT_AUTOBCN_BIT) &&
+ !ecrnx_hw->mod_params->autobcn) {
+ wiphy_err(wiphy,
+ "Auto beacon enabled in firmware but disabled in driver\n");
+ res = -1;
+ }
+
+ if (!!(sys_feat & BIT(MM_FEAT_HWSCAN_BIT)) !=
+ !!ecrnx_hw->mod_params->hwscan) {
+ wiphy_err(wiphy,
+ "hwscan %sabled in firmware but %sabled in driver\n",
+ (sys_feat & BIT(MM_FEAT_HWSCAN_BIT)) ? "en" : "dis",
+ ecrnx_hw->mod_params->hwscan ? "en" : "dis");
+ res = -1;
+ }
+
+ if (sys_feat & BIT(MM_FEAT_CMON_BIT)) {
+ ieee80211_hw_set(ecrnx_hw->hw, CONNECTION_MONITOR);
+ }
+
+ /* AMPDU (non)support implies different shared structure definition
+ so insure that fw and drv have consistent compilation option */
+ if (sys_feat & BIT(MM_FEAT_AMPDU_BIT)) {
+#ifndef CONFIG_ECRNX_AGG_TX
+ wiphy_err(wiphy,
+ "AMPDU enabled in firmware but support not compiled in driver\n");
+ res = -1;
+#endif /* CONFIG_ECRNX_AGG_TX */
+ } else {
+#ifdef CONFIG_ECRNX_AGG_TX
+ wiphy_err(wiphy,
+ "AMPDU disabled in firmware but support compiled in driver\n");
+ res = -1;
+#else
+ ecrnx_hw->mod_params->agg_tx = false;
+#endif /* CONFIG_ECRNX_AGG_TX */
+ }
+
+ if (!(sys_feat & BIT(MM_FEAT_DPSM_BIT))) {
+ ecrnx_hw->mod_params->dpsm = false;
+ }
+
+#else /* check for FULLMAC */
+
+ if (!(sys_feat & BIT(MM_FEAT_UMAC_BIT))) {
+ wiphy_err(wiphy,
+ "Loading softmac firmware with fullmac driver\n");
+ res = -1;
+ }
+
+ if (!(sys_feat & BIT(MM_FEAT_ANT_DIV_BIT))) {
+ ecrnx_hw->mod_params->ant_div = false;
+ }
+
+#endif /* CONFIG_ECRNX_SOFTMAC */
+
+ if (!(sys_feat & BIT(MM_FEAT_VHT_BIT))) {
+ ecrnx_hw->mod_params->vht_on = false;
+ }
+
+ // Check if HE is supported
+ if (!(sys_feat & BIT(MM_FEAT_HE_BIT))) {
+ ecrnx_hw->mod_params->he_on = false;
+ ecrnx_hw->mod_params->he_ul_on = false;
+ }
+
+ if (!(sys_feat & BIT(MM_FEAT_PS_BIT))) {
+ ecrnx_hw->mod_params->ps_on = false;
+ }
+
+ /* AMSDU (non)support implies different shared structure definition
+ so insure that fw and drv have consistent compilation option */
+ if (sys_feat & BIT(MM_FEAT_AMSDU_BIT)) {
+#ifndef CONFIG_ECRNX_SPLIT_TX_BUF
+ wiphy_err(wiphy,
+ "AMSDU enabled in firmware but support not compiled in driver\n");
+ res = -1;
+#else
+ /* Adjust amsdu_maxnb so that it stays in allowed bounds */
+ ecrnx_adjust_amsdu_maxnb(ecrnx_hw);
+#endif /* CONFIG_ECRNX_SPLIT_TX_BUF */
+ } else {
+#ifdef CONFIG_ECRNX_SPLIT_TX_BUF
+ wiphy_err(wiphy,
+ "AMSDU disabled in firmware but support compiled in driver\n");
+ res = -1;
+#endif /* CONFIG_ECRNX_SPLIT_TX_BUF */
+ }
+
+ if (!(sys_feat & BIT(MM_FEAT_UAPSD_BIT))) {
+ ecrnx_hw->mod_params->uapsd_timeout = 0;
+ }
+
+ if (!(sys_feat & BIT(MM_FEAT_BFMEE_BIT))) {
+ ecrnx_hw->mod_params->bfmee = false;
+ }
+
+ if ((sys_feat & BIT(MM_FEAT_BFMER_BIT))) {
+#ifndef CONFIG_ECRNX_BFMER
+ wiphy_err(wiphy,
+ "BFMER enabled in firmware but support not compiled in driver\n");
+ res = -1;
+#endif /* CONFIG_ECRNX_BFMER */
+ // Check PHY and MAC HW BFMER support and update parameter accordingly
+ if (!(phy_feat & MDM_BFMER_BIT) || !(mac_feat & NXMAC_BFMER_BIT)) {
+ ecrnx_hw->mod_params->bfmer = false;
+ // Disable the feature in the bitfield so that it won't be displayed
+ sys_feat &= ~BIT(MM_FEAT_BFMER_BIT);
+ }
+ } else {
+#ifdef CONFIG_ECRNX_BFMER
+ wiphy_err(wiphy,
+ "BFMER disabled in firmware but support compiled in driver\n");
+ res = -1;
+#else
+ ecrnx_hw->mod_params->bfmer = false;
+#endif /* CONFIG_ECRNX_BFMER */
+ }
+
+ if (!(sys_feat & BIT(MM_FEAT_MESH_BIT))) {
+ ecrnx_hw->mod_params->mesh = false;
+ }
+
+ if (!(sys_feat & BIT(MM_FEAT_TDLS_BIT))) {
+ ecrnx_hw->mod_params->tdls = false;
+ }
+
+ if (!(sys_feat & BIT(MM_FEAT_UF_BIT))) {
+ ecrnx_hw->mod_params->uf = false;
+ }
+
+#ifdef CONFIG_ECRNX_FULLMAC
+ if ((sys_feat & BIT(MM_FEAT_MON_DATA_BIT))) {
+#ifndef CONFIG_ECRNX_MON_DATA
+ wiphy_err(wiphy,
+ "Monitor+Data interface support (MON_DATA) is enabled in firmware but support not compiled in driver\n");
+ res = -1;
+#endif /* CONFIG_ECRNX_MON_DATA */
+ } else {
+#ifdef CONFIG_ECRNX_MON_DATA
+ wiphy_err(wiphy,
+ "Monitor+Data interface support (MON_DATA) disabled in firmware but support compiled in driver\n");
+ res = -1;
+#endif /* CONFIG_ECRNX_MON_DATA */
+ }
+#endif
+
+ // Check supported AMSDU RX size
+ amsdu_rx = (sys_feat >> MM_AMSDU_MAX_SIZE_BIT0) & 0x03;
+ if (amsdu_rx < ecrnx_hw->mod_params->amsdu_rx_max) {
+ ecrnx_hw->mod_params->amsdu_rx_max = amsdu_rx;
+ }
+
+ // Check supported BW
+ bw = (phy_feat & MDM_CHBW_MASK) >> MDM_CHBW_LSB;
+ // Check if 80MHz BW is supported
+ if (bw < 2) {
+ ecrnx_hw->mod_params->use_80 = false;
+ }
+ // Check if 40MHz BW is supported
+ if (bw < 1)
+ ecrnx_hw->mod_params->use_2040 = false;
+
+ // 80MHz BW shall be disabled if 40MHz is not enabled
+ if (!ecrnx_hw->mod_params->use_2040)
+ ecrnx_hw->mod_params->use_80 = false;
+
+ // Check if HT is supposed to be supported. If not, disable VHT/HE too
+ if (!ecrnx_hw->mod_params->ht_on)
+ {
+ ecrnx_hw->mod_params->vht_on = false;
+ ecrnx_hw->mod_params->he_on = false;
+ ecrnx_hw->mod_params->he_ul_on = false;
+ ecrnx_hw->mod_params->use_80 = false;
+ ecrnx_hw->mod_params->use_2040 = false;
+ }
+
+ // LDPC is mandatory for HE40 and above, so if LDPC is not supported, then disable
+ // HE to use HT/VHT only
+ if (ecrnx_hw->mod_params->he_on && !ecrnx_hw->mod_params->ldpc_on)
+ {
+ ecrnx_hw->mod_params->use_80 = false;
+ /* ESWIN turns off 40M automotically when it is HE mode */
+ //ecrnx_hw->mod_params->use_2040 = false;
+
+ }
+
+ // HT greenfield is not supported in modem >= 3.0
+ if (__MDM_MAJOR_VERSION(phy_vers) > 0) {
+#ifdef CONFIG_ECRNX_SOFTMAC
+ ecrnx_hw->mod_params->gf_on = false;
+#endif
+ ecrnx_hw->mod_params->gf_rx_on = false;
+ }
+
+ if (!(sys_feat & BIT(MM_FEAT_MU_MIMO_RX_BIT)) ||
+ !ecrnx_hw->mod_params->bfmee) {
+ ecrnx_hw->mod_params->murx = false;
+ }
+
+ if ((sys_feat & BIT(MM_FEAT_MU_MIMO_TX_BIT))) {
+#ifndef CONFIG_ECRNX_MUMIMO_TX
+ wiphy_err(wiphy,
+ "MU-MIMO TX enabled in firmware but support not compiled in driver\n");
+ res = -1;
+#endif /* CONFIG_ECRNX_MUMIMO_TX */
+ if (!ecrnx_hw->mod_params->bfmer)
+ ecrnx_hw->mod_params->mutx = false;
+ // Check PHY and MAC HW MU-MIMO TX support and update parameter accordingly
+ else if (!(phy_feat & MDM_MUMIMOTX_BIT) || !(mac_feat & NXMAC_MU_MIMO_TX_BIT)) {
+ ecrnx_hw->mod_params->mutx = false;
+ // Disable the feature in the bitfield so that it won't be displayed
+ sys_feat &= ~BIT(MM_FEAT_MU_MIMO_TX_BIT);
+ }
+ } else {
+#ifdef CONFIG_ECRNX_MUMIMO_TX
+ wiphy_err(wiphy,
+ "MU-MIMO TX disabled in firmware but support compiled in driver\n");
+ res = -1;
+#else
+ ecrnx_hw->mod_params->mutx = false;
+#endif /* CONFIG_ECRNX_MUMIMO_TX */
+ }
+
+ if (sys_feat & BIT(MM_FEAT_WAPI_BIT)) {
+ ecrnx_enable_wapi(ecrnx_hw);
+ }
+
+#ifdef CONFIG_ECRNX_FULLMAC
+ if (sys_feat & BIT(MM_FEAT_MFP_BIT)) {
+ ecrnx_enable_mfp(ecrnx_hw);
+ }
+ if (mac_feat & NXMAC_GCMP_BIT) {
+ ecrnx_enable_gcmp(ecrnx_hw);
+ }
+#endif
+
+#ifdef CONFIG_ECRNX_SOFTMAC
+#define QUEUE_NAME "BEACON queue "
+#else
+#define QUEUE_NAME "Broadcast/Multicast queue "
+#endif /* CONFIG_ECRNX_SOFTMAC */
+
+ if (sys_feat & BIT(MM_FEAT_BCN_BIT)) {
+#if NX_TXQ_CNT == 4
+ wiphy_err(wiphy, QUEUE_NAME
+ "enabled in firmware but support not compiled in driver\n");
+ res = -1;
+#endif /* NX_TXQ_CNT == 4 */
+ } else {
+#if NX_TXQ_CNT == 5
+ wiphy_err(wiphy, QUEUE_NAME
+ "disabled in firmware but support compiled in driver\n");
+ res = -1;
+#endif /* NX_TXQ_CNT == 5 */
+ }
+#undef QUEUE_NAME
+
+#ifdef CONFIG_ECRNX_RADAR
+ if (sys_feat & BIT(MM_FEAT_RADAR_BIT)) {
+ /* Enable combination with radar detection */
+ wiphy->n_iface_combinations++;
+ }
+#endif /* CONFIG_ECRNX_RADAR */
+
+#ifndef CONFIG_ECRNX_SDM
+ switch (__MDM_PHYCFG_FROM_VERS(phy_feat)) {
+ case MDM_PHY_CONFIG_TRIDENT:
+ ecrnx_hw->mod_params->nss = 1;
+ if ((ecrnx_hw->mod_params->phy_cfg < 0) || (ecrnx_hw->mod_params->phy_cfg > 2))
+ ecrnx_hw->mod_params->phy_cfg = 2;
+ break;
+ case MDM_PHY_CONFIG_KARST:
+ case MDM_PHY_CONFIG_CATAXIA:
+ {
+ int nss_supp = (phy_feat & MDM_NSS_MASK) >> MDM_NSS_LSB;
+ if (ecrnx_hw->mod_params->nss > nss_supp)
+ ecrnx_hw->mod_params->nss = nss_supp;
+ if ((ecrnx_hw->mod_params->phy_cfg < 0) || (ecrnx_hw->mod_params->phy_cfg > 1))
+ ecrnx_hw->mod_params->phy_cfg = 0;
+ }
+ break;
+ default:
+ WARN_ON(1);
+ break;
+ }
+#endif /* CONFIG_ECRNX_SDM */
+
+ if ((ecrnx_hw->mod_params->nss < 1) || (ecrnx_hw->mod_params->nss > 2))
+ ecrnx_hw->mod_params->nss = 1;
+
+
+ if ((ecrnx_hw->mod_params->mcs_map < 0) || (ecrnx_hw->mod_params->mcs_map > 2))
+ ecrnx_hw->mod_params->mcs_map = 0;
+
+#define PRINT_ECRNX_PHY_FEAT(feat) \
+ (phy_feat & MDM_##feat##_BIT ? "["#feat"]" : "")
+ wiphy_info(wiphy, "PHY features: [NSS=%d][CHBW=%d]%s%s%s%s%s%s%s\n",
+ (phy_feat & MDM_NSS_MASK) >> MDM_NSS_LSB,
+ 20 * (1 << ((phy_feat & MDM_CHBW_MASK) >> MDM_CHBW_LSB)),
+ (phy_feat & (MDM_LDPCDEC_BIT | MDM_LDPCENC_BIT)) ==
+ (MDM_LDPCDEC_BIT | MDM_LDPCENC_BIT) ? "[LDPC]" : "",
+ PRINT_ECRNX_PHY_FEAT(VHT),
+ PRINT_ECRNX_PHY_FEAT(HE),
+ PRINT_ECRNX_PHY_FEAT(BFMER),
+ PRINT_ECRNX_PHY_FEAT(BFMEE),
+ PRINT_ECRNX_PHY_FEAT(MUMIMOTX),
+ PRINT_ECRNX_PHY_FEAT(MUMIMORX)
+ );
+
+#define PRINT_ECRNX_FEAT(feat) \
+ (sys_feat & BIT(MM_FEAT_##feat##_BIT) ? "["#feat"]" : "")
+
+ wiphy_info(wiphy, "FW features: %s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n",
+ PRINT_ECRNX_FEAT(BCN),
+ PRINT_ECRNX_FEAT(AUTOBCN),
+ PRINT_ECRNX_FEAT(HWSCAN),
+ PRINT_ECRNX_FEAT(CMON),
+ PRINT_ECRNX_FEAT(MROLE),
+ PRINT_ECRNX_FEAT(RADAR),
+ PRINT_ECRNX_FEAT(PS),
+ PRINT_ECRNX_FEAT(UAPSD),
+ PRINT_ECRNX_FEAT(DPSM),
+ PRINT_ECRNX_FEAT(AMPDU),
+ PRINT_ECRNX_FEAT(AMSDU),
+ PRINT_ECRNX_FEAT(CHNL_CTXT),
+ PRINT_ECRNX_FEAT(REORD),
+ PRINT_ECRNX_FEAT(P2P),
+ PRINT_ECRNX_FEAT(P2P_GO),
+ PRINT_ECRNX_FEAT(UMAC),
+ PRINT_ECRNX_FEAT(VHT),
+ PRINT_ECRNX_FEAT(HE),
+ PRINT_ECRNX_FEAT(BFMEE),
+ PRINT_ECRNX_FEAT(BFMER),
+ PRINT_ECRNX_FEAT(WAPI),
+ PRINT_ECRNX_FEAT(MFP),
+ PRINT_ECRNX_FEAT(MU_MIMO_RX),
+ PRINT_ECRNX_FEAT(MU_MIMO_TX),
+ PRINT_ECRNX_FEAT(MESH),
+ PRINT_ECRNX_FEAT(TDLS),
+ PRINT_ECRNX_FEAT(ANT_DIV),
+ PRINT_ECRNX_FEAT(UF),
+ PRINT_ECRNX_FEAT(TWT));
+#undef PRINT_ECRNX_FEAT
+
+ if(max_sta_nb != NX_REMOTE_STA_MAX)
+ {
+ wiphy_err(wiphy, "Different number of supported stations between driver and FW (%d != %d)\n",
+ NX_REMOTE_STA_MAX, max_sta_nb);
+ res = -1;
+ }
+
+ if(max_vif_nb != NX_VIRT_DEV_MAX)
+ {
+ wiphy_err(wiphy, "Different number of supported virtual interfaces between driver and FW (%d != %d)\n",
+ NX_VIRT_DEV_MAX, max_vif_nb);
+ res = -1;
+ }
+
+ return res;
+}
+
+static void ecrnx_set_ppe_threshold(struct ecrnx_hw *ecrnx_hw,
+ struct ieee80211_sta_he_cap *he_cap)
+{
+ const u8_l PPE_THRES_INFO_OFT = 7;
+ const u8_l PPE_THRES_INFO_BIT_LEN = 6;
+ struct ppe_thres_info_tag
+ {
+ u8_l ppet16 : 3;
+ u8_l ppet8 : 3;
+ }__packed;
+ struct ppe_thres_field_tag
+ {
+ u8_l nsts : 3;
+ u8_l ru_idx_bmp : 4;
+ };
+ int nss = ecrnx_hw->mod_params->nss;
+ struct ppe_thres_field_tag* ppe_thres_field = (struct ppe_thres_field_tag*) he_cap->ppe_thres;
+ struct ppe_thres_info_tag ppe_thres_info = {.ppet16 = 0, //BSPK
+ .ppet8 = 7 //None
+ };
+ u8_l* ppe_thres_info_ptr = (u8_l*) &ppe_thres_info;
+ u16_l* ppe_thres_ptr = (u16_l*) he_cap->ppe_thres;
+ u8_l i, j, cnt, offset;
+ if (ecrnx_hw->mod_params->use_80)
+ {
+ ppe_thres_field->ru_idx_bmp = 7;
+ cnt = 3;
+ }
+ else
+ {
+ ppe_thres_field->ru_idx_bmp = 1;
+ cnt = 1;
+ }
+ ppe_thres_field->nsts = nss - 1;
+ for (i = 0; i < nss ; i++)
+ {
+ for (j = 0; j < cnt; j++){
+ offset = (i * cnt + j) * PPE_THRES_INFO_BIT_LEN + PPE_THRES_INFO_OFT;
+ ppe_thres_ptr = (u16_l*)&he_cap->ppe_thres[offset / 8];
+ *ppe_thres_ptr |= *ppe_thres_info_ptr << (offset % 8);
+ }
+ }
+}
+
+
+#ifdef CONFIG_ECRNX_SOFTMAC
+static void ecrnx_set_softmac_flags(struct ecrnx_hw *ecrnx_hw)
+{
+ struct ieee80211_hw *hw = ecrnx_hw->hw;
+ int nss;
+#ifdef CONFIG_MAC80211_AMSDUS_TX
+ ieee80211_hw_set(hw, TX_AMSDU);
+ ieee80211_hw_set(hw, TX_FRAG_LIST);
+ hw->max_tx_fragments = ecrnx_hw->mod_params->amsdu_maxnb;
+#endif
+
+ if (!ecrnx_hw->mod_params->autobcn)
+ ieee80211_hw_set(hw, HOST_BROADCAST_PS_BUFFERING);
+
+ if (ecrnx_hw->mod_params->agg_tx)
+ ieee80211_hw_set(hw, AMPDU_AGGREGATION);
+
+ if (ecrnx_hw->mod_params->cmon)
+ ieee80211_hw_set(hw, CONNECTION_MONITOR);
+
+ if (ecrnx_hw->mod_params->hwscan)
+ ieee80211_hw_set(hw, CHANCTX_STA_CSA);
+
+ if (ecrnx_hw->mod_params->ps_on) {
+ ieee80211_hw_set(hw, SUPPORTS_PS);
+ }
+ /* To disable the dynamic PS we say to the stack that we support it in
+ * HW. This will force mac80211 rely on us to handle this. */
+ ieee80211_hw_set(hw, SUPPORTS_DYNAMIC_PS);
+
+ if (ecrnx_hw->mod_params->mfp_on)
+ ieee80211_hw_set(hw, MFP_CAPABLE);
+
+ nss = ecrnx_hw->mod_params->nss;
+ ecrnx_hw->phy.ctrlinfo_1.value = 0;
+ ecrnx_hw->phy.ctrlinfo_2.value = 0;
+ if (nss == 1) {
+ ecrnx_hw->phy.ctrlinfo_2.antennaSet = 1;
+ } else {
+ ecrnx_hw->phy.ctrlinfo_1.fecCoding = 0;
+ ecrnx_hw->phy.ctrlinfo_1.nTx = 1;
+ ecrnx_hw->phy.ctrlinfo_2.antennaSet = 3;
+ ecrnx_hw->phy.ctrlinfo_2.smmIndex = 1;
+ }
+ ecrnx_hw->phy.stbc_nss = nss >> 1;
+}
+#endif
+
+#ifdef CONFIG_ECRNX_5G
+static void ecrnx_set_vht_capa(struct ecrnx_hw *ecrnx_hw, struct wiphy *wiphy)
+{
+ struct ieee80211_supported_band *band_5GHz = wiphy->bands[NL80211_BAND_5GHZ];
+ int i;
+ int nss = ecrnx_hw->mod_params->nss;
+ int mcs_map;
+ int mcs_map_max;
+ int mcs_map_max_2ss_rx = IEEE80211_VHT_MCS_SUPPORT_0_9;
+ int mcs_map_max_2ss_tx = IEEE80211_VHT_MCS_SUPPORT_0_9;
+ int bw_max;
+
+ if (!ecrnx_hw->mod_params->vht_on)
+ return;
+
+ band_5GHz->vht_cap.vht_supported = true;
+ if (ecrnx_hw->mod_params->sgi80)
+ band_5GHz->vht_cap.cap |= IEEE80211_VHT_CAP_SHORT_GI_80;
+ if (ecrnx_hw->mod_params->stbc_on)
+ band_5GHz->vht_cap.cap |= IEEE80211_VHT_CAP_RXSTBC_1;
+ if (ecrnx_hw->mod_params->ldpc_on)
+ band_5GHz->vht_cap.cap |= IEEE80211_VHT_CAP_RXLDPC;
+ if (ecrnx_hw->mod_params->bfmee) {
+ band_5GHz->vht_cap.cap |= IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE;
+ band_5GHz->vht_cap.cap |= 3 << IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT;
+ }
+ if (nss > 1)
+ band_5GHz->vht_cap.cap |= IEEE80211_VHT_CAP_TXSTBC;
+
+ // Update the AMSDU max RX size (not shifted as located at offset 0 of the VHT cap)
+ band_5GHz->vht_cap.cap |= ecrnx_hw->mod_params->amsdu_rx_max;
+
+ if (ecrnx_hw->mod_params->bfmer) {
+ band_5GHz->vht_cap.cap |= IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE;
+ /* Set number of sounding dimensions */
+ band_5GHz->vht_cap.cap |= (nss - 1) << IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_SHIFT;
+ }
+ if (ecrnx_hw->mod_params->murx)
+ band_5GHz->vht_cap.cap |= IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE;
+ if (ecrnx_hw->mod_params->mutx)
+ band_5GHz->vht_cap.cap |= IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE;
+
+ /*
+ * MCS map:
+ * This capabilities are filled according to the mcs_map module parameter.
+ * However currently we have some limitations due to FPGA clock constraints
+ * that prevent always using the range of MCS that is defined by the
+ * parameter:
+ * - in RX, 2SS, we support up to MCS7
+ * - in TX, 2SS, we support up to MCS8
+ */
+ // Get max supported BW
+ if (ecrnx_hw->mod_params->use_80) {
+ bw_max = PHY_CHNL_BW_80;
+ mcs_map_max_2ss_rx = IEEE80211_VHT_MCS_SUPPORT_0_7;
+ mcs_map_max_2ss_tx = IEEE80211_VHT_MCS_SUPPORT_0_8;
+ } else if (ecrnx_hw->mod_params->use_2040)
+ bw_max = PHY_CHNL_BW_40;
+ else
+ bw_max = PHY_CHNL_BW_20;
+
+ // Check if MCS map should be limited to MCS0_8 due to the standard. Indeed in BW20,
+ // MCS9 is not supported in 1 and 2 SS
+ if (ecrnx_hw->mod_params->use_2040)
+ mcs_map_max = IEEE80211_VHT_MCS_SUPPORT_0_9;
+ else
+ mcs_map_max = IEEE80211_VHT_MCS_SUPPORT_0_8;
+
+ mcs_map = min_t(int, ecrnx_hw->mod_params->mcs_map, mcs_map_max);
+ band_5GHz->vht_cap.vht_mcs.rx_mcs_map = cpu_to_le16(0);
+ for (i = 0; i < nss; i++) {
+ band_5GHz->vht_cap.vht_mcs.rx_mcs_map |= cpu_to_le16(mcs_map << (i*2));
+ band_5GHz->vht_cap.vht_mcs.rx_highest = MAX_VHT_RATE(mcs_map, nss, bw_max);
+ mcs_map = min_t(int, mcs_map, mcs_map_max_2ss_rx);
+ }
+ for (; i < 8; i++) {
+ band_5GHz->vht_cap.vht_mcs.rx_mcs_map |= cpu_to_le16(
+ IEEE80211_VHT_MCS_NOT_SUPPORTED << (i*2));
+ }
+
+ mcs_map = min_t(int, ecrnx_hw->mod_params->mcs_map, mcs_map_max);
+ band_5GHz->vht_cap.vht_mcs.tx_mcs_map = cpu_to_le16(0);
+ for (i = 0; i < nss; i++) {
+ band_5GHz->vht_cap.vht_mcs.tx_mcs_map |= cpu_to_le16(mcs_map << (i*2));
+ band_5GHz->vht_cap.vht_mcs.tx_highest = MAX_VHT_RATE(mcs_map, nss, bw_max);
+ mcs_map = min_t(int, mcs_map, mcs_map_max_2ss_tx);
+ }
+ for (; i < 8; i++) {
+ band_5GHz->vht_cap.vht_mcs.tx_mcs_map |= cpu_to_le16(
+ IEEE80211_VHT_MCS_NOT_SUPPORTED << (i*2));
+ }
+
+ if (!ecrnx_hw->mod_params->use_80) {
+#ifdef CONFIG_VENDOR_ECRNX_VHT_NO80
+ band_5GHz->vht_cap.cap |= IEEE80211_VHT_CAP_NOT_SUP_WIDTH_80;
+#endif
+ band_5GHz->vht_cap.cap &= ~IEEE80211_VHT_CAP_SHORT_GI_80;
+ }
+}
+#else
+static void ecrnx_set_vht_capa(struct ecrnx_hw *ecrnx_hw, struct wiphy *wiphy)
+{
+ struct ieee80211_supported_band *band_2GHz = wiphy->bands[NL80211_BAND_2GHZ];
+ int i;
+ int nss = ecrnx_hw->mod_params->nss;
+ int mcs_map;
+ int mcs_map_max;
+ int mcs_map_max_2ss_rx = IEEE80211_VHT_MCS_SUPPORT_0_9;
+ int mcs_map_max_2ss_tx = IEEE80211_VHT_MCS_SUPPORT_0_9;
+ int bw_max;
+
+ if (!ecrnx_hw->mod_params->vht_on)
+ return;
+
+ band_2GHz->vht_cap.vht_supported = true;
+ if (ecrnx_hw->mod_params->sgi80)
+ band_2GHz->vht_cap.cap |= IEEE80211_VHT_CAP_SHORT_GI_80;
+ if (ecrnx_hw->mod_params->stbc_on)
+ band_2GHz->vht_cap.cap |= IEEE80211_VHT_CAP_RXSTBC_1;
+ if (ecrnx_hw->mod_params->ldpc_on)
+ band_2GHz->vht_cap.cap |= IEEE80211_VHT_CAP_RXLDPC;
+ if (ecrnx_hw->mod_params->bfmee) {
+ band_2GHz->vht_cap.cap |= IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
+ band_2GHz->vht_cap.cap |= 3 << IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT;
+#endif
+ }
+ if (nss > 1)
+ band_2GHz->vht_cap.cap |= IEEE80211_VHT_CAP_TXSTBC;
+
+ // Update the AMSDU max RX size (not shifted as located at offset 0 of the VHT cap)
+ band_2GHz->vht_cap.cap |= ecrnx_hw->mod_params->amsdu_rx_max;
+
+ if (ecrnx_hw->mod_params->bfmer) {
+ band_2GHz->vht_cap.cap |= IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE;
+ /* Set number of sounding dimensions */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
+ band_2GHz->vht_cap.cap |= (nss - 1) << IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_SHIFT;
+#endif
+ }
+ if (ecrnx_hw->mod_params->murx)
+ band_2GHz->vht_cap.cap |= IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE;
+ if (ecrnx_hw->mod_params->mutx)
+ band_2GHz->vht_cap.cap |= IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE;
+
+ /*
+ * MCS map:
+ * This capabilities are filled according to the mcs_map module parameter.
+ * However currently we have some limitations due to FPGA clock constraints
+ * that prevent always using the range of MCS that is defined by the
+ * parameter:
+ * - in RX, 2SS, we support up to MCS7
+ * - in TX, 2SS, we support up to MCS8
+ */
+ // Get max supported BW
+ if (ecrnx_hw->mod_params->use_80) {
+ bw_max = PHY_CHNL_BW_80;
+ mcs_map_max_2ss_rx = IEEE80211_VHT_MCS_SUPPORT_0_7;
+ mcs_map_max_2ss_tx = IEEE80211_VHT_MCS_SUPPORT_0_8;
+ } else if (ecrnx_hw->mod_params->use_2040)
+ bw_max = PHY_CHNL_BW_40;
+ else
+ bw_max = PHY_CHNL_BW_20;
+
+ // Check if MCS map should be limited to MCS0_8 due to the standard. Indeed in BW20,
+ // MCS9 is not supported in 1 and 2 SS
+ if (ecrnx_hw->mod_params->use_2040)
+ mcs_map_max = IEEE80211_VHT_MCS_SUPPORT_0_9;
+ else
+ mcs_map_max = IEEE80211_VHT_MCS_SUPPORT_0_8;
+
+ mcs_map = min_t(int, ecrnx_hw->mod_params->mcs_map, mcs_map_max);
+ band_2GHz->vht_cap.vht_mcs.rx_mcs_map = cpu_to_le16(0);
+ for (i = 0; i < nss; i++) {
+ band_2GHz->vht_cap.vht_mcs.rx_mcs_map |= cpu_to_le16(mcs_map << (i*2));
+ band_2GHz->vht_cap.vht_mcs.rx_highest = MAX_VHT_RATE(mcs_map, nss, bw_max);
+ mcs_map = min_t(int, mcs_map, mcs_map_max_2ss_rx);
+ }
+ for (; i < 8; i++) {
+ band_2GHz->vht_cap.vht_mcs.rx_mcs_map |= cpu_to_le16(
+ IEEE80211_VHT_MCS_NOT_SUPPORTED << (i*2));
+ }
+
+ mcs_map = min_t(int, ecrnx_hw->mod_params->mcs_map, mcs_map_max);
+ band_2GHz->vht_cap.vht_mcs.tx_mcs_map = cpu_to_le16(0);
+ for (i = 0; i < nss; i++) {
+ band_2GHz->vht_cap.vht_mcs.tx_mcs_map |= cpu_to_le16(mcs_map << (i*2));
+ band_2GHz->vht_cap.vht_mcs.tx_highest = MAX_VHT_RATE(mcs_map, nss, bw_max);
+ mcs_map = min_t(int, mcs_map, mcs_map_max_2ss_tx);
+ }
+ for (; i < 8; i++) {
+ band_2GHz->vht_cap.vht_mcs.tx_mcs_map |= cpu_to_le16(
+ IEEE80211_VHT_MCS_NOT_SUPPORTED << (i*2));
+ }
+
+ if (!ecrnx_hw->mod_params->use_80) {
+#ifdef CONFIG_VENDOR_ECRNX_VHT_NO80
+ band_2GHz->vht_cap.cap |= IEEE80211_VHT_CAP_NOT_SUP_WIDTH_80;
+#endif
+ band_2GHz->vht_cap.cap &= ~IEEE80211_VHT_CAP_SHORT_GI_80;
+ }
+}
+#endif
+
+
+static void ecrnx_set_ht_capa(struct ecrnx_hw *ecrnx_hw, struct wiphy *wiphy)
+{
+#ifdef CONFIG_ECRNX_5G
+ struct ieee80211_supported_band *band_5GHz = wiphy->bands[NL80211_BAND_5GHZ];
+#endif
+ struct ieee80211_supported_band *band_2GHz = wiphy->bands[NL80211_BAND_2GHZ];
+ int i;
+ int nss = ecrnx_hw->mod_params->nss;
+
+ if (!ecrnx_hw->mod_params->ht_on) {
+ band_2GHz->ht_cap.ht_supported = false;
+#ifdef CONFIG_ECRNX_5G
+ band_5GHz->ht_cap.ht_supported = false;
+#endif
+ return;
+ }
+ //JIRA438 begin by E0000550
+ //if (ecrnx_hw->mod_params->stbc_on)
+ //JIRA438 end
+ band_2GHz->ht_cap.cap |= 1 << IEEE80211_HT_CAP_RX_STBC_SHIFT;
+ if (ecrnx_hw->mod_params->ldpc_on)
+ band_2GHz->ht_cap.cap |= IEEE80211_HT_CAP_LDPC_CODING;
+ if (ecrnx_hw->mod_params->use_2040) {
+ band_2GHz->ht_cap.mcs.rx_mask[4] = 0x1; /* MCS32 */
+ band_2GHz->ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
+ band_2GHz->ht_cap.mcs.rx_highest = cpu_to_le16(135 * nss);
+ } else {
+ band_2GHz->ht_cap.mcs.rx_highest = cpu_to_le16(65 * nss);
+ }
+ if (nss > 1)
+ band_2GHz->ht_cap.cap |= IEEE80211_HT_CAP_TX_STBC;
+
+ // Update the AMSDU max RX size
+ if (ecrnx_hw->mod_params->amsdu_rx_max)
+ band_2GHz->ht_cap.cap |= IEEE80211_HT_CAP_MAX_AMSDU;
+
+ if (ecrnx_hw->mod_params->sgi) {
+ band_2GHz->ht_cap.cap |= IEEE80211_HT_CAP_SGI_20;
+ if (ecrnx_hw->mod_params->use_2040) {
+ band_2GHz->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40;
+ band_2GHz->ht_cap.mcs.rx_highest = cpu_to_le16(150 * nss);
+ } else
+ band_2GHz->ht_cap.mcs.rx_highest = cpu_to_le16(72 * nss);
+ }
+ if (ecrnx_hw->mod_params->gf_rx_on)
+ band_2GHz->ht_cap.cap |= IEEE80211_HT_CAP_GRN_FLD;
+
+ for (i = 0; i < nss; i++) {
+ band_2GHz->ht_cap.mcs.rx_mask[i] = 0xFF;
+ }
+
+#ifdef CONFIG_ECRNX_5G
+ band_5GHz->ht_cap = band_2GHz->ht_cap;
+#endif
+}
+
+static void ecrnx_set_he_capa(struct ecrnx_hw *ecrnx_hw, struct wiphy *wiphy)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0)
+#ifdef CONFIG_ECRNX_5G
+ struct ieee80211_supported_band *band_5GHz = wiphy->bands[NL80211_BAND_5GHZ];
+#endif
+ struct ieee80211_supported_band *band_2GHz = wiphy->bands[NL80211_BAND_2GHZ];
+#endif
+
+ int i;
+ int nss = ecrnx_hw->mod_params->nss;
+ struct ieee80211_sta_he_cap *he_cap;
+ int mcs_map, mcs_map_max_2ss = IEEE80211_HE_MCS_SUPPORT_0_11;
+ u8 dcm_max_ru = IEEE80211_HE_PHY_CAP8_DCM_MAX_RU_242;
+ u32_l phy_vers = ecrnx_hw->version_cfm.version_phy_2;
+
+ if (!ecrnx_hw->mod_params->he_on) {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0)
+ band_2GHz->iftype_data = NULL;
+ band_2GHz->n_iftype_data = 0;
+ #ifdef CONFIG_ECRNX_5G
+ band_5GHz->iftype_data = NULL;
+ band_5GHz->n_iftype_data = 0;
+ #endif
+#endif
+ return;
+ }
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0)
+ he_cap = (struct ieee80211_sta_he_cap *) &band_2GHz->iftype_data->he_cap;
+#else
+ he_cap = &ecrnx_he_cap;
+#endif
+ he_cap->has_he = true;
+ #ifdef CONFIG_ECRNX_FULLMAC
+ if (ecrnx_hw->version_cfm.features & BIT(MM_FEAT_TWT_BIT))
+ {
+ ecrnx_hw->ext_capa[9] = WLAN_EXT_CAPA10_TWT_REQUESTER_SUPPORT;
+ he_cap->he_cap_elem.mac_cap_info[0] |= IEEE80211_HE_MAC_CAP0_TWT_REQ;
+ }
+ #endif
+ he_cap->he_cap_elem.mac_cap_info[2] |= IEEE80211_HE_MAC_CAP2_ALL_ACK;
+ ecrnx_set_ppe_threshold(ecrnx_hw, he_cap);
+#if 0
+ /* ESWIN turns off 40M automotically when it is HE mode */
+ if (ecrnx_hw->mod_params->use_2040) {
+ he_cap->he_cap_elem.phy_cap_info[0] |=
+ IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G;
+ dcm_max_ru = IEEE80211_HE_PHY_CAP8_DCM_MAX_RU_484;
+ }
+#endif
+ if (ecrnx_hw->mod_params->use_80) {
+ he_cap->he_cap_elem.phy_cap_info[0] |=
+ IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G;
+ mcs_map_max_2ss = IEEE80211_HE_MCS_SUPPORT_0_7;
+ dcm_max_ru = IEEE80211_HE_PHY_CAP8_DCM_MAX_RU_996;
+ }
+ if (ecrnx_hw->mod_params->ldpc_on) {
+ he_cap->he_cap_elem.phy_cap_info[1] |= IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD;
+ } else {
+ // If no LDPC is supported, we have to limit to MCS0_9, as LDPC is mandatory
+ // for MCS 10 and 11
+ ecrnx_hw->mod_params->he_mcs_map = min_t(int, ecrnx_hw->mod_params->he_mcs_map,
+ IEEE80211_HE_MCS_SUPPORT_0_9);
+ }
+#if 0
+ /* ESWIN: sync the he capa with 6600 standalone */
+ he_cap->he_cap_elem.phy_cap_info[1] |= IEEE80211_HE_PHY_CAP1_HE_LTF_AND_GI_FOR_HE_PPDUS_0_8US |
+ IEEE80211_HE_PHY_CAP1_MIDAMBLE_RX_TX_MAX_NSTS;
+ he_cap->he_cap_elem.phy_cap_info[2] |= IEEE80211_HE_PHY_CAP2_MIDAMBLE_RX_TX_MAX_NSTS |
+ IEEE80211_HE_PHY_CAP2_NDP_4x_LTF_AND_3_2US |
+ IEEE80211_HE_PHY_CAP2_DOPPLER_RX;
+#else
+ he_cap->he_cap_elem.phy_cap_info[1] |= IEEE80211_HE_PHY_CAP1_HE_LTF_AND_GI_FOR_HE_PPDUS_0_8US;
+ he_cap->he_cap_elem.phy_cap_info[2] |= IEEE80211_HE_PHY_CAP2_NDP_4x_LTF_AND_3_2US |
+ IEEE80211_HE_PHY_CAP2_DOPPLER_RX;
+#endif
+ if (ecrnx_hw->mod_params->stbc_on)
+ he_cap->he_cap_elem.phy_cap_info[2] |= IEEE80211_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ;
+ he_cap->he_cap_elem.phy_cap_info[3] |= IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_16_QAM |
+ IEEE80211_HE_PHY_CAP3_RX_HE_MU_PPDU_FROM_NON_AP_STA;
+
+ /* ESWIN: sync the he capa with 6600 standalone */
+ if (nss > 1) {
+ he_cap->he_cap_elem.phy_cap_info[3] |= IEEE80211_HE_PHY_CAP3_DCM_MAX_RX_NSS_2;
+ } else {
+ he_cap->he_cap_elem.phy_cap_info[3] |= IEEE80211_HE_PHY_CAP3_DCM_MAX_RX_NSS_1;
+ }
+ if (ecrnx_hw->mod_params->bfmee) {
+ he_cap->he_cap_elem.phy_cap_info[4] |= IEEE80211_HE_PHY_CAP4_SU_BEAMFORMEE;
+ he_cap->he_cap_elem.phy_cap_info[4] |=
+ IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_UNDER_80MHZ_4;
+ }
+ he_cap->he_cap_elem.phy_cap_info[5] |= IEEE80211_HE_PHY_CAP5_NG16_SU_FEEDBACK |
+ IEEE80211_HE_PHY_CAP5_NG16_MU_FEEDBACK;
+ he_cap->he_cap_elem.phy_cap_info[6] |= IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_42_SU |
+ IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_75_MU |
+#if 0
+ /* ESWIN: sync the he capa with 6600 standalone */
+
+ IEEE80211_HE_PHY_CAP6_TRIG_SU_BEAMFORMER_FB |
+ IEEE80211_HE_PHY_CAP6_TRIG_MU_BEAMFORMER_FB |
+#endif
+ IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT |
+ IEEE80211_HE_PHY_CAP6_PARTIAL_BANDWIDTH_DL_MUMIMO;
+ he_cap->he_cap_elem.phy_cap_info[7] |= IEEE80211_HE_PHY_CAP7_HE_SU_MU_PPDU_4XLTF_AND_08_US_GI;
+ he_cap->he_cap_elem.phy_cap_info[8] |= IEEE80211_HE_PHY_CAP8_20MHZ_IN_40MHZ_HE_PPDU_IN_2G |
+ dcm_max_ru;
+ he_cap->he_cap_elem.phy_cap_info[9] |= IEEE80211_HE_PHY_CAP9_RX_FULL_BW_SU_USING_MU_WITH_COMP_SIGB |
+ IEEE80211_HE_PHY_CAP9_RX_FULL_BW_SU_USING_MU_WITH_NON_COMP_SIGB |
+ IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_16US;
+#if 0
+ if (__MDM_VERSION(phy_vers) > 30) {
+ he_cap->he_cap_elem.phy_cap_info[6] |= IEEE80211_HE_PHY_CAP6_PARTIAL_BW_EXT_RANGE;
+ he_cap->he_cap_elem.phy_cap_info[8] |= IEEE80211_HE_PHY_CAP8_HE_ER_SU_1XLTF_AND_08_US_GI |
+ IEEE80211_HE_PHY_CAP8_HE_ER_SU_PPDU_4XLTF_AND_08_US_GI;
+ }
+#endif
+ mcs_map = ecrnx_hw->mod_params->he_mcs_map;
+ memset(&he_cap->he_mcs_nss_supp, 0, sizeof(he_cap->he_mcs_nss_supp));
+ for (i = 0; i < nss; i++) {
+ __le16 unsup_for_ss = cpu_to_le16(IEEE80211_HE_MCS_NOT_SUPPORTED << (i*2));
+ he_cap->he_mcs_nss_supp.rx_mcs_80 |= cpu_to_le16(mcs_map << (i*2));
+ he_cap->he_mcs_nss_supp.rx_mcs_160 |= unsup_for_ss;
+ he_cap->he_mcs_nss_supp.rx_mcs_80p80 |= unsup_for_ss;
+ mcs_map = min_t(int, ecrnx_hw->mod_params->he_mcs_map,
+ mcs_map_max_2ss);
+ }
+ for (; i < 8; i++) {
+ __le16 unsup_for_ss = cpu_to_le16(IEEE80211_HE_MCS_NOT_SUPPORTED << (i*2));
+ he_cap->he_mcs_nss_supp.rx_mcs_80 |= unsup_for_ss;
+ he_cap->he_mcs_nss_supp.rx_mcs_160 |= unsup_for_ss;
+ he_cap->he_mcs_nss_supp.rx_mcs_80p80 |= unsup_for_ss;
+ }
+ mcs_map = ecrnx_hw->mod_params->he_mcs_map;
+ for (i = 0; i < nss; i++) {
+ __le16 unsup_for_ss = cpu_to_le16(IEEE80211_HE_MCS_NOT_SUPPORTED << (i*2));
+ he_cap->he_mcs_nss_supp.tx_mcs_80 |= cpu_to_le16(mcs_map << (i*2));
+ he_cap->he_mcs_nss_supp.tx_mcs_160 |= unsup_for_ss;
+ he_cap->he_mcs_nss_supp.tx_mcs_80p80 |= unsup_for_ss;
+ mcs_map = min_t(int, ecrnx_hw->mod_params->he_mcs_map,
+ mcs_map_max_2ss);
+ }
+ for (; i < 8; i++) {
+ __le16 unsup_for_ss = cpu_to_le16(IEEE80211_HE_MCS_NOT_SUPPORTED << (i*2));
+ he_cap->he_mcs_nss_supp.tx_mcs_80 |= unsup_for_ss;
+ he_cap->he_mcs_nss_supp.tx_mcs_160 |= unsup_for_ss;
+ he_cap->he_mcs_nss_supp.tx_mcs_80p80 |= unsup_for_ss;
+ }
+}
+
+static void ecrnx_set_wiphy_params(struct ecrnx_hw *ecrnx_hw, struct wiphy *wiphy)
+{
+#ifdef CONFIG_ECRNX_SOFTMAC
+ struct ieee80211_hw *hw = ecrnx_hw->hw;
+
+ /* SOFTMAC specific parameters */
+ if (ecrnx_hw->mod_params->hwscan) {
+ hw->wiphy->max_scan_ssids = SCAN_SSID_MAX;
+ hw->wiphy->max_scan_ie_len = SCAN_MAX_IE_LEN;
+ }
+#else
+ /* FULLMAC specific parameters */
+ wiphy->flags |= WIPHY_FLAG_REPORTS_OBSS;
+ wiphy->max_scan_ssids = SCAN_SSID_MAX;
+ wiphy->max_scan_ie_len = SCANU_MAX_IE_LEN;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0)
+ wiphy->support_mbssid = 1;
+#endif
+#endif /* CONFIG_ECRNX_FULLMAC */
+
+ if (ecrnx_hw->mod_params->tdls) {
+ /* TDLS support */
+ wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS;
+#ifdef CONFIG_ECRNX_FULLMAC
+ /* TDLS external setup support */
+ wiphy->flags |= WIPHY_FLAG_TDLS_EXTERNAL_SETUP;
+#endif
+ }
+
+ if (ecrnx_hw->mod_params->ap_uapsd_on)
+ wiphy->flags |= WIPHY_FLAG_AP_UAPSD;
+
+#ifdef CONFIG_ECRNX_FULLMAC
+ if (ecrnx_hw->mod_params->ps_on)
+ wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT;
+ else
+ wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT;
+#endif
+
+ if (ecrnx_hw->mod_params->custregd) {
+ // Apply custom regulatory. Note that for recent kernel versions we use instead the
+ // REGULATORY_WIPHY_SELF_MANAGED flag, along with the regulatory_set_wiphy_regd()
+ // function, that needs to be called after wiphy registration
+ // Check if custom channel set shall be enabled. In such case only monitor mode is
+ // supported
+ if (ecrnx_hw->mod_params->custchan) {
+ wiphy->interface_modes = BIT(NL80211_IFTYPE_MONITOR);
+
+ // Enable "extra" channels
+ wiphy->bands[NL80211_BAND_2GHZ]->n_channels += 13;
+ #ifdef CONFIG_ECRNX_5G
+ wiphy->bands[NL80211_BAND_5GHZ]->n_channels += 59;
+ #endif
+ }
+ }
+}
+
+static void ecrnx_set_rf_params(struct ecrnx_hw *ecrnx_hw, struct wiphy *wiphy)
+{
+#ifndef CONFIG_ECRNX_SDM
+#ifdef CONFIG_ECRNX_5G
+ struct ieee80211_supported_band *band_5GHz = wiphy->bands[NL80211_BAND_5GHZ];
+#else
+#endif
+ u32 mdm_phy_cfg = __MDM_PHYCFG_FROM_VERS(ecrnx_hw->version_cfm.version_phy_1);
+
+ /*
+ * Get configuration file depending on the RF
+ */
+#if 0 /* baoyong: we use the custom rf, do not need this */
+ struct ecrnx_phy_conf_file phy_conf;
+ if (mdm_phy_cfg == MDM_PHY_CONFIG_TRIDENT) {
+ // Retrieve the Trident configuration
+ ecrnx_parse_phy_configfile(ecrnx_hw, ECRNX_PHY_CONFIG_TRD_NAME,
+ &phy_conf, ecrnx_hw->mod_params->phy_cfg);
+ memcpy(&ecrnx_hw->phy.cfg, &phy_conf.trd, sizeof(phy_conf.trd));
+ } else if (mdm_phy_cfg == MDM_PHY_CONFIG_CATAXIA) {
+ memset(&phy_conf.cataxia, 0, sizeof(phy_conf.cataxia));
+ phy_conf.cataxia.path_used = ecrnx_hw->mod_params->phy_cfg;
+ memcpy(&ecrnx_hw->phy.cfg, &phy_conf.cataxia, sizeof(phy_conf.cataxia));
+ } else if (mdm_phy_cfg == MDM_PHY_CONFIG_KARST) {
+ // We use the NSS parameter as is
+ // Retrieve the Karst configuration
+ ecrnx_parse_phy_configfile(ecrnx_hw, ECRNX_PHY_CONFIG_KARST_NAME,
+ &phy_conf, ecrnx_hw->mod_params->phy_cfg);
+
+ memcpy(&ecrnx_hw->phy.cfg, &phy_conf.karst, sizeof(phy_conf.karst));
+ } else {
+ WARN_ON(1);
+ }
+#endif
+
+ /*
+ * adjust caps depending on the RF
+ */
+ switch (mdm_phy_cfg) {
+ case MDM_PHY_CONFIG_TRIDENT:
+ {
+ wiphy_dbg(wiphy, "found Trident PHY .. limit BW to 40MHz\n");
+ ecrnx_hw->phy.limit_bw = true;
+#ifdef CONFIG_ECRNX_5G
+#ifdef CONFIG_VENDOR_ECRNX_VHT_NO80
+ band_5GHz->vht_cap.cap |= IEEE80211_VHT_CAP_NOT_SUP_WIDTH_80;
+#endif
+ band_5GHz->vht_cap.cap &= ~(IEEE80211_VHT_CAP_SHORT_GI_80 |
+ IEEE80211_VHT_CAP_RXSTBC_MASK);
+#endif
+ break;
+ }
+ case MDM_PHY_CONFIG_CATAXIA:
+ {
+ wiphy_dbg(wiphy, "found CATAXIA PHY\n");
+ break;
+ }
+ case MDM_PHY_CONFIG_KARST:
+ {
+ wiphy_dbg(wiphy, "found KARST PHY\n");
+ break;
+ }
+ default:
+ WARN_ON(1);
+ break;
+ }
+#endif /* CONFIG_ECRNX_SDM */
+}
+
+int ecrnx_handle_dynparams(struct ecrnx_hw *ecrnx_hw, struct wiphy *wiphy)
+{
+ int ret;
+
+ /* Check compatibility between requested parameters and HW/SW features */
+ ret = ecrnx_check_fw_hw_feature(ecrnx_hw, wiphy);
+ if (ret)
+ return ret;
+#ifndef CONFIG_ECRNX_ESWIN
+
+ /* Allocate the RX buffers according to the maximum AMSDU RX size */
+ ret = ecrnx_ipc_rxbuf_init(ecrnx_hw,
+ (4 * (ecrnx_hw->mod_params->amsdu_rx_max + 1) + 1) * 1024);
+ if (ret) {
+ wiphy_err(wiphy, "Cannot allocate the RX buffers\n");
+ return ret;
+ }
+#endif
+#ifdef CONFIG_ECRNX_SOFTMAC
+ /* SOFTMAC specific parameters*/
+ ecrnx_set_softmac_flags(ecrnx_hw);
+#endif /* CONFIG_ECRNX_SOFTMAC */
+
+ /* Set wiphy parameters */
+ ecrnx_set_wiphy_params(ecrnx_hw, wiphy);
+
+ /* Set VHT capabilities */
+ ecrnx_set_vht_capa(ecrnx_hw, wiphy);
+
+ /* Set HE capabilities */
+ ecrnx_set_he_capa(ecrnx_hw, wiphy);
+
+ /* Set HT capabilities */
+ ecrnx_set_ht_capa(ecrnx_hw, wiphy);
+
+ /* Set RF specific parameters (shall be done last as it might change some
+ capabilities previously set) */
+ ecrnx_set_rf_params(ecrnx_hw, wiphy);
+
+ return 0;
+}
+
+void ecrnx_custregd(struct ecrnx_hw *ecrnx_hw, struct wiphy *wiphy)
+{
+// For older kernel version, the custom regulatory is applied before the wiphy
+// registration (in ecrnx_set_wiphy_params()), so nothing has to be done here
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)
+ if (!ecrnx_hw->mod_params->custregd)
+ return;
+
+ wiphy->regulatory_flags |= REGULATORY_WIPHY_SELF_MANAGED;
+
+ rtnl_lock();
+ if (regulatory_set_wiphy_regd_sync_rtnl(wiphy, &ecrnx_regdom))
+ wiphy_err(wiphy, "Failed to set custom regdomain\n");
+ else
+ wiphy_err(wiphy,"\n"
+ "*******************************************************\n"
+ "** CAUTION: USING PERMISSIVE CUSTOM REGULATORY RULES **\n"
+ "*******************************************************\n");
+ rtnl_unlock();
+#endif
+}
+
+void ecrnx_adjust_amsdu_maxnb(struct ecrnx_hw *ecrnx_hw)
+{
+ if (ecrnx_hw->mod_params->amsdu_maxnb > NX_TX_PAYLOAD_MAX)
+ ecrnx_hw->mod_params->amsdu_maxnb = NX_TX_PAYLOAD_MAX;
+ else if (ecrnx_hw->mod_params->amsdu_maxnb == 0)
+ ecrnx_hw->mod_params->amsdu_maxnb = 1;
+}
diff --git a/drivers/net/wireless/eswin/ecrnx_mod_params.h b/drivers/net/wireless/eswin/ecrnx_mod_params.h
new file mode 100644
index 000000000000..72c87dbbd76f
--- /dev/null
+++ b/drivers/net/wireless/eswin/ecrnx_mod_params.h
@@ -0,0 +1,86 @@
+/**
+ ******************************************************************************
+ *
+ * @file ecrnx_mod_params.h
+ *
+ * @brief Declaration of module parameters
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+
+#ifndef _ECRNX_MOD_PARAM_H_
+#define _ECRNX_MOD_PARAM_H_
+
+struct ecrnx_mod_params {
+ bool ht_on;
+ bool vht_on;
+ bool he_on;
+ int mcs_map;
+ int he_mcs_map;
+ bool he_ul_on;
+ bool ldpc_on;
+ bool stbc_on;
+ bool gf_rx_on;
+ int phy_cfg;
+ int uapsd_timeout;
+ bool ap_uapsd_on;
+ bool sgi;
+ bool sgi80;
+ bool use_2040;
+ bool use_80;
+ bool custregd;
+ bool custchan;
+ int nss;
+ int amsdu_rx_max;
+ bool bfmee;
+ bool bfmer;
+ bool mesh;
+ bool murx;
+ bool mutx;
+ bool mutx_on;
+ unsigned int roc_dur_max;
+ int listen_itv;
+ bool listen_bcmc;
+ int lp_clk_ppm;
+ bool ps_on;
+ int tx_lft;
+ int amsdu_maxnb;
+ int uapsd_queues;
+ bool tdls;
+ bool uf;
+ char *ftl;
+ bool dpsm;
+ int tx_to_bk;
+ int tx_to_be;
+ int tx_to_vi;
+ int tx_to_vo;
+#ifdef CONFIG_ECRNX_SOFTMAC
+ bool mfp_on;
+ bool gf_on;
+ bool bwsig_on;
+ bool dynbw_on;
+ bool agg_tx;
+ int amsdu_force;
+ bool rc_probes_on;
+ bool cmon;
+ bool hwscan;
+ bool autobcn;
+#else
+ bool ant_div;
+#endif /* CONFIG_ECRNX_SOFTMAC */
+};
+
+extern struct ecrnx_mod_params ecrnx_mod_params;
+
+struct ecrnx_hw;
+struct wiphy;
+int ecrnx_handle_dynparams(struct ecrnx_hw *ecrnx_hw, struct wiphy *wiphy);
+void ecrnx_custregd(struct ecrnx_hw *ecrnx_hw, struct wiphy *wiphy);
+void ecrnx_enable_wapi(struct ecrnx_hw *ecrnx_hw);
+void ecrnx_enable_mfp(struct ecrnx_hw *ecrnx_hw);
+void ecrnx_enable_gcmp(struct ecrnx_hw *ecrnx_hw);
+void ecrnx_adjust_amsdu_maxnb(struct ecrnx_hw *ecrnx_hw);
+
+#endif /* _ECRNX_MOD_PARAM_H_ */
diff --git a/drivers/net/wireless/eswin/ecrnx_msg_rx.c b/drivers/net/wireless/eswin/ecrnx_msg_rx.c
new file mode 100644
index 000000000000..eab96aa57cb1
--- /dev/null
+++ b/drivers/net/wireless/eswin/ecrnx_msg_rx.c
@@ -0,0 +1,1604 @@
+/**
+ ****************************************************************************************
+ *
+ * @file ecrnx_msg_rx.c
+ *
+ * @brief RX function definitions
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ****************************************************************************************
+ */
+#include "ecrnx_defs.h"
+#include "ecrnx_prof.h"
+#include "ecrnx_tx.h"
+#ifdef CONFIG_ECRNX_BFMER
+#include "ecrnx_bfmer.h"
+#endif //(CONFIG_ECRNX_BFMER)
+#ifdef CONFIG_ECRNX_FULLMAC
+#include "ecrnx_debugfs.h"
+#include "ecrnx_msg_tx.h"
+#include "ecrnx_tdls.h"
+#endif /* CONFIG_ECRNX_FULLMAC */
+#include "ecrnx_events.h"
+#include "ecrnx_compat.h"
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0)
+#include <linux/time.h>
+#endif
+
+#if defined(CONFIG_ECRNX_DEBUGFS_CUSTOM)
+#include "ecrnx_debugfs_func.h"
+#endif
+static int ecrnx_freq_to_idx(struct ecrnx_hw *ecrnx_hw, int freq)
+{
+ struct ieee80211_supported_band *sband;
+ int band, ch, idx = 0;
+
+#ifdef CONFIG_ECRNX_5G
+ for (band = NL80211_BAND_2GHZ; band < NUM_NL80211_BANDS; band++) {
+#else
+ for (band = NL80211_BAND_2GHZ; band <= NL80211_BAND_2GHZ; band++) {
+#endif
+#ifdef CONFIG_ECRNX_SOFTMAC
+ sband = ecrnx_hw->hw->wiphy->bands[band];
+#else
+ sband = ecrnx_hw->wiphy->bands[band];
+#endif /* CONFIG_ECRNX_SOFTMAC */
+ if (!sband) {
+ continue;
+ }
+
+ for (ch = 0; ch < sband->n_channels; ch++, idx++) {
+ if (sband->channels[ch].center_freq == freq) {
+ goto exit;
+ }
+ }
+ }
+
+ ECRNX_ERR("--!!!!!!!!error freq-----%d\n", freq);
+ //BUG_ON(1);
+
+exit:
+ // Channel has been found, return the index
+ return idx;
+}
+
+/***************************************************************************
+ * Messages from MM task
+ **************************************************************************/
+static inline int ecrnx_rx_chan_pre_switch_ind(struct ecrnx_hw *ecrnx_hw,
+ struct ecrnx_cmd *cmd,
+ struct ipc_e2a_msg *msg)
+{
+#ifdef CONFIG_ECRNX_SOFTMAC
+ struct ecrnx_chanctx *chan_ctxt;
+#endif
+ struct ecrnx_vif *ecrnx_vif;
+ int chan_idx = ((struct mm_channel_pre_switch_ind *)msg->param)->chan_index;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ REG_SW_SET_PROFILING_CHAN(ecrnx_hw, SW_PROF_CHAN_CTXT_PSWTCH_BIT);
+
+#ifdef CONFIG_ECRNX_SOFTMAC
+ list_for_each_entry(chan_ctxt, &ecrnx_hw->chan_ctxts, list) {
+ if (chan_ctxt->index == chan_idx) {
+ chan_ctxt->active = false;
+ break;
+ }
+ }
+
+ list_for_each_entry(ecrnx_vif, &ecrnx_hw->vifs, list) {
+ if (ecrnx_vif->chanctx && (ecrnx_vif->chanctx->index == chan_idx)) {
+ ecrnx_txq_vif_stop(ecrnx_vif, ECRNX_TXQ_STOP_CHAN, ecrnx_hw);
+ }
+ }
+#else
+ list_for_each_entry(ecrnx_vif, &ecrnx_hw->vifs, list) {
+ if (ecrnx_vif->up && ecrnx_vif->ch_index == chan_idx) {
+ ecrnx_txq_vif_stop(ecrnx_vif, ECRNX_TXQ_STOP_CHAN, ecrnx_hw);
+ }
+ }
+#endif /* CONFIG_ECRNX_SOFTMAC */
+
+ REG_SW_CLEAR_PROFILING_CHAN(ecrnx_hw, SW_PROF_CHAN_CTXT_PSWTCH_BIT);
+
+ return 0;
+}
+
+static inline int ecrnx_rx_chan_switch_ind(struct ecrnx_hw *ecrnx_hw,
+ struct ecrnx_cmd *cmd,
+ struct ipc_e2a_msg *msg)
+{
+#ifdef CONFIG_ECRNX_SOFTMAC
+ struct ecrnx_chanctx *chan_ctxt;
+ struct ecrnx_sta *ecrnx_sta;
+#endif
+ struct ecrnx_vif *ecrnx_vif;
+ int chan_idx = ((struct mm_channel_switch_ind *)msg->param)->chan_index;
+ bool roc_req = ((struct mm_channel_switch_ind *)msg->param)->roc;
+ bool roc_tdls = ((struct mm_channel_switch_ind *)msg->param)->roc_tdls;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ REG_SW_SET_PROFILING_CHAN(ecrnx_hw, SW_PROF_CHAN_CTXT_SWTCH_BIT);
+
+#ifdef CONFIG_ECRNX_SOFTMAC
+ if (roc_tdls) {
+ u8 vif_index = ((struct mm_channel_switch_ind *)msg->param)->vif_index;
+ // Enable traffic only for TDLS station
+ list_for_each_entry(ecrnx_vif, &ecrnx_hw->vifs, list) {
+ if (ecrnx_vif->vif_index == vif_index) {
+ list_for_each_entry(ecrnx_sta, &ecrnx_vif->stations, list) {
+ if (ecrnx_sta->tdls.active) {
+ ecrnx_vif->roc_tdls = true;
+ ecrnx_txq_tdls_sta_start(ecrnx_sta, ECRNX_TXQ_STOP_CHAN, ecrnx_hw);
+ break;
+ }
+ }
+ break;
+ }
+ }
+ } else if (!roc_req) {
+ list_for_each_entry(ecrnx_vif, &ecrnx_hw->vifs, list) {
+ if (ecrnx_vif->chanctx && (ecrnx_vif->chanctx->index == chan_idx)) {
+ ecrnx_txq_vif_start(ecrnx_vif, ECRNX_TXQ_STOP_CHAN, ecrnx_hw);
+ }
+ }
+ } else {
+ u8 vif_index = ((struct mm_channel_switch_ind *)msg->param)->vif_index;
+
+ // Inform the host that the offchannel period has been started
+ ieee80211_ready_on_channel(ecrnx_hw->hw);
+
+ // Enable traffic for associated VIF (roc may happen without chanctx)
+ list_for_each_entry(ecrnx_vif, &ecrnx_hw->vifs, list) {
+ if (ecrnx_vif->vif_index == vif_index) {
+ ecrnx_txq_vif_start(ecrnx_vif, ECRNX_TXQ_STOP_CHAN, ecrnx_hw);
+ }
+ }
+ }
+
+ /* keep cur_chan up to date */
+ list_for_each_entry(chan_ctxt, &ecrnx_hw->chan_ctxts, list) {
+ if (chan_ctxt->index == chan_idx) {
+ chan_ctxt->active = true;
+ ecrnx_hw->cur_freq = chan_ctxt->ctx->def.center_freq1;
+ ecrnx_hw->cur_band = chan_ctxt->ctx->def.chan->band;
+ if (chan_ctxt->ctx->def.chan->flags & IEEE80211_CHAN_RADAR) {
+ ecrnx_radar_detection_enable(&ecrnx_hw->radar,
+ ECRNX_RADAR_DETECT_REPORT,
+ ECRNX_RADAR_RIU);
+ } else {
+ ecrnx_radar_detection_enable(&ecrnx_hw->radar,
+ ECRNX_RADAR_DETECT_DISABLE,
+ ECRNX_RADAR_RIU);
+ }
+ break;
+ }
+ }
+
+#else
+ if (roc_tdls) {
+ u8 vif_index = ((struct mm_channel_switch_ind *)msg->param)->vif_index;
+ list_for_each_entry(ecrnx_vif, &ecrnx_hw->vifs, list) {
+ if (ecrnx_vif->vif_index == vif_index) {
+ ecrnx_vif->roc_tdls = true;
+ ecrnx_txq_tdls_sta_start(ecrnx_vif, ECRNX_TXQ_STOP_CHAN, ecrnx_hw);
+ }
+ }
+ } else if (!roc_req) {
+ list_for_each_entry(ecrnx_vif, &ecrnx_hw->vifs, list) {
+ if (ecrnx_vif->up && ecrnx_vif->ch_index == chan_idx) {
+ ecrnx_txq_vif_start(ecrnx_vif, ECRNX_TXQ_STOP_CHAN, ecrnx_hw);
+ }
+ }
+ } else {
+ /* Retrieve the allocated RoC element */
+ struct ecrnx_roc *roc = ecrnx_hw->roc;
+
+ if (roc && roc->vif) {
+ /* Get VIF on which RoC has been started */
+ ecrnx_vif = roc->vif;
+ /* For debug purpose (use ftrace kernel option) */
+ trace_switch_roc(ecrnx_vif->vif_index);
+
+ if (!roc->internal) {
+ /* If mgmt_roc is true, remain on channel has been started by ourself */
+ /* Inform the host that we have switch on the indicated off-channel */
+ cfg80211_ready_on_channel(&ecrnx_vif->wdev, (u64)(ecrnx_hw->roc_cookie),
+ roc->chan, roc->duration, GFP_ATOMIC);
+ }
+
+ /* Keep in mind that we have switched on the channel */
+ roc->on_chan = true;
+ }
+
+ // Enable traffic on OFF channel queue
+ ecrnx_txq_offchan_start(ecrnx_hw);
+#if defined(CONFIG_ECRNX_P2P)
+ if (roc && roc->internal) {
+ ecrnx_hw->p2p_listen.rxdatas = 1;
+ wake_up(&ecrnx_hw->p2p_listen.rxdataq);
+ }
+#endif
+ }
+
+ ecrnx_hw->cur_chanctx = chan_idx;
+ ecrnx_radar_detection_enable_on_cur_channel(ecrnx_hw);
+
+#endif /* CONFIG_ECRNX_SOFTMAC */
+
+ REG_SW_CLEAR_PROFILING_CHAN(ecrnx_hw, SW_PROF_CHAN_CTXT_SWTCH_BIT);
+
+ return 0;
+}
+
+static inline int ecrnx_rx_tdls_chan_switch_cfm(struct ecrnx_hw *ecrnx_hw,
+ struct ecrnx_cmd *cmd,
+ struct ipc_e2a_msg *msg)
+{
+ return 0;
+}
+
+static inline int ecrnx_rx_tdls_chan_switch_ind(struct ecrnx_hw *ecrnx_hw,
+ struct ecrnx_cmd *cmd,
+ struct ipc_e2a_msg *msg)
+{
+#ifdef CONFIG_ECRNX_SOFTMAC
+ struct ecrnx_chanctx *chan_ctxt;
+ u8 chan_idx = ((struct tdls_chan_switch_ind *)msg->param)->chan_ctxt_index;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ // Enable channel context
+ list_for_each_entry(chan_ctxt, &ecrnx_hw->chan_ctxts, list) {
+ if (chan_ctxt->index == chan_idx) {
+ chan_ctxt->active = true;
+ ecrnx_hw->cur_freq = chan_ctxt->ctx->def.center_freq1;
+ ecrnx_hw->cur_band = chan_ctxt->ctx->def.chan->band;
+ }
+ }
+
+ return 0;
+#else
+ // Enable traffic on OFF channel queue
+ ecrnx_txq_offchan_start(ecrnx_hw);
+
+ return 0;
+#endif
+}
+
+static inline int ecrnx_rx_tdls_chan_switch_base_ind(struct ecrnx_hw *ecrnx_hw,
+ struct ecrnx_cmd *cmd,
+ struct ipc_e2a_msg *msg)
+{
+ struct ecrnx_vif *ecrnx_vif;
+ u8 vif_index = ((struct tdls_chan_switch_base_ind *)msg->param)->vif_index;
+#ifdef CONFIG_ECRNX_SOFTMAC
+ struct ecrnx_sta *ecrnx_sta;
+#endif
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+#ifdef CONFIG_ECRNX_SOFTMAC
+ // Disable traffic for associated VIF
+ list_for_each_entry(ecrnx_vif, &ecrnx_hw->vifs, list) {
+ if (ecrnx_vif->vif_index == vif_index) {
+ if (ecrnx_vif->chanctx)
+ ecrnx_vif->chanctx->active = false;
+ list_for_each_entry(ecrnx_sta, &ecrnx_vif->stations, list) {
+ if (ecrnx_sta->tdls.active) {
+ ecrnx_vif->roc_tdls = false;
+ ecrnx_txq_tdls_sta_stop(ecrnx_sta, ECRNX_TXQ_STOP_CHAN, ecrnx_hw);
+ break;
+ }
+ }
+ break;
+ }
+ }
+ return 0;
+#else
+ list_for_each_entry(ecrnx_vif, &ecrnx_hw->vifs, list) {
+ if (ecrnx_vif->vif_index == vif_index) {
+ ecrnx_vif->roc_tdls = false;
+ ecrnx_txq_tdls_sta_stop(ecrnx_vif, ECRNX_TXQ_STOP_CHAN, ecrnx_hw);
+ }
+ }
+ return 0;
+#endif
+}
+
+static inline int ecrnx_rx_tdls_peer_ps_ind(struct ecrnx_hw *ecrnx_hw,
+ struct ecrnx_cmd *cmd,
+ struct ipc_e2a_msg *msg)
+{
+ struct ecrnx_vif *ecrnx_vif;
+ u8 vif_index = ((struct tdls_peer_ps_ind *)msg->param)->vif_index;
+ bool ps_on = ((struct tdls_peer_ps_ind *)msg->param)->ps_on;
+
+#ifdef CONFIG_ECRNX_SOFTMAC
+ u8 sta_idx = ((struct tdls_peer_ps_ind *)msg->param)->sta_idx;
+ struct ecrnx_sta *ecrnx_sta;
+ list_for_each_entry(ecrnx_vif, &ecrnx_hw->vifs, list) {
+ if (ecrnx_vif->vif_index == vif_index) {
+ list_for_each_entry(ecrnx_sta, &ecrnx_vif->stations, list) {
+ if (ecrnx_sta->sta_idx == sta_idx) {
+ ecrnx_sta->tdls.ps_on = ps_on;
+ if (ps_on) {
+ // disable TXQ for TDLS peer
+ ecrnx_txq_tdls_sta_stop(ecrnx_sta, ECRNX_TXQ_STOP_STA_PS, ecrnx_hw);
+ } else {
+ // Enable TXQ for TDLS peer
+ ecrnx_txq_tdls_sta_start(ecrnx_sta, ECRNX_TXQ_STOP_STA_PS, ecrnx_hw);
+ }
+ break;
+ }
+ }
+ break;
+ }
+ }
+ return 0;
+#else
+ list_for_each_entry(ecrnx_vif, &ecrnx_hw->vifs, list) {
+ if (ecrnx_vif->vif_index == vif_index) {
+ ecrnx_vif->sta.tdls_sta->tdls.ps_on = ps_on;
+ // Update PS status for the TDLS station
+ ecrnx_ps_bh_enable(ecrnx_hw, ecrnx_vif->sta.tdls_sta, ps_on);
+ }
+ }
+
+ return 0;
+#endif
+}
+
+static inline int ecrnx_rx_remain_on_channel_exp_ind(struct ecrnx_hw *ecrnx_hw,
+ struct ecrnx_cmd *cmd,
+ struct ipc_e2a_msg *msg)
+{
+#ifdef CONFIG_ECRNX_SOFTMAC
+ struct ecrnx_vif *ecrnx_vif;
+ u8 vif_index = ((struct mm_remain_on_channel_exp_ind *)msg->param)->vif_index;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ ieee80211_remain_on_channel_expired(ecrnx_hw->hw);
+
+ // Disable traffic for associated VIF
+ list_for_each_entry(ecrnx_vif, &ecrnx_hw->vifs, list) {
+ if (ecrnx_vif->vif_index == vif_index) {
+ if (ecrnx_vif->chanctx)
+ ecrnx_vif->chanctx->active = false;
+
+ ecrnx_txq_vif_stop(ecrnx_vif, ECRNX_TXQ_STOP_CHAN, ecrnx_hw);
+ break;
+ }
+ }
+
+ return 0;
+
+#else
+ if(!ecrnx_hw->roc || !ecrnx_hw->roc->chan){
+ ECRNX_ERR("error!!!:ecrnx_hw->roc or !ecrnx_hw->roc->chan is null \n");
+ return 0;
+ }
+ /* Retrieve the allocated RoC element */
+ struct ecrnx_roc *roc = ecrnx_hw->roc;
+ /* Get VIF on which RoC has been started */
+ struct ecrnx_vif *ecrnx_vif = roc->vif;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ /* For debug purpose (use ftrace kernel option) */
+ trace_roc_exp(ecrnx_vif->vif_index);
+
+ /* If mgmt_roc is true, remain on channel has been started by ourself */
+ /* If RoC has been cancelled before we switched on channel, do not call cfg80211 */
+ if (!roc->internal && roc->on_chan) {
+ /* Inform the host that off-channel period has expired */
+ cfg80211_remain_on_channel_expired(&ecrnx_vif->wdev, (u64)(ecrnx_hw->roc_cookie),
+ roc->chan, GFP_ATOMIC);
+ }
+
+ /* De-init offchannel TX queue */
+ ecrnx_txq_offchan_deinit(ecrnx_vif);
+
+ /* Increase the cookie counter cannot be zero */
+ ecrnx_hw->roc_cookie++;
+
+ if (ecrnx_hw->roc_cookie == 0)
+ ecrnx_hw->roc_cookie = 1;
+
+#if CONFIG_ECRNX_P2P
+ ecrnx_hw->p2p_listen.listen_started = 0;
+#endif
+
+ /* Free the allocated RoC element */
+ kfree(roc);
+ ecrnx_hw->roc = NULL;
+
+#if defined(CONFIG_ECRNX_P2P)
+ wake_up(&ecrnx_hw->p2p_listen.rxdataq);
+#endif
+
+#endif /* CONFIG_ECRNX_SOFTMAC */
+ return 0;
+}
+
+static inline int ecrnx_rx_p2p_vif_ps_change_ind(struct ecrnx_hw *ecrnx_hw,
+ struct ecrnx_cmd *cmd,
+ struct ipc_e2a_msg *msg)
+{
+ int vif_idx = ((struct mm_p2p_vif_ps_change_ind *)msg->param)->vif_index;
+ int ps_state = ((struct mm_p2p_vif_ps_change_ind *)msg->param)->ps_state;
+ struct ecrnx_vif *vif_entry;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+#ifdef CONFIG_ECRNX_SOFTMAC
+ // Look for VIF entry
+ list_for_each_entry(vif_entry, &ecrnx_hw->vifs, list) {
+ if (vif_entry->vif_index == vif_idx) {
+ goto found_vif;
+ }
+ }
+#else
+ vif_entry = ecrnx_hw->vif_table[vif_idx];
+
+ if (vif_entry) {
+ goto found_vif;
+ }
+#endif /* CONFIG_ECRNX_SOFTMAC */
+
+ goto exit;
+
+found_vif:
+
+#ifdef CONFIG_ECRNX_SOFTMAC
+ if (ps_state == MM_PS_MODE_OFF) {
+ ecrnx_txq_vif_start(vif_entry, ECRNX_TXQ_STOP_VIF_PS, ecrnx_hw);
+ }
+ else {
+ ecrnx_txq_vif_stop(vif_entry, ECRNX_TXQ_STOP_VIF_PS, ecrnx_hw);
+ }
+#else
+ if (ps_state == MM_PS_MODE_OFF) {
+ // Start TX queues for provided VIF
+ ecrnx_txq_vif_start(vif_entry, ECRNX_TXQ_STOP_VIF_PS, ecrnx_hw);
+ }
+ else {
+ // Stop TX queues for provided VIF
+ ecrnx_txq_vif_stop(vif_entry, ECRNX_TXQ_STOP_VIF_PS, ecrnx_hw);
+ }
+#endif /* CONFIG_ECRNX_SOFTMAC */
+
+exit:
+ return 0;
+}
+
+static inline int ecrnx_rx_channel_survey_ind(struct ecrnx_hw *ecrnx_hw,
+ struct ecrnx_cmd *cmd,
+ struct ipc_e2a_msg *msg)
+{
+ struct mm_channel_survey_ind *ind = (struct mm_channel_survey_ind *)msg->param;
+ // Get the channel index
+ int idx = ecrnx_freq_to_idx(ecrnx_hw, ind->freq);
+ // Get the survey
+ struct ecrnx_survey_info *ecrnx_survey;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ if (idx > ARRAY_SIZE(ecrnx_hw->survey))
+ return 0;
+
+ ecrnx_survey = &ecrnx_hw->survey[idx];
+
+ // Store the received parameters
+ ecrnx_survey->chan_time_ms = ind->chan_time_ms;
+ ecrnx_survey->chan_time_busy_ms = ind->chan_time_busy_ms;
+ ecrnx_survey->noise_dbm = ind->noise_dbm;
+ ecrnx_survey->filled = (SURVEY_INFO_TIME |
+ SURVEY_INFO_TIME_BUSY);
+
+ if (ind->noise_dbm != 0) {
+ ecrnx_survey->filled |= SURVEY_INFO_NOISE_DBM;
+ }
+
+#if defined(CONFIG_ECRNX_DEBUGFS_CUSTOM)
+ ecrnx_debugfs_noise_of_survey_info_update(ecrnx_hw, ecrnx_survey, idx);
+#endif
+
+ return 0;
+}
+
+static inline int ecrnx_rx_p2p_noa_upd_ind(struct ecrnx_hw *ecrnx_hw,
+ struct ecrnx_cmd *cmd,
+ struct ipc_e2a_msg *msg)
+{
+ return 0;
+}
+
+static inline int ecrnx_rx_rssi_status_ind(struct ecrnx_hw *ecrnx_hw,
+ struct ecrnx_cmd *cmd,
+ struct ipc_e2a_msg *msg)
+{
+ struct mm_rssi_status_ind *ind = (struct mm_rssi_status_ind *)msg->param;
+ int vif_idx = ind->vif_index;
+ bool rssi_status = ind->rssi_status;
+
+ struct ecrnx_vif *vif_entry;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+#ifdef CONFIG_ECRNX_SOFTMAC
+ list_for_each_entry(vif_entry, &ecrnx_hw->vifs, list) {
+ if (vif_entry->vif_index == vif_idx) {
+ ecrnx_ieee80211_cqm_rssi_notify(vif_entry->vif,
+ rssi_status ? NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW :
+ NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH,
+ ind->rssi, GFP_ATOMIC);
+ }
+ }
+#else
+ vif_entry = ecrnx_hw->vif_table[vif_idx];
+ if (vif_entry) {
+ ecrnx_cfg80211_cqm_rssi_notify(vif_entry->ndev,
+ rssi_status ? NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW :
+ NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH,
+ ind->rssi, GFP_ATOMIC);
+ }
+#endif /* CONFIG_ECRNX_SOFTMAC */
+
+ return 0;
+}
+
+static inline int ecrnx_rx_pktloss_notify_ind(struct ecrnx_hw *ecrnx_hw,
+ struct ecrnx_cmd *cmd,
+ struct ipc_e2a_msg *msg)
+{
+#ifdef CONFIG_ECRNX_FULLMAC
+ struct mm_pktloss_ind *ind = (struct mm_pktloss_ind *)msg->param;
+ struct ecrnx_vif *vif_entry;
+ int vif_idx = ind->vif_index;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ vif_entry = ecrnx_hw->vif_table[vif_idx];
+ if (vif_entry) {
+ cfg80211_cqm_pktloss_notify(vif_entry->ndev, (const u8 *)ind->mac_addr.array,
+ ind->num_packets, GFP_ATOMIC);
+ }
+#endif /* CONFIG_ECRNX_FULLMAC */
+
+ return 0;
+}
+
+static inline int ecrnx_rx_csa_counter_ind(struct ecrnx_hw *ecrnx_hw,
+ struct ecrnx_cmd *cmd,
+ struct ipc_e2a_msg *msg)
+{
+ struct mm_csa_counter_ind *ind = (struct mm_csa_counter_ind *)msg->param;
+ struct ecrnx_vif *vif;
+ bool found = false;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ // Look for VIF entry
+ list_for_each_entry(vif, &ecrnx_hw->vifs, list) {
+ if (vif->vif_index == ind->vif_index) {
+ found=true;
+ break;
+ }
+ }
+
+ if (found) {
+#ifdef CONFIG_ECRNX_SOFTMAC
+ if (ind->csa_count == 1)
+ ieee80211_csa_finish(vif->vif);
+ else
+ ieee80211_csa_update_counter(vif->vif);
+#else
+ if (vif->ap.csa)
+ vif->ap.csa->count = ind->csa_count;
+ else
+ netdev_err(vif->ndev, "CSA counter update but no active CSA");
+
+#endif
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_ECRNX_SOFTMAC
+static inline int ecrnx_rx_connection_loss_ind(struct ecrnx_hw *ecrnx_hw,
+ struct ecrnx_cmd *cmd,
+ struct ipc_e2a_msg *msg)
+{
+ struct ecrnx_vif *ecrnx_vif;
+ u8 inst_nbr;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ inst_nbr = ((struct mm_connection_loss_ind *)msg->param)->inst_nbr;
+
+ /* Search the VIF entry corresponding to the instance number */
+ list_for_each_entry(ecrnx_vif, &ecrnx_hw->vifs, list) {
+ if (ecrnx_vif->vif_index == inst_nbr) {
+ ieee80211_connection_loss(ecrnx_vif->vif);
+ break;
+ }
+ }
+
+ return 0;
+}
+
+
+#ifdef CONFIG_ECRNX_BCN
+static inline int ecrnx_rx_prm_tbtt_ind(struct ecrnx_hw *ecrnx_hw,
+ struct ecrnx_cmd *cmd,
+ struct ipc_e2a_msg *msg)
+{
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ ecrnx_tx_bcns(ecrnx_hw);
+
+ return 0;
+}
+#endif
+
+#else /* !CONFIG_ECRNX_SOFTMAC */
+static inline int ecrnx_rx_csa_finish_ind(struct ecrnx_hw *ecrnx_hw,
+ struct ecrnx_cmd *cmd,
+ struct ipc_e2a_msg *msg)
+{
+ struct mm_csa_finish_ind *ind = (struct mm_csa_finish_ind *)msg->param;
+ struct ecrnx_vif *vif;
+ bool found = false;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ // Look for VIF entry
+ list_for_each_entry(vif, &ecrnx_hw->vifs, list) {
+ if (vif->vif_index == ind->vif_index) {
+ found=true;
+ break;
+ }
+ }
+
+ if (found) {
+ if (ECRNX_VIF_TYPE(vif) == NL80211_IFTYPE_AP ||
+ ECRNX_VIF_TYPE(vif) == NL80211_IFTYPE_P2P_GO) {
+ if (vif->ap.csa) {
+ vif->ap.csa->status = ind->status;
+ vif->ap.csa->ch_idx = ind->chan_idx;
+ schedule_work(&vif->ap.csa->work);
+ } else
+ netdev_err(vif->ndev, "CSA finish indication but no active CSA");
+ } else {
+ if (ind->status == 0) {
+ ecrnx_chanctx_unlink(vif);
+ ecrnx_chanctx_link(vif, ind->chan_idx, NULL);
+ if (ecrnx_hw->cur_chanctx == ind->chan_idx) {
+ ecrnx_radar_detection_enable_on_cur_channel(ecrnx_hw);
+ ecrnx_txq_vif_start(vif, ECRNX_TXQ_STOP_CHAN, ecrnx_hw);
+ } else
+ ecrnx_txq_vif_stop(vif, ECRNX_TXQ_STOP_CHAN, ecrnx_hw);
+ }
+ }
+ }
+
+ return 0;
+}
+
+static inline int ecrnx_rx_csa_traffic_ind(struct ecrnx_hw *ecrnx_hw,
+ struct ecrnx_cmd *cmd,
+ struct ipc_e2a_msg *msg)
+{
+ struct mm_csa_traffic_ind *ind = (struct mm_csa_traffic_ind *)msg->param;
+ struct ecrnx_vif *vif;
+ bool found = false;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ // Look for VIF entry
+ list_for_each_entry(vif, &ecrnx_hw->vifs, list) {
+ if (vif->vif_index == ind->vif_index) {
+ found=true;
+ break;
+ }
+ }
+
+ if (found) {
+ if (ind->enable)
+ ecrnx_txq_vif_start(vif, ECRNX_TXQ_STOP_CSA, ecrnx_hw);
+ else
+ ecrnx_txq_vif_stop(vif, ECRNX_TXQ_STOP_CSA, ecrnx_hw);
+ }
+
+ return 0;
+}
+
+static inline int ecrnx_rx_ps_change_ind(struct ecrnx_hw *ecrnx_hw,
+ struct ecrnx_cmd *cmd,
+ struct ipc_e2a_msg *msg)
+{
+ struct mm_ps_change_ind *ind = (struct mm_ps_change_ind *)msg->param;
+ struct ecrnx_sta *sta = &ecrnx_hw->sta_table[ind->sta_idx];
+
+ if (ind->sta_idx >= (NX_REMOTE_STA_MAX + NX_VIRT_DEV_MAX)) {
+ wiphy_err(ecrnx_hw->wiphy, "Invalid sta index reported by fw %d\n",
+ ind->sta_idx);
+ return 1;
+ }
+
+ netdev_dbg(ecrnx_hw->vif_table[sta->vif_idx]->ndev,
+ "Sta %d, change PS mode to %s", sta->sta_idx,
+ ind->ps_state ? "ON" : "OFF");
+
+ ECRNX_DBG("Sta:0x%p, sta_idx:%d, sta->valid:%d, sta_mac:%pM, change PS mode to: %s \n",sta, sta->sta_idx, sta->valid, sta->mac_addr, ind->ps_state ? "ON" : "OFF");
+
+ if (sta->valid) {
+ ecrnx_ps_bh_enable(ecrnx_hw, sta, ind->ps_state);
+ } else if (test_bit(ECRNX_DEV_ADDING_STA, &ecrnx_hw->flags)) {
+ sta->ps.active = ind->ps_state ? true : false;
+ } else {
+ netdev_err(ecrnx_hw->vif_table[sta->vif_idx]->ndev,
+ "Ignore PS mode change on invalid sta\n");
+ }
+
+ return 0;
+}
+
+
+static inline int ecrnx_rx_traffic_req_ind(struct ecrnx_hw *ecrnx_hw,
+ struct ecrnx_cmd *cmd,
+ struct ipc_e2a_msg *msg)
+{
+ struct mm_traffic_req_ind *ind = (struct mm_traffic_req_ind *)msg->param;
+ struct ecrnx_sta *sta = &ecrnx_hw->sta_table[ind->sta_idx];
+
+ ECRNX_DBG("%s-%d:Sta:0x%p, sta_idx:%d, sta->valid:%d, sta_mac:%pM \n",__func__, __LINE__, sta, ind->sta_idx, sta->valid, sta->mac_addr);
+
+ netdev_dbg(ecrnx_hw->vif_table[sta->vif_idx]->ndev,
+ "Sta %d, asked for %d pkt", sta->sta_idx, ind->pkt_cnt);
+
+ ecrnx_ps_bh_traffic_req(ecrnx_hw, sta, ind->pkt_cnt,
+ ind->uapsd ? UAPSD_ID : LEGACY_PS_ID);
+
+ return 0;
+}
+#endif /* CONFIG_ECRNX_SOFTMAC */
+
+/***************************************************************************
+ * Messages from SCAN task
+ **************************************************************************/
+#ifdef CONFIG_ECRNX_SOFTMAC
+static inline int ecrnx_rx_scan_done_ind(struct ecrnx_hw *ecrnx_hw,
+ struct ecrnx_cmd *cmd,
+ struct ipc_e2a_msg *msg)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0)
+ struct cfg80211_scan_info info = {
+ .aborted = false,
+ };
+#endif
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ ecrnx_ipc_elem_var_deallocs(ecrnx_hw, &ecrnx_hw->scan_ie);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0)
+ ieee80211_scan_completed(ecrnx_hw->hw, &info);
+#else
+ ieee80211_scan_completed(ecrnx_hw->hw, false);
+#endif
+
+ return 0;
+}
+#endif /* CONFIG_ECRNX_SOFTMAC */
+
+/***************************************************************************
+ * Messages from SCANU task
+ **************************************************************************/
+#ifdef CONFIG_ECRNX_FULLMAC
+static inline int ecrnx_scanu_cancel_cfm(struct ecrnx_hw *ecrnx_hw,
+ struct ecrnx_cmd *cmd,
+ struct ipc_e2a_msg *msg)
+{
+ struct scanu_cancel_cfm *cfm = (struct scanu_cancel_cfm *)msg;
+ bool abort = false;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0)
+ struct cfg80211_scan_info info = {
+ .aborted = abort,
+ };
+#endif
+
+ ECRNX_PRINT("%s: cfm status:%d, scan_request:0x%p \n", __func__, cfm->status, ecrnx_hw->scan_request);
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0)
+ cfg80211_scan_done(ecrnx_hw->scan_request, &info);
+#else
+ cfg80211_scan_done(ecrnx_hw->scan_request, false);
+#endif
+ ecrnx_hw->scan_request = NULL;
+
+ return 0;
+}
+
+static inline int ecrnx_rx_scanu_start_cfm(struct ecrnx_hw *ecrnx_hw,
+ struct ecrnx_cmd *cmd,
+ struct ipc_e2a_msg *msg)
+{
+ struct scanu_start_cfm* cfm = (struct scanu_start_cfm*)msg->param;
+ u8_l abort_status;
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+ ECRNX_DBG("receive scanu cfm, status:%d \n", cfm->status);
+ abort_status = cfm->status?true:false;
+
+#ifndef CONFIG_ECRNX_ESWIN
+ ecrnx_ipc_elem_var_deallocs(ecrnx_hw, &ecrnx_hw->scan_ie);
+#endif
+
+ spin_lock_bh(&ecrnx_hw->scan_req_lock);
+ if (ecrnx_hw->scan_request) {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0)
+ struct cfg80211_scan_info info = {
+ .aborted = abort_status,
+ };
+
+ ECRNX_PRINT("%s: cfm status:%d, scan_request:0x%p \n", __func__, cfm->status, ecrnx_hw->scan_request);
+ cfg80211_scan_done(ecrnx_hw->scan_request, &info);
+#else
+ cfg80211_scan_done(ecrnx_hw->scan_request, abort_status);
+#endif
+ }
+
+ ecrnx_hw->scan_request = NULL;
+ spin_unlock_bh(&ecrnx_hw->scan_req_lock);
+
+ return 0;
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0)
+u64_l getBootTime(void)
+{
+ struct timespec64 ts;
+ u64_l bootTime = 0;
+
+ ts = ktime_to_timespec64(ktime_get_boottime());
+ bootTime = ts.tv_sec;
+ bootTime *= 1000000;
+ bootTime += ts.tv_nsec / 1000;
+ return bootTime;
+}
+#endif
+static inline int ecrnx_rx_scanu_result_ind(struct ecrnx_hw *ecrnx_hw,
+ struct ecrnx_cmd *cmd,
+ struct ipc_e2a_msg *msg)
+{
+ struct cfg80211_bss *bss = NULL;
+ struct ieee80211_channel *chan;
+ struct scanu_result_ind *ind = (struct scanu_result_ind *)msg->param;
+ struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)ind->payload;
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ chan = ieee80211_get_channel(ecrnx_hw->wiphy, ind->center_freq);
+
+ if (chan != NULL)
+ {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0)
+ if(ieee80211_is_beacon(mgmt->frame_control))
+ {
+ mgmt->u.beacon.timestamp = getBootTime();
+ }
+ if(ieee80211_is_probe_resp(mgmt->frame_control))
+ {
+ mgmt->u.probe_resp.timestamp = getBootTime();
+ }
+#endif
+ bss = cfg80211_inform_bss_frame(ecrnx_hw->wiphy, chan,
+ mgmt,
+ ind->length, ind->rssi * 100, GFP_ATOMIC);
+ }
+
+ if (bss != NULL)
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+ cfg80211_put_bss(ecrnx_hw->wiphy, bss);
+#else
+ cfg80211_put_bss(bss);
+#endif
+
+#if defined(CONFIG_ECRNX_DEBUGFS_CUSTOM)
+ ecrnx_debugfs_survey_info_update(ecrnx_hw, bss);
+#endif
+
+ return 0;
+}
+#endif /* CONFIG_ECRNX_FULLMAC */
+
+/***************************************************************************
+ * Messages from ME task
+ **************************************************************************/
+#ifdef CONFIG_ECRNX_FULLMAC
+static inline int ecrnx_rx_me_tkip_mic_failure_ind(struct ecrnx_hw *ecrnx_hw,
+ struct ecrnx_cmd *cmd,
+ struct ipc_e2a_msg *msg)
+{
+ struct me_tkip_mic_failure_ind *ind = (struct me_tkip_mic_failure_ind *)msg->param;
+ struct ecrnx_vif *ecrnx_vif = ecrnx_hw->vif_table[ind->vif_idx];
+ struct net_device *dev = ecrnx_vif->ndev;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ cfg80211_michael_mic_failure(dev, (u8 *)&ind->addr, (ind->ga?NL80211_KEYTYPE_GROUP:
+ NL80211_KEYTYPE_PAIRWISE), ind->keyid,
+ (u8 *)&ind->tsc, GFP_ATOMIC);
+
+ return 0;
+}
+
+static inline int ecrnx_rx_me_tx_credits_update_ind(struct ecrnx_hw *ecrnx_hw,
+ struct ecrnx_cmd *cmd,
+ struct ipc_e2a_msg *msg)
+{
+ struct me_tx_credits_update_ind *ind = (struct me_tx_credits_update_ind *)msg->param;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ ecrnx_txq_credit_update(ecrnx_hw, ind->sta_idx, ind->tid, ind->credits);
+
+ return 0;
+}
+#endif /* CONFIG_ECRNX_FULLMAC */
+
+/***************************************************************************
+ * Messages from SM task
+ **************************************************************************/
+#ifdef CONFIG_ECRNX_FULLMAC
+static inline int ecrnx_rx_sm_connect_ind(struct ecrnx_hw *ecrnx_hw,
+ struct ecrnx_cmd *cmd,
+ struct ipc_e2a_msg *msg)
+{
+ struct sm_connect_ind *ind = (struct sm_connect_ind *)msg->param;
+ struct ecrnx_vif *ecrnx_vif = ecrnx_hw->vif_table[ind->vif_idx];
+ struct net_device *dev = ecrnx_vif->ndev;
+ const u8 *req_ie, *rsp_ie;
+ const u8 *extcap_ie;
+ const struct ieee_types_extcap *extcap;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ spin_lock_bh(&ecrnx_hw->connect_req_lock);
+ /* Retrieve IE addresses and lengths */
+ req_ie = (const u8 *)ind->assoc_ie_buf;
+ rsp_ie = req_ie + ind->assoc_req_ie_len;
+
+ // Fill-in the AP information
+ if (ind->status_code == 0)
+ {
+ struct ecrnx_sta *sta = &ecrnx_hw->sta_table[ind->ap_idx];
+ u8 txq_status;
+ struct ieee80211_channel *chan;
+ struct cfg80211_chan_def chandef;
+
+ sta->valid = true;
+ memset(&sta->rx_pn, 0, TID_MAX * sizeof(uint64_t));
+ memset(&ecrnx_vif->rx_pn, 0, TID_MAX * sizeof(uint64_t));
+ sta->sta_idx = ind->ap_idx;
+ sta->ch_idx = ind->ch_idx;
+ sta->vif_idx = ind->vif_idx;
+ sta->vlan_idx = sta->vif_idx;
+ sta->qos = ind->qos;
+ sta->acm = ind->acm;
+ sta->ps.active = false;
+ sta->aid = ind->aid;
+ sta->band = ind->chan.band;
+ sta->width = ind->chan.type;
+ sta->center_freq = ind->chan.prim20_freq;
+ sta->center_freq1 = ind->chan.center1_freq;
+ sta->center_freq2 = ind->chan.center2_freq;
+ ecrnx_vif->sta.ap = sta;
+ ecrnx_vif->generation++;
+ chan = ieee80211_get_channel(ecrnx_hw->wiphy, ind->chan.prim20_freq);
+ cfg80211_chandef_create(&chandef, chan, NL80211_CHAN_NO_HT);
+ if (!ecrnx_hw->mod_params->ht_on)
+ chandef.width = NL80211_CHAN_WIDTH_20_NOHT;
+ else
+ chandef.width = chnl2bw[ind->chan.type];
+ chandef.center_freq1 = ind->chan.center1_freq;
+ chandef.center_freq2 = ind->chan.center2_freq;
+ ecrnx_chanctx_link(ecrnx_vif, ind->ch_idx, &chandef);
+ memcpy(sta->mac_addr, ind->bssid.array, ETH_ALEN);
+ if (ind->ch_idx == ecrnx_hw->cur_chanctx) {
+ txq_status = 0;
+ } else {
+ txq_status = ECRNX_TXQ_STOP_CHAN;
+ }
+ memcpy(sta->ac_param, ind->ac_param, sizeof(sta->ac_param));
+ ecrnx_txq_sta_init(ecrnx_hw, sta, txq_status);
+ ecrnx_rx_reord_sta_init(ecrnx_hw, ecrnx_hw->vif_table[sta->vif_idx], sta->sta_idx);
+ ecrnx_dbgfs_register_sta(ecrnx_hw, sta);
+ ECRNX_PRINT("ecrnx_rx_sm_connect_ind, mac[%02x:%02x:%02x:%02x:%02x:%02x], status_code:%d \n", \
+ sta->mac_addr[0], sta->mac_addr[1], sta->mac_addr[2], sta->mac_addr[3], sta->mac_addr[4], sta->mac_addr[5], ind->status_code);
+ ecrnx_txq_tdls_vif_init(ecrnx_vif);
+ ecrnx_mu_group_sta_init(sta, NULL);
+ /* Look for TDLS Channel Switch Prohibited flag in the Extended Capability
+ * Information Element*/
+ extcap_ie = cfg80211_find_ie(WLAN_EID_EXT_CAPABILITY, rsp_ie, ind->assoc_rsp_ie_len);
+ if (extcap_ie && extcap_ie[1] >= 5) {
+ extcap = (void *)(extcap_ie);
+ ecrnx_vif->tdls_chsw_prohibited = extcap->ext_capab[4] & WLAN_EXT_CAPA5_TDLS_CH_SW_PROHIBITED;
+ }
+
+#ifdef CONFIG_ECRNX_BFMER
+ /* If Beamformer feature is activated, check if features can be used
+ * with the new peer device
+ */
+ if (ecrnx_hw->mod_params->bfmer) {
+ const u8 *vht_capa_ie;
+ const struct ieee80211_vht_cap *vht_cap;
+
+ do {
+ /* Look for VHT Capability Information Element */
+ vht_capa_ie = cfg80211_find_ie(WLAN_EID_VHT_CAPABILITY, rsp_ie,
+ ind->assoc_rsp_ie_len);
+
+ /* Stop here if peer device does not support VHT */
+ if (!vht_capa_ie) {
+ break;
+ }
+
+ vht_cap = (const struct ieee80211_vht_cap *)(vht_capa_ie + 2);
+
+ /* Send MM_BFMER_ENABLE_REQ message if needed */
+ ecrnx_send_bfmer_enable(ecrnx_hw, sta, vht_cap);
+ } while (0);
+ }
+#endif //(CONFIG_ECRNX_BFMER)
+
+#ifdef CONFIG_ECRNX_MON_DATA
+ // If there are 1 sta and 1 monitor interface active at the same time then
+ // monitor interface channel context is always the same as the STA interface.
+ // This doesn't work with 2 STA interfaces but we don't want to support it.
+ if (ecrnx_hw->monitor_vif != ECRNX_INVALID_VIF) {
+ struct ecrnx_vif *ecrnx_mon_vif = ecrnx_hw->vif_table[ecrnx_hw->monitor_vif];
+ ecrnx_chanctx_unlink(ecrnx_mon_vif);
+ ecrnx_chanctx_link(ecrnx_mon_vif, ind->ch_idx, NULL);
+ }
+#endif
+ }
+
+ if (ind->roamed) {
+ struct cfg80211_roam_info info;
+ memset(&info, 0, sizeof(info));
+ if (ecrnx_vif->ch_index < NX_CHAN_CTXT_CNT)
+ info.links[0].channel = ecrnx_hw->chanctx_table[ecrnx_vif->ch_index].chan_def.chan;
+ info.links[0].bssid = (const u8 *)ind->bssid.array;
+ info.req_ie = req_ie;
+ info.req_ie_len = ind->assoc_req_ie_len;
+ info.resp_ie = rsp_ie;
+ info.resp_ie_len = ind->assoc_rsp_ie_len;
+ cfg80211_roamed(dev, &info, GFP_ATOMIC);
+ } else {
+ cfg80211_connect_result(dev, (const u8 *)ind->bssid.array, req_ie,
+ ind->assoc_req_ie_len, rsp_ie,
+ ind->assoc_rsp_ie_len, ind->status_code,
+ GFP_ATOMIC);
+ }
+
+ netif_tx_start_all_queues(dev);
+ netif_carrier_on(dev);
+ spin_unlock_bh(&ecrnx_hw->connect_req_lock);
+
+ return 0;
+}
+
+static inline int ecrnx_rx_sm_disconnect_ind(struct ecrnx_hw *ecrnx_hw,
+ struct ecrnx_cmd *cmd,
+ struct ipc_e2a_msg *msg)
+{
+ struct sm_disconnect_ind *ind = (struct sm_disconnect_ind *)msg->param;
+ struct ecrnx_vif *ecrnx_vif = ecrnx_hw->vif_table[ind->vif_idx];
+ struct net_device *dev = ecrnx_vif->ndev;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ ECRNX_PRINT("%s:dev:%p, vif_up:%d, reason_code:%d \n", __func__, dev, ecrnx_vif->up, ind->reason_code);
+ /* if vif is not up, ecrnx_close has already been called */
+ if (ecrnx_vif->up) {
+ if (!ind->reassoc) {
+ cfg80211_disconnected(dev, ind->reason_code, NULL, 0,
+ (ind->reason_code <= 1), GFP_ATOMIC);
+ if (ecrnx_vif->sta.ft_assoc_ies) {
+ kfree(ecrnx_vif->sta.ft_assoc_ies);
+ ecrnx_vif->sta.ft_assoc_ies = NULL;
+ ecrnx_vif->sta.ft_assoc_ies_len = 0;
+ }
+ }
+ netif_tx_stop_all_queues(dev);
+ netif_carrier_off(dev);
+ }
+
+ if (ecrnx_vif->sta.ap && ecrnx_vif->sta.ap->valid)
+ {
+ ecrnx_dbgfs_unregister_sta(ecrnx_hw, ecrnx_vif->sta.ap);
+#ifdef CONFIG_ECRNX_BFMER
+ /* Disable Beamformer if supported */
+ ecrnx_bfmer_report_del(ecrnx_hw, ecrnx_vif->sta.ap);
+#endif //(CONFIG_ECRNX_BFMER)
+
+ ecrnx_txq_sta_deinit(ecrnx_hw, ecrnx_vif->sta.ap);
+ ecrnx_rx_reord_sta_deinit(ecrnx_hw, ecrnx_vif->sta.ap->sta_idx, true);
+ ecrnx_vif->sta.ap->valid = false;
+ ecrnx_vif->sta.ap = NULL;
+ }
+ ecrnx_txq_tdls_vif_deinit(ecrnx_vif);
+ //ecrnx_dbgfs_unregister_sta(ecrnx_hw, ecrnx_vif->sta.ap);
+ ecrnx_vif->generation++;
+ ecrnx_external_auth_disable(ecrnx_vif);
+ ecrnx_chanctx_unlink(ecrnx_vif);
+
+ return 0;
+}
+
+static inline int ecrnx_rx_sm_external_auth_required_ind(struct ecrnx_hw *ecrnx_hw,
+ struct ecrnx_cmd *cmd,
+ struct ipc_e2a_msg *msg)
+{
+ struct sm_external_auth_required_ind *ind =
+ (struct sm_external_auth_required_ind *)msg->param;
+ struct ecrnx_vif *ecrnx_vif = ecrnx_hw->vif_table[ind->vif_idx];
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0)
+ struct net_device *dev = ecrnx_vif->ndev;
+ struct cfg80211_external_auth_params params;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ params.action = NL80211_EXTERNAL_AUTH_START;
+ memcpy(params.bssid, ind->bssid.array, ETH_ALEN);
+ params.ssid.ssid_len = ind->ssid.length;
+ memcpy(params.ssid.ssid, ind->ssid.array,
+ min_t(size_t, ind->ssid.length, sizeof(params.ssid.ssid)));
+ params.key_mgmt_suite = ind->akm;
+
+ if ((ind->vif_idx > NX_VIRT_DEV_MAX) || !ecrnx_vif->up ||
+ (ECRNX_VIF_TYPE(ecrnx_vif) != NL80211_IFTYPE_STATION) ||
+ cfg80211_external_auth_request(dev, &params, GFP_ATOMIC)) {
+ wiphy_err(ecrnx_hw->wiphy, "Failed to start external auth on vif %d",
+ ind->vif_idx);
+ ecrnx_send_sm_external_auth_required_rsp(ecrnx_hw, ecrnx_vif,
+ WLAN_STATUS_UNSPECIFIED_FAILURE);
+ return 0;
+ }
+
+ ecrnx_external_auth_enable(ecrnx_vif);
+#else
+ ecrnx_send_sm_external_auth_required_rsp(ecrnx_hw, ecrnx_vif,
+ WLAN_STATUS_UNSPECIFIED_FAILURE);
+#endif
+ return 0;
+}
+
+static inline int ecrnx_rx_sm_ft_auth_ind(struct ecrnx_hw *ecrnx_hw,
+ struct ecrnx_cmd *cmd,
+ struct ipc_e2a_msg *msg)
+{
+ struct sm_ft_auth_ind *ind = (struct sm_ft_auth_ind *)msg->param;
+ struct ecrnx_vif *ecrnx_vif = ecrnx_hw->vif_table[ind->vif_idx];
+ struct sk_buff *skb;
+ size_t data_len = (offsetof(struct ieee80211_mgmt, u.auth.variable) +
+ ind->ft_ie_len);
+ skb = dev_alloc_skb(data_len);
+ if (skb) {
+ struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb_put(skb, data_len);
+ mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_AUTH);
+ memcpy(mgmt->u.auth.variable, ind->ft_ie_buf, ind->ft_ie_len);
+ ecrnx_rx_defer_skb(ecrnx_hw, ecrnx_vif, skb);
+ dev_kfree_skb(skb);
+ } else {
+ netdev_warn(ecrnx_vif->ndev, "Allocation failed for FT auth ind\n");
+ }
+ return 0;
+}
+static inline int ecrnx_rx_twt_setup_ind(struct ecrnx_hw *ecrnx_hw,
+ struct ecrnx_cmd *cmd,
+ struct ipc_e2a_msg *msg)
+{
+ struct twt_setup_ind *ind = (struct twt_setup_ind *)msg->param;
+ struct ecrnx_sta *ecrnx_sta = &ecrnx_hw->sta_table[ind->sta_idx];
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+ memcpy(&ecrnx_sta->twt_ind, ind, sizeof(struct twt_setup_ind));
+ return 0;
+}
+
+static inline int ecrnx_rx_mesh_path_create_cfm(struct ecrnx_hw *ecrnx_hw,
+ struct ecrnx_cmd *cmd,
+ struct ipc_e2a_msg *msg)
+{
+ struct mesh_path_create_cfm *cfm = (struct mesh_path_create_cfm *)msg->param;
+ struct ecrnx_vif *ecrnx_vif = ecrnx_hw->vif_table[cfm->vif_idx];
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ /* Check we well have a Mesh Point Interface */
+ if (ecrnx_vif && (ECRNX_VIF_TYPE(ecrnx_vif) == NL80211_IFTYPE_MESH_POINT))
+ ecrnx_vif->ap.flags &= ~ECRNX_AP_CREATE_MESH_PATH;
+
+ return 0;
+}
+
+static inline int ecrnx_rx_mesh_peer_update_ind(struct ecrnx_hw *ecrnx_hw,
+ struct ecrnx_cmd *cmd,
+ struct ipc_e2a_msg *msg)
+{
+ struct mesh_peer_update_ind *ind = (struct mesh_peer_update_ind *)msg->param;
+ struct ecrnx_vif *ecrnx_vif = ecrnx_hw->vif_table[ind->vif_idx];
+ struct ecrnx_sta *ecrnx_sta = &ecrnx_hw->sta_table[ind->sta_idx];
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ if ((ind->vif_idx >= (NX_VIRT_DEV_MAX + NX_REMOTE_STA_MAX)) ||
+ (ecrnx_vif && (ECRNX_VIF_TYPE(ecrnx_vif) != NL80211_IFTYPE_MESH_POINT)) ||
+ (ind->sta_idx >= NX_REMOTE_STA_MAX))
+ return 1;
+
+ if (ecrnx_vif->ap.flags & ECRNX_AP_USER_MESH_PM)
+ {
+ if (!ind->estab && ecrnx_sta->valid) {
+ ecrnx_sta->ps.active = false;
+ ecrnx_sta->valid = false;
+ list_del_init(&ecrnx_sta->list);
+ ecrnx_txq_sta_deinit(ecrnx_hw, ecrnx_sta);
+ ecrnx_dbgfs_unregister_sta(ecrnx_hw, ecrnx_sta);
+ } else {
+ WARN_ON(0);
+ }
+ } else {
+ /* Check if peer link has been established or lost */
+ if (ind->estab) {
+ if (!ecrnx_sta->valid) {
+ u8 txq_status;
+
+ ecrnx_sta->valid = true;
+ ecrnx_sta->sta_idx = ind->sta_idx;
+ ecrnx_sta->ch_idx = ecrnx_vif->ch_index;
+ ecrnx_sta->vif_idx = ind->vif_idx;
+ ecrnx_sta->vlan_idx = ecrnx_sta->vif_idx;
+ ecrnx_sta->ps.active = false;
+ ecrnx_sta->qos = true;
+ ecrnx_sta->aid = ind->sta_idx + 1;
+ //ecrnx_sta->acm = ind->acm;
+ memcpy(ecrnx_sta->mac_addr, ind->peer_addr.array, ETH_ALEN);
+
+ ecrnx_chanctx_link(ecrnx_vif, ecrnx_sta->ch_idx, NULL);
+
+ /* Add the station in the list of VIF's stations */
+ INIT_LIST_HEAD(&ecrnx_sta->list);
+ list_add_tail(&ecrnx_sta->list, &ecrnx_vif->ap.sta_list);
+
+ /* Initialize the TX queues */
+ if (ecrnx_sta->ch_idx == ecrnx_hw->cur_chanctx) {
+ txq_status = 0;
+ } else {
+ txq_status = ECRNX_TXQ_STOP_CHAN;
+ }
+
+ ecrnx_txq_sta_init(ecrnx_hw, ecrnx_sta, txq_status);
+ ecrnx_dbgfs_register_sta(ecrnx_hw, ecrnx_sta);
+
+#ifdef CONFIG_ECRNX_BFMER
+ // TODO: update indication to contains vht capabilties
+ if (ecrnx_hw->mod_params->bfmer)
+ ecrnx_send_bfmer_enable(ecrnx_hw, ecrnx_sta, NULL);
+
+ ecrnx_mu_group_sta_init(ecrnx_sta, NULL);
+#endif /* CONFIG_ECRNX_BFMER */
+
+ } else {
+ WARN_ON(0);
+ }
+ } else {
+ if (ecrnx_sta->valid) {
+ ecrnx_sta->ps.active = false;
+ ecrnx_sta->valid = false;
+
+ /* Remove the station from the list of VIF's station */
+ list_del_init(&ecrnx_sta->list);
+
+ ecrnx_txq_sta_deinit(ecrnx_hw, ecrnx_sta);
+ ecrnx_dbgfs_unregister_sta(ecrnx_hw, ecrnx_sta);
+ } else {
+ WARN_ON(0);
+ }
+ /* There is no way to inform upper layer for lost of peer, still
+ clean everything in the driver */
+
+ /* Remove the station from the list of VIF's station */
+
+ }
+ }
+
+ return 0;
+}
+
+static inline int ecrnx_rx_mesh_path_update_ind(struct ecrnx_hw *ecrnx_hw,
+ struct ecrnx_cmd *cmd,
+ struct ipc_e2a_msg *msg)
+{
+ struct mesh_path_update_ind *ind = (struct mesh_path_update_ind *)msg->param;
+ struct ecrnx_vif *ecrnx_vif = ecrnx_hw->vif_table[ind->vif_idx];
+ struct ecrnx_mesh_path *mesh_path;
+ bool found = false;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ if (ind->vif_idx >= (NX_VIRT_DEV_MAX + NX_REMOTE_STA_MAX))
+ return 1;
+
+ if (!ecrnx_vif || (ECRNX_VIF_TYPE(ecrnx_vif) != NL80211_IFTYPE_MESH_POINT))
+ return 0;
+
+ /* Look for path with provided target address */
+ list_for_each_entry(mesh_path, &ecrnx_vif->ap.mpath_list, list) {
+ if (mesh_path->path_idx == ind->path_idx) {
+ found = true;
+ break;
+ }
+ }
+
+ /* Check if element has been deleted */
+ if (ind->delete) {
+ if (found) {
+ trace_mesh_delete_path(mesh_path);
+ /* Remove element from list */
+ list_del_init(&mesh_path->list);
+ /* Free the element */
+ kfree(mesh_path);
+ }
+ }
+ else {
+ if (found) {
+ // Update the Next Hop STA
+ mesh_path->nhop_sta = &ecrnx_hw->sta_table[ind->nhop_sta_idx];
+ trace_mesh_update_path(mesh_path);
+ } else {
+ // Allocate a Mesh Path structure
+ mesh_path = kmalloc(sizeof(struct ecrnx_mesh_path), GFP_ATOMIC);
+
+ if (mesh_path) {
+ INIT_LIST_HEAD(&mesh_path->list);
+
+ mesh_path->path_idx = ind->path_idx;
+ mesh_path->nhop_sta = &ecrnx_hw->sta_table[ind->nhop_sta_idx];
+ memcpy(&mesh_path->tgt_mac_addr, &ind->tgt_mac_addr, MAC_ADDR_LEN);
+
+ // Insert the path in the list of path
+ list_add_tail(&mesh_path->list, &ecrnx_vif->ap.mpath_list);
+
+ trace_mesh_create_path(mesh_path);
+ }
+ }
+ }
+
+ return 0;
+}
+
+static inline int ecrnx_rx_mesh_proxy_update_ind(struct ecrnx_hw *ecrnx_hw,
+ struct ecrnx_cmd *cmd,
+ struct ipc_e2a_msg *msg)
+{
+ struct mesh_proxy_update_ind *ind = (struct mesh_proxy_update_ind *)msg->param;
+ struct ecrnx_vif *ecrnx_vif = ecrnx_hw->vif_table[ind->vif_idx];
+ struct ecrnx_mesh_proxy *mesh_proxy;
+ bool found = false;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ if (ind->vif_idx >= (NX_VIRT_DEV_MAX + NX_REMOTE_STA_MAX))
+ return 1;
+
+ if (!ecrnx_vif || (ECRNX_VIF_TYPE(ecrnx_vif) != NL80211_IFTYPE_MESH_POINT))
+ return 0;
+
+ /* Look for path with provided external STA address */
+ list_for_each_entry(mesh_proxy, &ecrnx_vif->ap.proxy_list, list) {
+ if (!memcmp(&ind->ext_sta_addr, &mesh_proxy->ext_sta_addr, ETH_ALEN)) {
+ found = true;
+ break;
+ }
+ }
+
+ if (ind->delete && found) {
+ /* Delete mesh path */
+ list_del_init(&mesh_proxy->list);
+ kfree(mesh_proxy);
+ } else if (!ind->delete && !found) {
+ /* Allocate a Mesh Path structure */
+ mesh_proxy = (struct ecrnx_mesh_proxy *)kmalloc(sizeof(*mesh_proxy),
+ GFP_ATOMIC);
+
+ if (mesh_proxy) {
+ INIT_LIST_HEAD(&mesh_proxy->list);
+
+ memcpy(&mesh_proxy->ext_sta_addr, &ind->ext_sta_addr, MAC_ADDR_LEN);
+ mesh_proxy->local = ind->local;
+
+ if (!ind->local) {
+ memcpy(&mesh_proxy->proxy_addr, &ind->proxy_mac_addr, MAC_ADDR_LEN);
+ }
+
+ /* Insert the path in the list of path */
+ list_add_tail(&mesh_proxy->list, &ecrnx_vif->ap.proxy_list);
+ }
+ }
+
+ return 0;
+}
+
+/***************************************************************************
+ * Messages from APM task
+ **************************************************************************/
+static inline int ecrnx_rx_apm_probe_client_ind(struct ecrnx_hw *ecrnx_hw,
+ struct ecrnx_cmd *cmd,
+ struct ipc_e2a_msg *msg)
+{
+ struct apm_probe_client_ind *ind = (struct apm_probe_client_ind *)msg->param;
+ struct ecrnx_vif *ecrnx_vif = ecrnx_hw->vif_table[ind->vif_idx];
+ struct ecrnx_sta *ecrnx_sta = &ecrnx_hw->sta_table[ind->sta_idx];
+
+ ecrnx_sta->stats.last_act = jiffies;
+ cfg80211_probe_status(ecrnx_vif->ndev, ecrnx_sta->mac_addr, (u64)ind->probe_id,
+ ind->client_present, 0, false, GFP_ATOMIC);
+ return 0;
+}
+#endif /* CONFIG_ECRNX_FULLMAC */
+
+/***************************************************************************
+ * Messages from DEBUG task
+ **************************************************************************/
+static inline int ecrnx_rx_dbg_error_ind(struct ecrnx_hw *ecrnx_hw,
+ struct ecrnx_cmd *cmd,
+ struct ipc_e2a_msg *msg)
+{
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ ecrnx_error_ind(ecrnx_hw);
+
+ return 0;
+}
+
+
+
+
+#ifdef CONFIG_ECRNX_SOFTMAC
+
+static msg_cb_fct mm_hdlrs[MSG_I(MM_MAX)] = {
+ [MSG_I(MM_CONNECTION_LOSS_IND)] = ecrnx_rx_connection_loss_ind,
+ [MSG_I(MM_CHANNEL_SWITCH_IND)] = ecrnx_rx_chan_switch_ind,
+ [MSG_I(MM_CHANNEL_PRE_SWITCH_IND)] = ecrnx_rx_chan_pre_switch_ind,
+ [MSG_I(MM_REMAIN_ON_CHANNEL_EXP_IND)] = ecrnx_rx_remain_on_channel_exp_ind,
+#ifdef CONFIG_ECRNX_BCN
+ [MSG_I(MM_PRIMARY_TBTT_IND)] = ecrnx_rx_prm_tbtt_ind,
+#endif
+ [MSG_I(MM_P2P_VIF_PS_CHANGE_IND)] = ecrnx_rx_p2p_vif_ps_change_ind,
+ [MSG_I(MM_CSA_COUNTER_IND)] = ecrnx_rx_csa_counter_ind,
+ [MSG_I(MM_CHANNEL_SURVEY_IND)] = ecrnx_rx_channel_survey_ind,
+ [MSG_I(MM_RSSI_STATUS_IND)] = ecrnx_rx_rssi_status_ind,
+};
+
+static msg_cb_fct scan_hdlrs[MSG_I(SCAN_MAX)] = {
+ [MSG_I(SCAN_DONE_IND)] = ecrnx_rx_scan_done_ind,
+};
+
+#else /* CONFIG_ECRNX_FULLMAC */
+
+static msg_cb_fct mm_hdlrs[MSG_I(MM_MAX)] = {
+ [MSG_I(MM_CHANNEL_SWITCH_IND)] = ecrnx_rx_chan_switch_ind,
+ [MSG_I(MM_CHANNEL_PRE_SWITCH_IND)] = ecrnx_rx_chan_pre_switch_ind,
+ [MSG_I(MM_REMAIN_ON_CHANNEL_EXP_IND)] = ecrnx_rx_remain_on_channel_exp_ind,
+ [MSG_I(MM_PS_CHANGE_IND)] = ecrnx_rx_ps_change_ind,
+ [MSG_I(MM_TRAFFIC_REQ_IND)] = ecrnx_rx_traffic_req_ind,
+ [MSG_I(MM_P2P_VIF_PS_CHANGE_IND)] = ecrnx_rx_p2p_vif_ps_change_ind,
+ [MSG_I(MM_CSA_COUNTER_IND)] = ecrnx_rx_csa_counter_ind,
+ [MSG_I(MM_CSA_FINISH_IND)] = ecrnx_rx_csa_finish_ind,
+ [MSG_I(MM_CSA_TRAFFIC_IND)] = ecrnx_rx_csa_traffic_ind,
+ [MSG_I(MM_CHANNEL_SURVEY_IND)] = ecrnx_rx_channel_survey_ind,
+ [MSG_I(MM_P2P_NOA_UPD_IND)] = ecrnx_rx_p2p_noa_upd_ind,
+ [MSG_I(MM_RSSI_STATUS_IND)] = ecrnx_rx_rssi_status_ind,
+ [MSG_I(MM_PKTLOSS_IND)] = ecrnx_rx_pktloss_notify_ind,
+};
+
+static msg_cb_fct scan_hdlrs[MSG_I(SCANU_MAX)] = {
+ [MSG_I(SCANU_START_CFM)] = ecrnx_rx_scanu_start_cfm,
+ [MSG_I(SCANU_RESULT_IND)] = ecrnx_rx_scanu_result_ind,
+ [MSG_I(SCANU_CANCEL_CFM)] = ecrnx_scanu_cancel_cfm,
+};
+
+static msg_cb_fct me_hdlrs[MSG_I(ME_MAX)] = {
+ [MSG_I(ME_TKIP_MIC_FAILURE_IND)] = ecrnx_rx_me_tkip_mic_failure_ind,
+ [MSG_I(ME_TX_CREDITS_UPDATE_IND)] = ecrnx_rx_me_tx_credits_update_ind,
+};
+
+static msg_cb_fct sm_hdlrs[MSG_I(SM_MAX)] = {
+ [MSG_I(SM_CONNECT_IND)] = ecrnx_rx_sm_connect_ind,
+ [MSG_I(SM_DISCONNECT_IND)] = ecrnx_rx_sm_disconnect_ind,
+ [MSG_I(SM_EXTERNAL_AUTH_REQUIRED_IND)] = ecrnx_rx_sm_external_auth_required_ind,
+ [MSG_I(SM_FT_AUTH_IND)] = ecrnx_rx_sm_ft_auth_ind,
+};
+
+static msg_cb_fct apm_hdlrs[MSG_I(APM_MAX)] = {
+ [MSG_I(APM_PROBE_CLIENT_IND)] = ecrnx_rx_apm_probe_client_ind,
+};
+static msg_cb_fct twt_hdlrs[MSG_I(TWT_MAX)] = {
+ [MSG_I(TWT_SETUP_IND)] = ecrnx_rx_twt_setup_ind,
+};
+
+static msg_cb_fct mesh_hdlrs[MSG_I(MESH_MAX)] = {
+ [MSG_I(MESH_PATH_CREATE_CFM)] = ecrnx_rx_mesh_path_create_cfm,
+ [MSG_I(MESH_PEER_UPDATE_IND)] = ecrnx_rx_mesh_peer_update_ind,
+ [MSG_I(MESH_PATH_UPDATE_IND)] = ecrnx_rx_mesh_path_update_ind,
+ [MSG_I(MESH_PROXY_UPDATE_IND)] = ecrnx_rx_mesh_proxy_update_ind,
+};
+
+
+#endif /* CONFIG_ECRNX_SOFTMAC */
+
+static msg_cb_fct dbg_hdlrs[MSG_I(DBG_MAX)] = {
+ [MSG_I(DBG_ERROR_IND)] = ecrnx_rx_dbg_error_ind,
+};
+
+static msg_cb_fct tdls_hdlrs[MSG_I(TDLS_MAX)] = {
+ [MSG_I(TDLS_CHAN_SWITCH_CFM)] = ecrnx_rx_tdls_chan_switch_cfm,
+ [MSG_I(TDLS_CHAN_SWITCH_IND)] = ecrnx_rx_tdls_chan_switch_ind,
+ [MSG_I(TDLS_CHAN_SWITCH_BASE_IND)] = ecrnx_rx_tdls_chan_switch_base_ind,
+ [MSG_I(TDLS_PEER_PS_IND)] = ecrnx_rx_tdls_peer_ps_ind,
+};
+
+static msg_cb_fct *msg_hdlrs[] = {
+ [TASK_MM] = mm_hdlrs,
+ [TASK_DBG] = dbg_hdlrs,
+#ifdef CONFIG_ECRNX_SOFTMAC
+ [TASK_SCAN] = scan_hdlrs,
+ [TASK_TDLS] = tdls_hdlrs,
+#else
+ [TASK_TDLS] = tdls_hdlrs,
+ [TASK_SCANU] = scan_hdlrs,
+ [TASK_ME] = me_hdlrs,
+ [TASK_SM] = sm_hdlrs,
+ [TASK_APM] = apm_hdlrs,
+ [TASK_MESH] = mesh_hdlrs,
+ [TASK_TWT] = twt_hdlrs,
+#if 0//(CONFIG_ECRNX_P2P)
+ [TASK_P2P_LISTEN] = p2p_listen_hdlrs,
+#endif
+#endif /* CONFIG_ECRNX_SOFTMAC */
+};
+
+/**
+ *
+ */
+void ecrnx_rx_handle_msg(struct ecrnx_hw *ecrnx_hw, struct ipc_e2a_msg *msg)
+{
+
+ if(!ecrnx_hw || !msg || !(&ecrnx_hw->cmd_mgr))
+ {
+ ECRNX_ERR("ecrnx_rx_handle_msg:receive msg info error \n");
+ return;
+ }
+
+#if defined(CONFIG_ECRNX_P2P)
+ if (MSG_T(msg->id) != TASK_P2P_LISTEN && (MSG_T(msg->id) < TASK_MM || MSG_T(msg->id) > TASK_MESH || MSG_I(msg->id) >= MSG_I(MM_MAX)))
+#else
+ if (MSG_T(msg->id) < TASK_MM || MSG_T(msg->id) > TASK_MESH || MSG_I(msg->id) >= MSG_I(MM_MAX))
+#endif
+ {
+ ECRNX_ERR("msg id 0x%x,%d,%d, max %d, dst:0x%x, src:0x%x\n", msg->id, MSG_T(msg->id), MSG_I(msg->id), MSG_I(MM_MAX), msg->dummy_dest_id, msg->dummy_src_id);
+ ECRNX_ERR("skip msg %p\n", msg);
+ return;
+ }
+
+ if(ecrnx_hw->wiphy != NULL)
+ {
+ ecrnx_hw->cmd_mgr.msgind(&ecrnx_hw->cmd_mgr, msg,
+ msg_hdlrs[MSG_T(msg->id)][MSG_I(msg->id)]);
+ }
+}
diff --git a/drivers/net/wireless/eswin/ecrnx_msg_rx.h b/drivers/net/wireless/eswin/ecrnx_msg_rx.h
new file mode 100644
index 000000000000..66fad20cc406
--- /dev/null
+++ b/drivers/net/wireless/eswin/ecrnx_msg_rx.h
@@ -0,0 +1,18 @@
+/**
+ ****************************************************************************************
+ *
+ * @file ecrnx_msg_rx.h
+ *
+ * @brief RX function declarations
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ****************************************************************************************
+ */
+
+#ifndef _ECRNX_MSG_RX_H_
+#define _ECRNX_MSG_RX_H_
+
+void ecrnx_rx_handle_msg(struct ecrnx_hw *ecrnx_hw, struct ipc_e2a_msg *msg);
+
+#endif /* _ECRNX_MSG_RX_H_ */
diff --git a/drivers/net/wireless/eswin/ecrnx_msg_tx.c b/drivers/net/wireless/eswin/ecrnx_msg_tx.c
new file mode 100644
index 000000000000..8e1b97a13a18
--- /dev/null
+++ b/drivers/net/wireless/eswin/ecrnx_msg_tx.c
@@ -0,0 +1,3173 @@
+/**
+ ******************************************************************************
+ *
+ * @file ecrnx_msg_tx.c
+ *
+ * @brief TX function definitions
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+
+#include "ecrnx_msg_tx.h"
+#include "ecrnx_mod_params.h"
+#include "reg_access.h"
+#ifdef CONFIG_ECRNX_BFMER
+#include "ecrnx_bfmer.h"
+#endif //(CONFIG_ECRNX_BFMER)
+#include "ecrnx_compat.h"
+#include "ecrnx_defs.h"
+#include "ecrnx_calibration_data.h"
+#include "eswin_utils.h"
+#include "core.h"
+
+const struct mac_addr mac_addr_bcst = {{0xFFFF, 0xFFFF, 0xFFFF}};
+
+/* Default MAC Rx filters that can be changed by mac80211
+ * (via the configure_filter() callback) */
+#define ECRNX_MAC80211_CHANGEABLE ( \
+ NXMAC_ACCEPT_BA_BIT | \
+ NXMAC_ACCEPT_BAR_BIT | \
+ NXMAC_ACCEPT_OTHER_DATA_FRAMES_BIT | \
+ NXMAC_ACCEPT_PROBE_REQ_BIT | \
+ NXMAC_ACCEPT_PS_POLL_BIT \
+ )
+
+/* Default MAC Rx filters that cannot be changed by mac80211 */
+#define ECRNX_MAC80211_NOT_CHANGEABLE ( \
+ NXMAC_ACCEPT_QO_S_NULL_BIT | \
+ NXMAC_ACCEPT_Q_DATA_BIT | \
+ NXMAC_ACCEPT_DATA_BIT | \
+ NXMAC_ACCEPT_OTHER_MGMT_FRAMES_BIT | \
+ NXMAC_ACCEPT_MY_UNICAST_BIT | \
+ NXMAC_ACCEPT_BROADCAST_BIT | \
+ NXMAC_ACCEPT_BEACON_BIT | \
+ NXMAC_ACCEPT_PROBE_RESP_BIT \
+ )
+
+/* Default MAC Rx filter */
+#define ECRNX_DEFAULT_RX_FILTER (ECRNX_MAC80211_CHANGEABLE | ECRNX_MAC80211_NOT_CHANGEABLE)
+
+const int bw2chnl[] = {
+ [NL80211_CHAN_WIDTH_20_NOHT] = PHY_CHNL_BW_20,
+ [NL80211_CHAN_WIDTH_20] = PHY_CHNL_BW_20,
+ [NL80211_CHAN_WIDTH_40] = PHY_CHNL_BW_40,
+ [NL80211_CHAN_WIDTH_80] = PHY_CHNL_BW_80,
+ [NL80211_CHAN_WIDTH_160] = PHY_CHNL_BW_160,
+ [NL80211_CHAN_WIDTH_80P80] = PHY_CHNL_BW_80P80,
+};
+
+const int chnl2bw[] = {
+ [PHY_CHNL_BW_20] = NL80211_CHAN_WIDTH_20,
+ [PHY_CHNL_BW_40] = NL80211_CHAN_WIDTH_40,
+ [PHY_CHNL_BW_80] = NL80211_CHAN_WIDTH_80,
+ [PHY_CHNL_BW_160] = NL80211_CHAN_WIDTH_160,
+ [PHY_CHNL_BW_80P80] = NL80211_CHAN_WIDTH_80P80,
+};
+
+/*****************************************************************************/
+/*
+ * Parse the ampdu density to retrieve the value in usec, according to the
+ * values defined in ieee80211.h
+ */
+static inline u8 ecrnx_ampdudensity2usec(u8 ampdudensity)
+{
+ switch (ampdudensity) {
+ case IEEE80211_HT_MPDU_DENSITY_NONE:
+ return 0;
+ /* 1 microsecond is our granularity */
+ case IEEE80211_HT_MPDU_DENSITY_0_25:
+ case IEEE80211_HT_MPDU_DENSITY_0_5:
+ case IEEE80211_HT_MPDU_DENSITY_1:
+ return 1;
+ case IEEE80211_HT_MPDU_DENSITY_2:
+ return 2;
+ case IEEE80211_HT_MPDU_DENSITY_4:
+ return 4;
+ case IEEE80211_HT_MPDU_DENSITY_8:
+ return 8;
+ case IEEE80211_HT_MPDU_DENSITY_16:
+ return 16;
+ default:
+ return 0;
+ }
+}
+
+static inline bool use_pairwise_key(struct cfg80211_crypto_settings *crypto)
+{
+ if ((crypto->cipher_group == WLAN_CIPHER_SUITE_WEP40) ||
+ (crypto->cipher_group == WLAN_CIPHER_SUITE_WEP104))
+ return false;
+
+ return true;
+}
+
+static inline bool is_non_blocking_msg(int id)
+{
+ return ((id == MM_TIM_UPDATE_REQ) || (id == ME_RC_SET_RATE_REQ) ||
+ (id == MM_BFMER_ENABLE_REQ) || (id == ME_TRAFFIC_IND_REQ) ||
+ (id == TDLS_PEER_TRAFFIC_IND_REQ) ||
+ (id == MESH_PATH_CREATE_REQ) || (id == MESH_PROXY_ADD_REQ) ||
+ (id == SM_EXTERNAL_AUTH_REQUIRED_RSP));
+}
+
+#ifdef CONFIG_ECRNX_FULLMAC
+/**
+ * copy_connect_ies -- Copy Association Elements in the the request buffer
+ * send to the firmware
+ *
+ * @vif: Vif that received the connection request
+ * @req: Connection request to send to the firmware
+ * @sme: Connection info
+ *
+ * For driver that do not use userspace SME (like this one) the host connection
+ * request doesn't explicitly mentions that the connection can use FT over the
+ * air. if FT is possible, send the FT elements (as received in update_ft_ies callback)
+ * to the firmware
+ *
+ * In all other cases simply copy the list povided by the user space in the
+ * request buffer
+ */
+static void copy_connect_ies(struct ecrnx_vif *vif, struct sm_connect_req *req,
+ struct cfg80211_connect_params *sme)
+{
+ if ((sme->auth_type == NL80211_AUTHTYPE_FT) && !(vif->sta.flags & ECRNX_STA_FT_OVER_DS))
+ {
+ const struct ecrnx_element *rsne, *fte, *mde;
+ uint8_t *pos;
+ rsne = cfg80211_find_ecrnx_elem(WLAN_EID_RSN, vif->sta.ft_assoc_ies,
+ vif->sta.ft_assoc_ies_len);
+ fte = cfg80211_find_ecrnx_elem(WLAN_EID_FAST_BSS_TRANSITION, vif->sta.ft_assoc_ies,
+ vif->sta.ft_assoc_ies_len);
+ mde = cfg80211_find_ecrnx_elem(WLAN_EID_MOBILITY_DOMAIN,
+ vif->sta.ft_assoc_ies, vif->sta.ft_assoc_ies_len);
+ pos = (uint8_t *)req->ie_buf;
+
+ // We can use FT over the air
+ memcpy(&vif->sta.ft_target_ap, sme->bssid, ETH_ALEN);
+
+ if (rsne) {
+ memcpy(pos, rsne, sizeof(struct ecrnx_element) + rsne->datalen);
+ pos += sizeof(struct ecrnx_element) + rsne->datalen;
+ }
+ memcpy(pos, mde, sizeof(struct ecrnx_element) + mde->datalen);
+ pos += sizeof(struct ecrnx_element) + mde->datalen;
+ if (fte) {
+ memcpy(pos, fte, sizeof(struct ecrnx_element) + fte->datalen);
+ pos += sizeof(struct ecrnx_element) + fte->datalen;
+ }
+
+ req->ie_len = pos - (uint8_t *)req->ie_buf;
+ }
+ else
+ {
+ memcpy(req->ie_buf, sme->ie, sme->ie_len);
+ req->ie_len = sme->ie_len;
+ }
+}
+
+/**
+ * update_connect_req -- Return the length of the association request IEs
+ *
+ * @vif: Vif that received the connection request
+ * @sme: Connection info
+ *
+ * Return the ft_ie_len in case of FT.
+ * FT over the air is possible if:
+ * - auth_type = AUTOMATIC (if already set to FT then it means FT over DS)
+ * - already associated to a FT BSS
+ * - Target Mobility domain is the same as the curent one
+ *
+ * If FT is not possible return ie length of the connection info
+ */
+static int update_connect_req(struct ecrnx_vif *vif, struct cfg80211_connect_params *sme)
+{
+ if ((vif->sta.ap) &&
+ (vif->sta.ft_assoc_ies) &&
+ (sme->auth_type == NL80211_AUTHTYPE_AUTOMATIC))
+ {
+ const struct ecrnx_element *rsne, *fte, *mde, *mde_req;
+ int ft_ie_len = 0;
+
+ mde_req = cfg80211_find_ecrnx_elem(WLAN_EID_MOBILITY_DOMAIN,
+ sme->ie, sme->ie_len);
+ mde = cfg80211_find_ecrnx_elem(WLAN_EID_MOBILITY_DOMAIN,
+ vif->sta.ft_assoc_ies, vif->sta.ft_assoc_ies_len);
+ if (!mde || !mde_req ||
+ memcmp(mde, mde_req, sizeof(struct ecrnx_element) + mde->datalen))
+ {
+ return sme->ie_len;
+ }
+
+ ft_ie_len += sizeof(struct ecrnx_element) + mde->datalen;
+
+ rsne = cfg80211_find_ecrnx_elem(WLAN_EID_RSN, vif->sta.ft_assoc_ies,
+ vif->sta.ft_assoc_ies_len);
+ fte = cfg80211_find_ecrnx_elem(WLAN_EID_FAST_BSS_TRANSITION, vif->sta.ft_assoc_ies,
+ vif->sta.ft_assoc_ies_len);
+
+ if (rsne && fte)
+ {
+ ft_ie_len += 2 * sizeof(struct ecrnx_element) + rsne->datalen + fte->datalen;
+ sme->auth_type = NL80211_AUTHTYPE_FT;
+ return ft_ie_len;
+ }
+ else if (rsne || fte)
+ {
+ netdev_warn(vif->ndev, "Missing RSNE or FTE element, skip FT over air");
+ }
+ else
+ {
+ sme->auth_type = NL80211_AUTHTYPE_FT;
+ return ft_ie_len;
+ }
+ }
+ return sme->ie_len;
+}
+#endif
+static inline u8_l get_chan_flags(uint32_t flags)
+{
+ u8_l chan_flags = 0;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 14, 0)
+ if (flags & IEEE80211_CHAN_PASSIVE_SCAN)
+#else
+ if (flags & IEEE80211_CHAN_NO_IR)
+ chan_flags |= CHAN_NO_IR;
+ if (flags & IEEE80211_CHAN_RADAR)
+ chan_flags |= CHAN_RADAR;
+#endif
+ return chan_flags;
+}
+
+static inline s8_l chan_to_fw_pwr(int power)
+{
+ return power > 127 ? 127 : (s8_l)power;
+}
+
+static void cfg80211_to_ecrnx_chan(const struct cfg80211_chan_def *chandef,
+ struct mac_chan_op *chan)
+{
+ chan->band = chandef->chan->band;
+ chan->type = bw2chnl[chandef->width];
+ chan->prim20_freq = chandef->chan->center_freq;
+ chan->center1_freq = chandef->center_freq1;
+ chan->center2_freq = chandef->center_freq2;
+ chan->flags = get_chan_flags(chandef->chan->flags);
+ chan->tx_power = chan_to_fw_pwr(chandef->chan->max_power);
+}
+
+static inline void limit_chan_bw(u8_l *bw, u16_l primary, u16_l *center1)
+{
+ int oft, new_oft = 10;
+
+ if (*bw <= PHY_CHNL_BW_40)
+ return;
+
+ oft = *center1 - primary;
+ *bw = PHY_CHNL_BW_40;
+
+ if (oft < 0)
+ new_oft = new_oft * -1;
+ if (abs(oft) == 10 || abs(oft) == 50)
+ new_oft = new_oft * -1;
+
+ *center1 = primary + new_oft;
+}
+
+/**
+ ******************************************************************************
+ * @brief Allocate memory for a message
+ *
+ * This primitive allocates memory for a message that has to be sent. The memory
+ * is allocated dynamically on the heap and the length of the variable parameter
+ * structure has to be provided in order to allocate the correct size.
+ *
+ * Several additional parameters are provided which will be preset in the message
+ * and which may be used internally to choose the kind of memory to allocate.
+ *
+ * The memory allocated will be automatically freed by the kernel, after the
+ * pointer has been sent to ke_msg_send(). If the message is not sent, it must
+ * be freed explicitly with ke_msg_free().
+ *
+ * Allocation failure is considered critical and should not happen.
+ *
+ * @param[in] id Message identifier
+ * @param[in] dest_id Destination Task Identifier
+ * @param[in] src_id Source Task Identifier
+ * @param[in] param_len Size of the message parameters to be allocated
+ *
+ * @return Pointer to the parameter member of the ke_msg. If the parameter
+ * structure is empty, the pointer will point to the end of the message
+ * and should not be used (except to retrieve the message pointer or to
+ * send the message)
+ ******************************************************************************
+ */
+static inline void *ecrnx_msg_zalloc(lmac_msg_id_t const id,
+ lmac_task_id_t const dest_id,
+ lmac_task_id_t const src_id,
+ uint16_t const param_len)
+{
+ struct lmac_msg *msg;
+ gfp_t flags;
+
+ if (is_non_blocking_msg(id) && in_softirq())
+ flags = GFP_ATOMIC;
+ else
+ flags = GFP_KERNEL;
+
+ msg = (struct lmac_msg *)kzalloc(sizeof(struct lmac_msg) + param_len,
+ flags);
+ if (msg == NULL) {
+ ECRNX_ERR(KERN_CRIT "%s: msg allocation failed\n", __func__);
+ return NULL;
+ }
+
+ msg->id = id;
+ msg->dest_id = dest_id;
+ msg->src_id = src_id;
+ msg->param_len = param_len;
+
+ return msg->param;
+}
+
+static void ecrnx_msg_free(struct ecrnx_hw *ecrnx_hw, const void *msg_params)
+{
+ struct lmac_msg *msg = container_of((void *)msg_params,
+ struct lmac_msg, param);
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ /* Free the message */
+ kfree(msg);
+}
+
+static int ecrnx_send_msg(struct ecrnx_hw *ecrnx_hw, const void *msg_params,
+ int reqcfm, lmac_msg_id_t reqid, void *cfm)
+{
+ struct lmac_msg *msg;
+ struct ecrnx_cmd *cmd;
+ bool nonblock;
+ int ret;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ msg = container_of((void *)msg_params, struct lmac_msg, param);
+
+ if (!test_bit(ECRNX_DEV_STARTED, &ecrnx_hw->flags) &&
+ reqid != MM_RESET_CFM && reqid != MM_VERSION_CFM &&
+ reqid != MM_START_CFM && reqid != MM_SET_IDLE_CFM &&
+ reqid != ME_CONFIG_CFM && reqid != MM_SET_PS_MODE_CFM &&
+ reqid != ME_CHAN_CONFIG_CFM && reqid != MM_SET_GAIN_DELTA_CFM &&
+ reqid != MM_GET_CAL_RESULT_CFM) {
+ ECRNX_ERR(KERN_CRIT "%s: bypassing (ECRNX_DEV_RESTARTING set) 0x%02x\n",
+ __func__, reqid);
+ kfree(msg);
+ return -EBUSY;
+ } else if (!ecrnx_hw->ipc_env) {
+ ECRNX_ERR(KERN_CRIT "%s: bypassing (restart must have failed)\n", __func__);
+ kfree(msg);
+ return -EBUSY;
+ }
+
+ nonblock = is_non_blocking_msg(msg->id);
+
+#if defined(CONFIG_ECRNX_ESWIN_USB)
+ if(register_status == false){
+ kfree(msg);
+ return -ENODEV;
+ }
+#endif
+
+ cmd = kzalloc(sizeof(struct ecrnx_cmd), nonblock ? GFP_ATOMIC : GFP_KERNEL);
+ cmd->result = -EINTR;
+ cmd->id = msg->id;
+ cmd->reqid = reqid;
+ cmd->a2e_msg = msg;
+ cmd->e2a_msg = cfm;
+ if (nonblock)
+ cmd->flags = ECRNX_CMD_FLAG_NONBLOCK;
+ if (reqcfm)
+ cmd->flags |= ECRNX_CMD_FLAG_REQ_CFM;
+ if(ecrnx_hw->wiphy != NULL)
+ {
+ ret = ecrnx_hw->cmd_mgr.queue(&ecrnx_hw->cmd_mgr, cmd);
+ }
+ if (!nonblock)
+ kfree(cmd);
+
+ if (!ret)
+ ret = cmd->result;
+
+ return ret;
+}
+
+/******************************************************************************
+ * Control messages handling functions (SOFTMAC and FULLMAC)
+ *****************************************************************************/
+int ecrnx_send_reset(struct ecrnx_hw *ecrnx_hw)
+{
+ void *void_param;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ /* RESET REQ has no parameter */
+ void_param = ecrnx_msg_zalloc(MM_RESET_REQ, TASK_MM, DRV_TASK_ID, 0);
+ if (!void_param)
+ return -ENOMEM;
+
+ return ecrnx_send_msg(ecrnx_hw, void_param, 1, MM_RESET_CFM, NULL);
+}
+
+int ecrnx_send_start(struct ecrnx_hw *ecrnx_hw)
+{
+ struct mm_start_req *start_req_param;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ /* Build the START REQ message */
+ start_req_param = ecrnx_msg_zalloc(MM_START_REQ, TASK_MM, DRV_TASK_ID,
+ sizeof(struct mm_start_req));
+ if (!start_req_param)
+ return -ENOMEM;
+
+ /* Set parameters for the START message */
+ memcpy(&start_req_param->phy_cfg, &ecrnx_hw->phy.cfg, sizeof(ecrnx_hw->phy.cfg));
+ start_req_param->uapsd_timeout = (u32_l)ecrnx_hw->mod_params->uapsd_timeout;
+ start_req_param->lp_clk_accuracy = (u16_l)ecrnx_hw->mod_params->lp_clk_ppm;
+ start_req_param->tx_timeout[AC_BK] = (u16_l)ecrnx_hw->mod_params->tx_to_bk;
+ start_req_param->tx_timeout[AC_BE] = (u16_l)ecrnx_hw->mod_params->tx_to_be;
+ start_req_param->tx_timeout[AC_VI] = (u16_l)ecrnx_hw->mod_params->tx_to_vi;
+ start_req_param->tx_timeout[AC_VO] = (u16_l)ecrnx_hw->mod_params->tx_to_vo;
+
+ /* Send the START REQ message to LMAC FW */
+ return ecrnx_send_msg(ecrnx_hw, start_req_param, 1, MM_START_CFM, NULL);
+}
+
+int ecrnx_send_version_req(struct ecrnx_hw *ecrnx_hw, struct mm_version_cfm *cfm)
+{
+ void *void_param;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+ /* VERSION REQ has no parameter */
+ void_param = ecrnx_msg_zalloc(MM_VERSION_REQ, TASK_MM, DRV_TASK_ID, 0);
+ if (!void_param)
+ return -ENOMEM;
+
+ return ecrnx_send_msg(ecrnx_hw, void_param, 1, MM_VERSION_CFM, cfm);
+}
+
+int ecrnx_send_add_if(struct ecrnx_hw *ecrnx_hw, const unsigned char *mac,
+ enum nl80211_iftype iftype, bool p2p, struct mm_add_if_cfm *cfm)
+{
+ struct mm_add_if_req *add_if_req_param;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ /* Build the ADD_IF_REQ message */
+ add_if_req_param = ecrnx_msg_zalloc(MM_ADD_IF_REQ, TASK_MM, DRV_TASK_ID,
+ sizeof(struct mm_add_if_req));
+ if (!add_if_req_param)
+ return -ENOMEM;
+
+ /* Set parameters for the ADD_IF_REQ message */
+ memcpy(&(add_if_req_param->addr.array[0]), mac, ETH_ALEN);
+ switch (iftype) {
+ #ifdef CONFIG_ECRNX_FULLMAC
+ case NL80211_IFTYPE_P2P_CLIENT:
+ add_if_req_param->p2p = true;
+ add_if_req_param->type = MM_STA;
+ break;
+ #endif /* CONFIG_ECRNX_FULLMAC */
+ case NL80211_IFTYPE_STATION:
+ add_if_req_param->type = MM_STA;
+ break;
+
+ case NL80211_IFTYPE_ADHOC:
+ add_if_req_param->type = MM_IBSS;
+ break;
+
+ #ifdef CONFIG_ECRNX_FULLMAC
+ case NL80211_IFTYPE_P2P_GO:
+ add_if_req_param->p2p = true;
+ add_if_req_param->type = MM_AP;
+ break;
+ #endif /* CONFIG_ECRNX_FULLMAC */
+ case NL80211_IFTYPE_AP:
+ add_if_req_param->type = MM_AP;
+ break;
+ case NL80211_IFTYPE_MESH_POINT:
+ add_if_req_param->type = MM_MESH_POINT;
+ break;
+ case NL80211_IFTYPE_AP_VLAN:
+ return -1;
+ case NL80211_IFTYPE_MONITOR:
+ add_if_req_param->type = MM_MONITOR;
+ break;
+ default:
+ add_if_req_param->type = MM_STA;
+ break;
+ }
+
+ #ifdef CONFIG_ECRNX_SOFTMAC
+ add_if_req_param->p2p = p2p;
+ #endif /* CONFIG_ECRNX_SOFTMAC */
+
+ /* Send the ADD_IF_REQ message to LMAC FW */
+ return ecrnx_send_msg(ecrnx_hw, add_if_req_param, 1, MM_ADD_IF_CFM, cfm);
+}
+
+int ecrnx_send_remove_if(struct ecrnx_hw *ecrnx_hw, u8 vif_index)
+{
+ struct mm_remove_if_req *remove_if_req;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ /* Build the MM_REMOVE_IF_REQ message */
+ remove_if_req = ecrnx_msg_zalloc(MM_REMOVE_IF_REQ, TASK_MM, DRV_TASK_ID,
+ sizeof(struct mm_remove_if_req));
+ if (!remove_if_req)
+ return -ENOMEM;
+
+ /* Set parameters for the MM_REMOVE_IF_REQ message */
+ remove_if_req->inst_nbr = vif_index;
+
+ /* Send the MM_REMOVE_IF_REQ message to LMAC FW */
+ return ecrnx_send_msg(ecrnx_hw, remove_if_req, 1, MM_REMOVE_IF_CFM, NULL);
+}
+
+int ecrnx_send_set_channel(struct ecrnx_hw *ecrnx_hw, int phy_idx,
+ struct mm_set_channel_cfm *cfm)
+{
+#ifdef CONFIG_ECRNX_SOFTMAC
+ struct cfg80211_chan_def *chandef = &ecrnx_hw->hw->conf.chandef;
+#endif /* CONFIG_ECRNX_SOFTMAC */
+ struct mm_set_channel_req *req;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ if (phy_idx >= ecrnx_hw->phy.cnt)
+ return -ENOTSUPP;
+
+ req = ecrnx_msg_zalloc(MM_SET_CHANNEL_REQ, TASK_MM, DRV_TASK_ID,
+ sizeof(struct mm_set_channel_req));
+ if (!req)
+ return -ENOMEM;
+
+ if (phy_idx == 0) {
+#ifdef CONFIG_ECRNX_SOFTMAC
+ cfg80211_to_ecrnx_chan(chandef, &req->chan);
+#else
+ /* On FULLMAC only setting channel of secondary chain */
+ wiphy_err(ecrnx_hw->wiphy, "Trying to set channel of primary chain");
+ return 0;
+#endif /* CONFIG_ECRNX_SOFTMAC */
+ } else {
+ req->chan = ecrnx_hw->phy.sec_chan;
+ }
+
+ req->index = phy_idx;
+
+ if (ecrnx_hw->phy.limit_bw)
+ limit_chan_bw(&req->chan.type, req->chan.prim20_freq, &req->chan.center1_freq);
+
+ /*ECRNX_DBG("mac80211: freq=%d(c1:%d - c2:%d)/width=%d - band=%d\n"
+ " hw(%d): prim20=%d(c1:%d - c2:%d)/ type=%d - band=%d\n",
+ center_freq, center_freq1, center_freq2, width, band,
+ phy_idx, req->chan.prim20_freq, req->chan.center1_freq,
+ req->chan.center2_freq, req->chan.type, req->chan.band);*/
+
+ /* Send the MM_SET_CHANNEL_REQ REQ message to LMAC FW */
+ return ecrnx_send_msg(ecrnx_hw, req, 1, MM_SET_CHANNEL_CFM, cfm);
+}
+
+
+int ecrnx_send_key_add(struct ecrnx_hw *ecrnx_hw, u8 vif_idx, u8 sta_idx, bool pairwise,
+ u8 *key, u8 key_len, u8 key_idx, u8 cipher_suite,
+ struct mm_key_add_cfm *cfm)
+{
+ struct mm_key_add_req *key_add_req;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ /* Build the MM_KEY_ADD_REQ message */
+ key_add_req = ecrnx_msg_zalloc(MM_KEY_ADD_REQ, TASK_MM, DRV_TASK_ID,
+ sizeof(struct mm_key_add_req));
+ if (!key_add_req)
+ return -ENOMEM;
+
+ /* Set parameters for the MM_KEY_ADD_REQ message */
+ if (sta_idx != 0xFF) {
+ /* Pairwise key */
+ key_add_req->sta_idx = sta_idx;
+ } else {
+ /* Default key */
+ key_add_req->sta_idx = sta_idx;
+ key_add_req->key_idx = (u8_l)key_idx; /* only useful for default keys */
+ }
+ key_add_req->pairwise = pairwise;
+ key_add_req->inst_nbr = vif_idx;
+ key_add_req->key.length = key_len;
+ memcpy(&(key_add_req->key.array[0]), key, key_len);
+
+ key_add_req->cipher_suite = cipher_suite;
+
+ ECRNX_DBG("%s: sta_idx:%d key_idx:%d inst_nbr:%d cipher:%d key_len:%d\n", __func__,
+ key_add_req->sta_idx, key_add_req->key_idx, key_add_req->inst_nbr,
+ key_add_req->cipher_suite, key_add_req->key.length);
+#if defined(CONFIG_ECRNX_DBG) || defined(CONFIG_DYNAMIC_DEBUG)
+ print_hex_dump_bytes("key: ", DUMP_PREFIX_OFFSET, key_add_req->key.array, key_add_req->key.length);
+#endif
+
+ /* Send the MM_KEY_ADD_REQ message to LMAC FW */
+ return ecrnx_send_msg(ecrnx_hw, key_add_req, 1, MM_KEY_ADD_CFM, cfm);
+}
+
+int ecrnx_send_key_del(struct ecrnx_hw *ecrnx_hw, uint8_t hw_key_idx)
+{
+ struct mm_key_del_req *key_del_req;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ /* Build the MM_KEY_DEL_REQ message */
+ key_del_req = ecrnx_msg_zalloc(MM_KEY_DEL_REQ, TASK_MM, DRV_TASK_ID,
+ sizeof(struct mm_key_del_req));
+ if (!key_del_req)
+ return -ENOMEM;
+
+ /* Set parameters for the MM_KEY_DEL_REQ message */
+ key_del_req->hw_key_idx = hw_key_idx;
+
+ /* Send the MM_KEY_DEL_REQ message to LMAC FW */
+ return ecrnx_send_msg(ecrnx_hw, key_del_req, 1, MM_KEY_DEL_CFM, NULL);
+}
+
+int ecrnx_send_bcn_change(struct ecrnx_hw *ecrnx_hw, u8 vif_idx, dma_addr_t bcn_addr,
+ u16 bcn_len, u16 tim_oft, u16 tim_len, u16 *csa_oft)
+{
+ struct mm_bcn_change_req *req;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ /* Build the MM_BCN_CHANGE_REQ message */
+ req = ecrnx_msg_zalloc(MM_BCN_CHANGE_REQ, TASK_MM, DRV_TASK_ID,
+ sizeof(struct mm_bcn_change_req));
+ if (!req)
+ return -ENOMEM;
+
+ /* Set parameters for the MM_BCN_CHANGE_REQ message */
+ req->bcn_ptr = bcn_addr;
+ req->bcn_len = bcn_len;
+ req->tim_oft = tim_oft;
+ req->tim_len = tim_len;
+ req->inst_nbr = vif_idx;
+
+#if defined(CONFIG_ECRNX_SOFTMAC)
+ BUILD_BUG_ON_MSG(IEEE80211_MAX_CSA_COUNTERS_NUM != BCN_MAX_CSA_CPT,
+ "BCN_MAX_CSA_CPT and IEEE80211_MAX_CSA_COUNTERS_NUM "
+ "have different value");
+#endif /* CONFIG_ECRNX_SOFTMAC */
+ if (csa_oft) {
+ int i;
+ for (i = 0; i < BCN_MAX_CSA_CPT; i++) {
+ req->csa_oft[i] = csa_oft[i];
+ }
+ }
+
+ /* Send the MM_BCN_CHANGE_REQ message to LMAC FW */
+ return ecrnx_send_msg(ecrnx_hw, req, 1, MM_BCN_CHANGE_CFM, NULL);
+}
+
+int ecrnx_send_roc(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *vif,
+ struct ieee80211_channel *chan, unsigned int duration)
+{
+ struct mm_remain_on_channel_req *req;
+ struct cfg80211_chan_def chandef;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ /* Create channel definition structure */
+ cfg80211_chandef_create(&chandef, chan, NL80211_CHAN_NO_HT);
+
+ /* Build the MM_REMAIN_ON_CHANNEL_REQ message */
+ req = ecrnx_msg_zalloc(MM_REMAIN_ON_CHANNEL_REQ, TASK_MM, DRV_TASK_ID,
+ sizeof(struct mm_remain_on_channel_req));
+ if (!req)
+ return -ENOMEM;
+
+ /* Set parameters for the MM_REMAIN_ON_CHANNEL_REQ message */
+ req->op_code = MM_ROC_OP_START;
+ req->vif_index = vif->vif_index;
+ req->duration_ms = duration;
+ cfg80211_to_ecrnx_chan(&chandef, &req->chan);
+
+ /* Send the MM_REMAIN_ON_CHANNEL_REQ message to LMAC FW */
+ return ecrnx_send_msg(ecrnx_hw, req, 1, MM_REMAIN_ON_CHANNEL_CFM, NULL);
+}
+
+int ecrnx_send_cancel_roc(struct ecrnx_hw *ecrnx_hw)
+{
+ struct mm_remain_on_channel_req *req;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ /* Build the MM_REMAIN_ON_CHANNEL_REQ message */
+ req = ecrnx_msg_zalloc(MM_REMAIN_ON_CHANNEL_REQ, TASK_MM, DRV_TASK_ID,
+ sizeof(struct mm_remain_on_channel_req));
+ if (!req)
+ return -ENOMEM;
+
+ /* Set parameters for the MM_REMAIN_ON_CHANNEL_REQ message */
+ req->op_code = MM_ROC_OP_CANCEL;
+
+ /* Send the MM_REMAIN_ON_CHANNEL_REQ message to LMAC FW */
+ return ecrnx_send_msg(ecrnx_hw, req, 1, MM_REMAIN_ON_CHANNEL_CFM, NULL);
+}
+
+int ecrnx_send_set_power(struct ecrnx_hw *ecrnx_hw, u8 vif_idx, s8 pwr,
+ struct mm_set_power_cfm *cfm)
+{
+ struct mm_set_power_req *req;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ /* Build the MM_SET_POWER_REQ message */
+ req = ecrnx_msg_zalloc(MM_SET_POWER_REQ, TASK_MM, DRV_TASK_ID,
+ sizeof(struct mm_set_power_req));
+ if (!req)
+ return -ENOMEM;
+
+ /* Set parameters for the MM_SET_POWER_REQ message */
+ req->inst_nbr = vif_idx;
+ req->power = pwr;
+
+ /* Send the MM_SET_POWER_REQ message to LMAC FW */
+ return ecrnx_send_msg(ecrnx_hw, req, 1, MM_SET_POWER_CFM, cfm);
+}
+
+int ecrnx_send_set_edca(struct ecrnx_hw *ecrnx_hw, u8 hw_queue, u32 param,
+ bool uapsd, u8 inst_nbr)
+{
+ struct mm_set_edca_req *set_edca_req;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ /* Build the MM_SET_EDCA_REQ message */
+ set_edca_req = ecrnx_msg_zalloc(MM_SET_EDCA_REQ, TASK_MM, DRV_TASK_ID,
+ sizeof(struct mm_set_edca_req));
+ if (!set_edca_req)
+ return -ENOMEM;
+
+ /* Set parameters for the MM_SET_EDCA_REQ message */
+ set_edca_req->ac_param = param;
+ set_edca_req->uapsd = uapsd;
+ set_edca_req->hw_queue = hw_queue;
+ set_edca_req->inst_nbr = inst_nbr;
+
+ /* Send the MM_SET_EDCA_REQ message to LMAC FW */
+ return ecrnx_send_msg(ecrnx_hw, set_edca_req, 1, MM_SET_EDCA_CFM, NULL);
+}
+
+#ifdef CONFIG_ECRNX_P2P_DEBUGFS
+int ecrnx_send_p2p_oppps_req(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *ecrnx_vif,
+ u8 ctw, struct mm_set_p2p_oppps_cfm *cfm)
+{
+ struct mm_set_p2p_oppps_req *p2p_oppps_req;
+ int error;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ /* Build the MM_SET_P2P_OPPPS_REQ message */
+ p2p_oppps_req = ecrnx_msg_zalloc(MM_SET_P2P_OPPPS_REQ, TASK_MM, DRV_TASK_ID,
+ sizeof(struct mm_set_p2p_oppps_req));
+
+ if (!p2p_oppps_req) {
+ return -ENOMEM;
+ }
+
+ /* Fill the message parameters */
+ p2p_oppps_req->vif_index = ecrnx_vif->vif_index;
+ p2p_oppps_req->ctwindow = ctw;
+
+ /* Send the MM_P2P_OPPPS_REQ message to LMAC FW */
+ error = ecrnx_send_msg(ecrnx_hw, p2p_oppps_req, 1, MM_SET_P2P_OPPPS_CFM, cfm);
+
+ return (error);
+}
+
+int ecrnx_send_p2p_noa_req(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *ecrnx_vif,
+ int count, int interval, int duration, bool dyn_noa,
+ struct mm_set_p2p_noa_cfm *cfm)
+{
+ struct mm_set_p2p_noa_req *p2p_noa_req;
+ int error;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ /* Param check */
+ if (count > 255)
+ count = 255;
+
+ if (duration >= interval) {
+ dev_err(ecrnx_hw->dev, "Invalid p2p NOA config: interval=%d <= duration=%d\n",
+ interval, duration);
+ return -EINVAL;
+ }
+
+ /* Build the MM_SET_P2P_NOA_REQ message */
+ p2p_noa_req = ecrnx_msg_zalloc(MM_SET_P2P_NOA_REQ, TASK_MM, DRV_TASK_ID,
+ sizeof(struct mm_set_p2p_noa_req));
+
+ if (!p2p_noa_req) {
+ return -ENOMEM;
+ }
+
+ /* Fill the message parameters */
+ p2p_noa_req->vif_index = ecrnx_vif->vif_index;
+ p2p_noa_req->noa_inst_nb = 0;
+ p2p_noa_req->count = count;
+
+ if (count) {
+ p2p_noa_req->duration_us = duration * 1024;
+ p2p_noa_req->interval_us = interval * 1024;
+ p2p_noa_req->start_offset = (interval - duration - 10) * 1024;
+ p2p_noa_req->dyn_noa = dyn_noa;
+ }
+
+ /* Send the MM_SET_2P_NOA_REQ message to LMAC FW */
+ error = ecrnx_send_msg(ecrnx_hw, p2p_noa_req, 1, MM_SET_P2P_NOA_CFM, cfm);
+
+ return (error);
+}
+#endif /* CONFIG_ECRNX_P2P_DEBUGFS */
+
+/******************************************************************************
+ * Control messages handling functions (SOFTMAC only)
+ *****************************************************************************/
+#ifdef CONFIG_ECRNX_SOFTMAC
+int ecrnx_send_sta_add(struct ecrnx_hw *ecrnx_hw, struct ieee80211_sta *sta,
+ u8 inst_nbr, struct mm_sta_add_cfm *cfm)
+{
+ struct mm_sta_add_req *sta_add_req;
+ union ecrnx_thd_phy_ctrl_info *phy;
+ struct ecrnx_sta *ecrnx_sta;
+ int error;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ /* Build the MM_STA_ADD_REQ message */
+ sta_add_req = ecrnx_msg_zalloc(MM_STA_ADD_REQ, TASK_MM, DRV_TASK_ID,
+ sizeof(struct mm_sta_add_req));
+ if (!sta_add_req)
+ return -ENOMEM;
+
+ /* Set parameters for the MM_STA_ADD_REQ message */
+ memcpy(&(sta_add_req->mac_addr.array[0]), &(sta->addr[0]), ETH_ALEN);
+
+ if (sta->wme)
+ sta_add_req->capa_flags |= STA_QOS_CAPA;
+ /* TODO: check if a density of 0 microseconds is OK or not for LMAC */
+ if (sta->ht_cap.ht_supported) {
+ int ht_exp = sta->ht_cap.ampdu_factor;
+ int vht_exp = 0;
+
+ sta_add_req->capa_flags |= STA_HT_CAPA;
+ sta_add_req->ampdu_size_max_ht =
+ (1 << (IEEE80211_HT_MAX_AMPDU_FACTOR + ht_exp)) - 1;
+ sta_add_req->ampdu_spacing_min =
+ ecrnx_ampdudensity2usec(sta->ht_cap.ampdu_density);
+
+ if (sta->vht_cap.vht_supported) {
+ sta_add_req->capa_flags |= STA_VHT_CAPA;
+ vht_exp = (sta->vht_cap.cap &
+ IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK) >>
+ IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT;
+ sta_add_req->ampdu_size_max_vht =
+ (1 << (IEEE80211_HT_MAX_AMPDU_FACTOR + vht_exp)) - 1;
+ }
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0) && defined(CONFIG_ECRNX_HE)
+ if (sta->he_cap.has_he) {
+ int he_exp_ext = (sta->he_cap.he_cap_elem.mac_cap_info[3] &
+ IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_MASK) >> 3;
+
+ sta_add_req->capa_flags |= STA_HE_CAPA;
+ if (sta->vht_cap.vht_supported) {
+ if ((vht_exp == 7) && he_exp_ext)
+ sta_add_req->ampdu_size_max_he =
+ (1 << (IEEE80211_HT_MAX_AMPDU_FACTOR + vht_exp + he_exp_ext)) - 1;
+ else
+ sta_add_req->ampdu_size_max_he = sta_add_req->ampdu_size_max_vht;
+ } else {
+ if ((ht_exp == 3) && he_exp_ext)
+ sta_add_req->ampdu_size_max_he =
+ (1 << (IEEE80211_HT_MAX_AMPDU_FACTOR + ht_exp + he_exp_ext)) - 1;
+ else
+ sta_add_req->ampdu_size_max_he = sta_add_req->ampdu_size_max_ht;
+ }
+ }
+#endif
+ }
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0)
+ if (sta->mfp)
+ sta_add_req->capa_flags |= STA_MFP_CAPA;
+#endif
+
+ ecrnx_sta = (struct ecrnx_sta *)sta->drv_priv;
+ /* TODO Set the interface index from the vif structure */
+ sta_add_req->inst_nbr = inst_nbr;
+ sta_add_req->tdls_sta = sta->tdls;
+ sta_add_req->tdls_sta_initiator = STA_TDLS_INITIATOR(sta);
+ sta_add_req->bssid_index = 0;
+ sta_add_req->max_bssid_ind = 0;
+ phy = (union ecrnx_thd_phy_ctrl_info *)&sta_add_req->paid_gid;
+ phy->partialAIDTx = ecrnx_sta->paid;
+ phy->groupIDTx = ecrnx_sta->gid;
+
+ /* Send the MM_STA_ADD_REQ message to LMAC FW */
+ error = ecrnx_send_msg(ecrnx_hw, sta_add_req, 1, MM_STA_ADD_CFM, cfm);
+
+ return (error);
+}
+
+int ecrnx_send_sta_del(struct ecrnx_hw *ecrnx_hw, u8 sta_idx)
+{
+ struct mm_sta_del_req *sta_del_req;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ /* Build the MM_STA_DEL_REQ message */
+ sta_del_req = ecrnx_msg_zalloc(MM_STA_DEL_REQ, TASK_MM, DRV_TASK_ID,
+ sizeof(struct mm_sta_del_req));
+ if (!sta_del_req)
+ return -ENOMEM;
+
+ /* Set parameters for the MM_STA_DEL_REQ message */
+ sta_del_req->sta_idx = sta_idx;
+
+ /* Send the MM_STA_DEL_REQ message to LMAC FW */
+ return ecrnx_send_msg(ecrnx_hw, sta_del_req, 1, MM_STA_DEL_CFM, NULL);
+}
+
+int ecrnx_send_set_filter(struct ecrnx_hw *ecrnx_hw, uint32_t filter)
+{
+ struct mm_set_filter_req *set_filter_req_param;
+ uint32_t rx_filter = 0;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ /* Build the MM_SET_FILTER_REQ message */
+ set_filter_req_param =
+ ecrnx_msg_zalloc(MM_SET_FILTER_REQ, TASK_MM, DRV_TASK_ID,
+ sizeof(struct mm_set_filter_req));
+ if (!set_filter_req_param)
+ return -ENOMEM;
+
+ /* Set parameters for the MM_SET_FILTER_REQ message */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 2, 0)
+ if (filter & FIF_PROMISC_IN_BSS)
+ rx_filter |= NXMAC_ACCEPT_UNICAST_BIT;
+#endif
+ if (filter & FIF_ALLMULTI)
+ rx_filter |= NXMAC_ACCEPT_MULTICAST_BIT;
+
+ if (filter & (FIF_FCSFAIL | FIF_PLCPFAIL))
+ rx_filter |= NXMAC_ACCEPT_ERROR_FRAMES_BIT;
+
+ if (filter & FIF_BCN_PRBRESP_PROMISC)
+ rx_filter |= NXMAC_ACCEPT_OTHER_BSSID_BIT;
+
+ if (filter & FIF_CONTROL)
+ rx_filter |= NXMAC_ACCEPT_OTHER_CNTRL_FRAMES_BIT |
+ NXMAC_ACCEPT_CF_END_BIT |
+ NXMAC_ACCEPT_ACK_BIT |
+ NXMAC_ACCEPT_CTS_BIT |
+ NXMAC_ACCEPT_RTS_BIT |
+ NXMAC_ACCEPT_BA_BIT | NXMAC_ACCEPT_BAR_BIT;
+
+ if (filter & FIF_OTHER_BSS)
+ rx_filter |= NXMAC_ACCEPT_OTHER_BSSID_BIT;
+
+ if (filter & FIF_PSPOLL) {
+ /* TODO: check if the MAC filters apply to our BSSID or is general */
+ rx_filter |= NXMAC_ACCEPT_PS_POLL_BIT;
+ }
+
+ if (filter & FIF_PROBE_REQ) {
+ rx_filter |= NXMAC_ACCEPT_PROBE_REQ_BIT;
+ rx_filter |= NXMAC_ACCEPT_ALL_BEACON_BIT;
+ }
+
+ /* Add the filter flags that are set by default and cannot be changed here */
+ rx_filter |= ECRNX_MAC80211_NOT_CHANGEABLE;
+
+ /* XXX */
+ if (ieee80211_hw_check(ecrnx_hw->hw, AMPDU_AGGREGATION))
+ rx_filter |= NXMAC_ACCEPT_BA_BIT;
+
+ /* Now copy all the flags into the message parameter */
+ set_filter_req_param->filter = rx_filter;
+
+ ECRNX_DBG("new total_flags = 0x%08x\nrx filter set to 0x%08x\n",
+ filter, rx_filter);
+
+ /* Send the MM_SET_FILTER_REQ message to LMAC FW */
+ return ecrnx_send_msg(ecrnx_hw, set_filter_req_param, 1, MM_SET_FILTER_CFM, NULL);
+}
+
+
+int ecrnx_send_add_chanctx(struct ecrnx_hw *ecrnx_hw,
+ struct ieee80211_chanctx_conf *ctx,
+ struct mm_chan_ctxt_add_cfm *cfm)
+{
+ struct mm_chan_ctxt_add_req *req;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ /* Build the MM_CHAN_CTXT_ADD_REQ message */
+ req = ecrnx_msg_zalloc(MM_CHAN_CTXT_ADD_REQ, TASK_MM, DRV_TASK_ID,
+ sizeof(struct mm_chan_ctxt_add_req));
+ if (!req)
+ return -ENOMEM;
+
+ /* Set parameters for the CHAN_CTXT_ADD_REQ message */
+ cfg80211_to_ecrnx_chan(&ctx->def, &req->chan);
+
+ if (ecrnx_hw->phy.limit_bw)
+ limit_chan_bw(&req->chan.type, req->chan.prim20_freq,
+ &req->chan.center1_freq);
+
+ ECRNX_DBG("mac80211: freq=%d(c1:%d - c2:%d)/width=%d - band=%d\n"
+ " prim20=%d(c1:%d - c2:%d)/ type=%d - band=%d\n",
+ ctx->def.chan->center_freq, ctx->def.center_freq1,
+ ctx->def.center_freq2, ctx->def.width, ctx->def.chan->band,
+ req->chan.prim20_freq, req->chan.center1_freq,
+ req->chan.center2_freq, req->chan.type, req->chan.band);
+ /* Send the CHAN_CTXT_ADD_REQ message to LMAC FW */
+ return ecrnx_send_msg(ecrnx_hw, req, 1, MM_CHAN_CTXT_ADD_CFM, cfm);
+}
+
+int ecrnx_send_del_chanctx(struct ecrnx_hw *ecrnx_hw, u8 index)
+{
+ struct mm_chan_ctxt_del_req *req;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ /* Build the MM_CHAN_CTXT_DEL_REQ message */
+ req = ecrnx_msg_zalloc(MM_CHAN_CTXT_DEL_REQ, TASK_MM, DRV_TASK_ID,
+ sizeof(struct mm_chan_ctxt_del_req));
+ if (!req)
+ return -ENOMEM;
+
+ /* Set parameters for the MM_REMOVE_IF_REQ message */
+ req->index = index;
+
+ /* Send the MM_CHAN_CTXT_DEL_REQ message to LMAC FW */
+ return ecrnx_send_msg(ecrnx_hw, req, 1, MM_CHAN_CTXT_DEL_CFM, NULL);
+}
+
+int ecrnx_send_link_chanctx(struct ecrnx_hw *ecrnx_hw, u8 vif_idx, u8 chan_idx,
+ u8 chan_switch)
+{
+ struct mm_chan_ctxt_link_req *req;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ /* Build the MM_CHAN_CTXT_LINK_REQ message */
+ req = ecrnx_msg_zalloc(MM_CHAN_CTXT_LINK_REQ, TASK_MM, DRV_TASK_ID,
+ sizeof(struct mm_chan_ctxt_link_req));
+ if (!req)
+ return -ENOMEM;
+
+ /* Set parameters for the MM_CHAN_CTXT_LINK_REQ message */
+ req->vif_index = vif_idx;
+ req->chan_index = chan_idx;
+ req->chan_switch = chan_switch;
+
+ /* Send the MM_CHAN_CTXT_LINK_REQ message to LMAC FW */
+ return ecrnx_send_msg(ecrnx_hw, req, 1, MM_CHAN_CTXT_LINK_CFM, NULL);
+}
+
+int ecrnx_send_unlink_chanctx(struct ecrnx_hw *ecrnx_hw, u8 vif_idx)
+{
+ struct mm_chan_ctxt_unlink_req *req;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ /* Build the MM_CHAN_CTXT_UNLINK_REQ message */
+ req = ecrnx_msg_zalloc(MM_CHAN_CTXT_UNLINK_REQ, TASK_MM, DRV_TASK_ID,
+ sizeof(struct mm_chan_ctxt_unlink_req));
+ if (!req)
+ return -ENOMEM;
+
+ /* Set parameters for the MM_CHAN_CTXT_UNLINK_REQ message */
+ req->vif_index = vif_idx;
+
+ /* Send the MM_CHAN_CTXT_UNLINK_REQ message to LMAC FW */
+ return ecrnx_send_msg(ecrnx_hw, req, 1, MM_CHAN_CTXT_UNLINK_CFM, NULL);
+}
+
+int ecrnx_send_update_chanctx(struct ecrnx_hw *ecrnx_hw,
+ struct ieee80211_chanctx_conf *ctx)
+{
+ struct mm_chan_ctxt_update_req *req;
+ struct ecrnx_chanctx *ecrnx_chanctx;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ /* Build the MM_CHAN_CTXT_UPDATE_REQ message */
+ req = ecrnx_msg_zalloc(MM_CHAN_CTXT_UPDATE_REQ, TASK_MM, DRV_TASK_ID,
+ sizeof(struct mm_chan_ctxt_update_req));
+ if (!req)
+ return -ENOMEM;
+
+ /* Set parameters for the MM_CHAN_CTXT_UPDATE_REQ message */
+ ecrnx_chanctx = (struct ecrnx_chanctx *)ctx->drv_priv;
+ req->chan_index = ecrnx_chanctx->index;
+ cfg80211_to_ecrnx_chan(&ctx->def, &req->chan);
+
+ if (ecrnx_hw->phy.limit_bw)
+ limit_chan_bw(&req->chan.type, req->chan.prim20_freq, &req->chan.center1_freq);
+
+ ECRNX_DBG("mac80211: freq=%d(c1:%d - c2:%d)/width=%d - band=%d\n"
+ " prim20=%d(c1:%d - c2:%d)/ type=%d - band=%d\n",
+ ctx->def.chan->center_freq, ctx->def.center_freq1,
+ ctx->def.center_freq2, ctx->def.width, ctx->def.chan->band,
+ req->chan.prim20_freq, req->chan.center1_freq,
+ req->chan.center2_freq, req->chan.type, req->chan.band);
+ /* Send the MM_CHAN_CTXT_UPDATE_REQ message to LMAC FW */
+ return ecrnx_send_msg(ecrnx_hw, req, 1, MM_CHAN_CTXT_UPDATE_CFM, NULL);
+}
+
+int ecrnx_send_sched_chanctx(struct ecrnx_hw *ecrnx_hw, u8 vif_idx, u8 chan_idx, u8 type)
+{
+ struct mm_chan_ctxt_sched_req *req;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ /* Build the MM_CHAN_CTXT_SCHED_REQ message */
+ req = ecrnx_msg_zalloc(MM_CHAN_CTXT_SCHED_REQ, TASK_MM, DRV_TASK_ID,
+ sizeof(struct mm_chan_ctxt_sched_req));
+ if (!req)
+ return -ENOMEM;
+
+ /* Set parameters for the MM_CHAN_CTXT_SCHED_REQ message */
+ req->vif_index = vif_idx;
+ req->chan_index = chan_idx;
+ req->type = type;
+
+ /* Send the MM_CHAN_CTXT_SCHED_REQ message to LMAC FW */
+ return ecrnx_send_msg(ecrnx_hw, req, 1, MM_CHAN_CTXT_SCHED_CFM, NULL);
+}
+
+int ecrnx_send_dtim_req(struct ecrnx_hw *ecrnx_hw, u8 dtim_period)
+{
+ struct mm_set_dtim_req *set_dtim_req;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ /* Build the MM_SET_DTIM_REQ message */
+ set_dtim_req = ecrnx_msg_zalloc(MM_SET_DTIM_REQ, TASK_MM, DRV_TASK_ID,
+ sizeof(struct mm_set_dtim_req));
+ if (!set_dtim_req)
+ return -ENOMEM;
+
+ /* Set parameters for the MM_SET_DTIM_REQ message */
+ set_dtim_req->dtim_period = dtim_period;
+
+ /* Send the MM_SET_DTIM_REQ message to LMAC FW */
+ return ecrnx_send_msg(ecrnx_hw, set_dtim_req, 1, MM_SET_DTIM_CFM, NULL);
+}
+
+int ecrnx_send_set_br(struct ecrnx_hw *ecrnx_hw, u32 basic_rates, u8 vif_idx, u8 band)
+{
+ struct mm_set_basic_rates_req *set_basic_rates_req_param;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ /* Build the MM_SET_BASIC_RATES_REQ message */
+ set_basic_rates_req_param =
+ ecrnx_msg_zalloc(MM_SET_BASIC_RATES_REQ, TASK_MM, DRV_TASK_ID,
+ sizeof(struct mm_set_basic_rates_req));
+ if (!set_basic_rates_req_param)
+ return -ENOMEM;
+
+ /* Set parameters for the MM_SET_BASIC_RATES_REQ message */
+ set_basic_rates_req_param->rates = basic_rates;
+ set_basic_rates_req_param->inst_nbr = vif_idx;
+ set_basic_rates_req_param->band = band;
+
+ /* Send the MM_SET_BASIC_RATES_REQ message to LMAC FW */
+ return ecrnx_send_msg(ecrnx_hw, set_basic_rates_req_param, 1, MM_SET_BASIC_RATES_CFM, NULL);
+}
+
+int ecrnx_send_set_beacon_int(struct ecrnx_hw *ecrnx_hw, u16 beacon_int, u8 vif_idx)
+{
+ struct mm_set_beacon_int_req *set_beacon_int_req_param;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ /* Build the MM_SET_BEACON_INT_REQ message */
+ set_beacon_int_req_param =
+ ecrnx_msg_zalloc(MM_SET_BEACON_INT_REQ, TASK_MM, DRV_TASK_ID,
+ sizeof(struct mm_set_beacon_int_req));
+ if (!set_beacon_int_req_param)
+ return -ENOMEM;
+
+ /* Set parameters for the MM_SET_BEACON_INT_REQ message */
+ set_beacon_int_req_param->beacon_int = beacon_int;
+ set_beacon_int_req_param->inst_nbr = vif_idx;
+
+ /* Send the MM_SET_BEACON_INT_REQ message to LMAC FW */
+ return ecrnx_send_msg(ecrnx_hw, set_beacon_int_req_param, 1,
+ MM_SET_BEACON_INT_CFM, NULL);
+}
+
+int ecrnx_send_set_bssid(struct ecrnx_hw *ecrnx_hw, const u8 *bssid, u8 vif_idx)
+{
+ struct mm_set_bssid_req *set_bssid_req_param;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ /* Build the MM_SET_BSSID_REQ message */
+ set_bssid_req_param = ecrnx_msg_zalloc(MM_SET_BSSID_REQ, TASK_MM, DRV_TASK_ID,
+ sizeof(struct mm_set_bssid_req));
+ if (!set_bssid_req_param)
+ return -ENOMEM;
+
+ /* Set parameters for the MM_SET_BSSID_REQ message */
+ memcpy(&(set_bssid_req_param->bssid.array[0]), bssid, ETH_ALEN);
+ set_bssid_req_param->inst_nbr = vif_idx;
+
+ /* Send the MM_SET_BSSID_REQ message to LMAC FW */
+ return ecrnx_send_msg(ecrnx_hw, set_bssid_req_param, 1, MM_SET_BSSID_CFM, NULL);
+}
+
+int ecrnx_send_set_vif_state(struct ecrnx_hw *ecrnx_hw, bool active,
+ u16 aid, u8 vif_idx)
+{
+ struct mm_set_vif_state_req *req;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ /* Build the MM_SET_VIF_STATE_REQ message */
+ req = ecrnx_msg_zalloc(MM_SET_VIF_STATE_REQ, TASK_MM, DRV_TASK_ID,
+ sizeof(struct mm_set_vif_state_req));
+ if (!req)
+ return -ENOMEM;
+
+ /* Set parameters for the MM_SET_VIF_STATE_REQ message */
+ req->active = active;
+ req->aid = aid;
+ req->inst_nbr = vif_idx;
+
+ /* Send the MM_SET_VIF_STATE_REQ message to LMAC FW */
+ return ecrnx_send_msg(ecrnx_hw, req, 1, MM_SET_VIF_STATE_CFM, NULL);
+}
+
+int ecrnx_send_set_mode(struct ecrnx_hw *ecrnx_hw, u8 abgnmode)
+{
+ struct mm_set_mode_req *set_mode_req_param;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ /* Build the MM_SET_MODE_REQ message */
+ set_mode_req_param = ecrnx_msg_zalloc(MM_SET_MODE_REQ, TASK_MM, DRV_TASK_ID,
+ sizeof(struct mm_set_mode_req));
+ if (!set_mode_req_param)
+ return -ENOMEM;
+
+ set_mode_req_param->abgnmode = abgnmode;
+
+ /* Send the MM_SET_MODE_REQ message to LMAC FW */
+ return ecrnx_send_msg(ecrnx_hw, set_mode_req_param, 1, MM_SET_MODE_CFM, NULL);
+}
+
+int ecrnx_send_set_idle(struct ecrnx_hw *ecrnx_hw, int idle)
+{
+ struct mm_set_idle_req *set_idle_req_param;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ set_idle_req_param = ecrnx_msg_zalloc(MM_SET_IDLE_REQ, TASK_MM, DRV_TASK_ID,
+ sizeof(struct mm_set_idle_req));
+ if (!set_idle_req_param)
+ return -ENOMEM;
+
+ set_idle_req_param->hw_idle = idle;
+
+ return ecrnx_send_msg(ecrnx_hw, set_idle_req_param, 1, MM_SET_IDLE_CFM, NULL);
+}
+
+int ecrnx_send_set_ps_mode(struct ecrnx_hw *ecrnx_hw, u8 ps_mode)
+{
+ struct mm_set_ps_mode_req *set_ps_mode_req_param;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ set_ps_mode_req_param =
+ ecrnx_msg_zalloc(MM_SET_PS_MODE_REQ, TASK_MM, DRV_TASK_ID,
+ sizeof(struct mm_set_ps_mode_req));
+ if (!set_ps_mode_req_param)
+ return -ENOMEM;
+
+ set_ps_mode_req_param->new_state = ps_mode;
+
+ return ecrnx_send_msg(ecrnx_hw, set_ps_mode_req_param, 1, MM_SET_PS_MODE_CFM, NULL);
+}
+
+int ecrnx_send_set_ps_options(struct ecrnx_hw *ecrnx_hw, bool listen_bcmc,
+ u16 listen_interval, u8 vif_idx)
+{
+ struct mm_set_ps_options_req *req;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ /* Build the MM_SET_PS_OPTIONS_REQ message */
+ req = ecrnx_msg_zalloc(MM_SET_PS_OPTIONS_REQ, TASK_MM, DRV_TASK_ID,
+ sizeof(struct mm_set_ps_options_req));
+ if (!req)
+ return -ENOMEM;
+
+ /* Set parameters for the MM_SET_VIF_STATE_REQ message */
+ req->listen_interval = listen_interval;
+ req->dont_listen_bc_mc = !listen_bcmc;
+ req->vif_index = vif_idx;
+
+ /* Send the MM_SET_PS_OPTIONS_REQ message to LMAC FW */
+ return ecrnx_send_msg(ecrnx_hw, req, 1, MM_SET_PS_OPTIONS_CFM, NULL);
+}
+
+int ecrnx_send_set_slottime(struct ecrnx_hw *ecrnx_hw, int use_short_slot)
+{
+ struct mm_set_slottime_req *set_slottime_req_param;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ /* Build the MM_SET_SLOTTIME_REQ message */
+ set_slottime_req_param =
+ ecrnx_msg_zalloc(MM_SET_SLOTTIME_REQ, TASK_MM, DRV_TASK_ID,
+ sizeof(struct mm_set_slottime_req));
+ if (!set_slottime_req_param)
+ return -ENOMEM;
+
+ /* Set parameters for the MM_SET_SLOTTIME_REQ message */
+ set_slottime_req_param->slottime = use_short_slot ? 9 : 20;
+
+ /* Send the MM_SET_SLOTTIME_REQ message to LMAC FW */
+ return ecrnx_send_msg(ecrnx_hw, set_slottime_req_param, 1, MM_SET_SLOTTIME_CFM, NULL);
+}
+
+int ecrnx_send_ba_add(struct ecrnx_hw *ecrnx_hw, uint8_t type, uint8_t sta_idx,
+ u16 tid, uint8_t bufsz, uint16_t ssn,
+ struct mm_ba_add_cfm *cfm)
+{
+ struct mm_ba_add_req *req;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ /* Build the MM_BA_ADD_REQ message */
+ req = ecrnx_msg_zalloc(MM_BA_ADD_REQ, TASK_MM, DRV_TASK_ID,
+ sizeof(struct mm_ba_add_req));
+ if (!req)
+ return -ENOMEM;
+
+ /* Set parameters for the MM_BA_ADD_REQ message */
+ req->type = type;
+ req->sta_idx = sta_idx;
+ req->tid = (u8_l)tid;
+ req->bufsz = bufsz;
+ req->ssn = ssn;
+
+ /* Send the MM_BA_ADD_REQ message to LMAC FW */
+ return ecrnx_send_msg(ecrnx_hw, req, 1, MM_BA_ADD_CFM, cfm);
+}
+
+int ecrnx_send_ba_del(struct ecrnx_hw *ecrnx_hw, uint8_t sta_idx, u16 tid,
+ struct mm_ba_del_cfm *cfm)
+{
+ struct mm_ba_del_req *req;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ /* Build the MM_BA_DEL_REQ message */
+ req = ecrnx_msg_zalloc(MM_BA_DEL_REQ, TASK_MM, DRV_TASK_ID,
+ sizeof(struct mm_ba_del_req));
+ if (!req)
+ return -ENOMEM;
+
+ /* Set parameters */
+ req->type = 0;
+ req->sta_idx = sta_idx;
+ req->tid = (u8_l)tid;
+
+ /* Send the MM_BA_DEL_REQ message to LMAC FW */
+ return ecrnx_send_msg(ecrnx_hw, req, 1, MM_BA_DEL_CFM, cfm);
+}
+
+int ecrnx_send_scan_req(struct ecrnx_hw *ecrnx_hw, struct ieee80211_vif *vif,
+ struct cfg80211_scan_request *param,
+ struct scan_start_cfm *cfm)
+{
+ struct scan_start_req *req;
+ int i;
+ struct ecrnx_vif *ecrnx_vif;
+ uint8_t chan_flags = 0;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ ecrnx_vif = (struct ecrnx_vif *)vif->drv_priv;
+
+ /* Build the SCAN_START_REQ message */
+ req = ecrnx_msg_zalloc(SCAN_START_REQ, TASK_SCAN, DRV_TASK_ID,
+ sizeof(struct scan_start_req));
+ if (!req)
+ return -ENOMEM;
+
+ /* Set parameters */
+ req->vif_idx = ecrnx_vif->vif_index;
+ req->chan_cnt = (u8)min_t(int, SCAN_CHANNEL_MAX, param->n_channels);
+ req->ssid_cnt = (u8)min_t(int, SCAN_SSID_MAX, param->n_ssids);
+ req->bssid = mac_addr_bcst;
+ req->no_cck = param->no_cck;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0)
+ if (param->duration_mandatory)
+ req->duration = ieee80211_tu_to_usec(param->duration);
+#endif
+
+ if (req->ssid_cnt == 0)
+ chan_flags |= CHAN_NO_IR;
+ for (i = 0; i < req->ssid_cnt; i++) {
+ int j;
+ for (j = 0; j < param->ssids[i].ssid_len; j++)
+ req->ssid[i].array[j] = param->ssids[i].ssid[j];
+ req->ssid[i].length = param->ssids[i].ssid_len;
+ }
+
+ if (param->ie) {
+ if (ecrnx_ipc_elem_var_allocs(ecrnx_hw, &ecrnx_hw->scan_ie,
+ param->ie_len, DMA_TO_DEVICE,
+ NULL, param->ie, NULL))
+ goto error;
+
+ req->add_ie_len = param->ie_len;
+ req->add_ies = ecrnx_hw->scan_ie.dma_addr;
+ } else {
+ req->add_ie_len = 0;
+ req->add_ies = 0;
+ }
+
+ for (i = 0; i < req->chan_cnt; i++) {
+ struct ieee80211_channel *chan = param->channels[i];
+
+ req->chan[i].band = chan->band;
+ req->chan[i].freq = chan->center_freq;
+ req->chan[i].flags = chan_flags | get_chan_flags(chan->flags);
+ req->chan[i].tx_power = chan_to_fw_pwr(chan->max_reg_power);
+ }
+
+ /* Send the SCAN_START_REQ message to LMAC FW */
+ return ecrnx_send_msg(ecrnx_hw, req, 1, SCAN_START_CFM, cfm);
+error:
+ if (req != NULL)
+ ecrnx_msg_free(ecrnx_hw, req);
+ return -ENOMEM;
+}
+
+int ecrnx_send_scan_cancel_req(struct ecrnx_hw *ecrnx_hw,
+ struct scan_cancel_cfm *cfm)
+{
+ struct scan_cancel_req *req;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ /* Build the SCAN_CANCEL_REQ message */
+ req = ecrnx_msg_zalloc(SCAN_CANCEL_REQ, TASK_SCAN, DRV_TASK_ID,
+ sizeof(struct scan_cancel_req));
+ if (!req)
+ return -ENOMEM;
+
+ /* Send the SCAN_CANCEL_REQ message to LMAC FW */
+ return ecrnx_send_msg(ecrnx_hw, req, 1, SCAN_CANCEL_CFM, cfm);
+}
+
+void ecrnx_send_tdls_ps(struct ecrnx_hw *ecrnx_hw, bool ps_mode)
+{
+ if (!ecrnx_hw->mod_params->ps_on)
+ return;
+
+ ecrnx_send_set_ps_mode(ecrnx_hw, ps_mode);
+}
+
+int ecrnx_send_tim_update(struct ecrnx_hw *ecrnx_hw, u8 vif_idx, u16 aid,
+ u8 tx_status)
+{
+ struct mm_tim_update_req *req;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ /* Build the MM_TIM_UPDATE_REQ message */
+ req = ecrnx_msg_zalloc(MM_TIM_UPDATE_REQ, TASK_MM, DRV_TASK_ID,
+ sizeof(struct mm_tim_update_req));
+ if (!req)
+ return -ENOMEM;
+
+ /* Set parameters for the MM_TIM_UPDATE_REQ message */
+ req->aid = aid;
+ req->tx_avail = tx_status;
+ req->inst_nbr = vif_idx;
+
+ /* Send the MM_TIM_UPDATE_REQ message to LMAC FW */
+ return ecrnx_send_msg(ecrnx_hw, req, 1, MM_TIM_UPDATE_CFM, NULL);
+}
+#endif /* CONFIG_ECRNX_SOFTMAC */
+
+/******************************************************************************
+ * Control messages handling functions (FULLMAC only)
+ *****************************************************************************/
+#ifdef CONFIG_ECRNX_FULLMAC
+
+int ecrnx_send_me_config_req(struct ecrnx_hw *ecrnx_hw)
+{
+ struct me_config_req *req;
+ struct wiphy *wiphy = ecrnx_hw->wiphy;
+#ifdef CONFIG_ECRNX_5G
+ struct ieee80211_sta_ht_cap *ht_cap = &wiphy->bands[NL80211_BAND_5GHZ]->ht_cap;
+ struct ieee80211_sta_vht_cap *vht_cap = &wiphy->bands[NL80211_BAND_5GHZ]->vht_cap;
+#else
+ struct ieee80211_sta_ht_cap *ht_cap = &wiphy->bands[NL80211_BAND_2GHZ]->ht_cap;
+ struct ieee80211_sta_vht_cap *vht_cap = &wiphy->bands[NL80211_BAND_2GHZ]->vht_cap;
+#endif
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0) && defined(CONFIG_ECRNX_HE)
+ struct ieee80211_sta_he_cap const *he_cap;
+#endif
+ uint8_t *ht_mcs = (uint8_t *)&ht_cap->mcs;
+ int i;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ /* Build the ME_CONFIG_REQ message */
+ req = ecrnx_msg_zalloc(ME_CONFIG_REQ, TASK_ME, DRV_TASK_ID,
+ sizeof(struct me_config_req));
+ if (!req)
+ return -ENOMEM;
+
+ /* Set parameters for the ME_CONFIG_REQ message */
+ req->ht_supp = ht_cap->ht_supported;
+ req->vht_supp = vht_cap->vht_supported;
+ req->ht_cap.ht_capa_info = cpu_to_le16(ht_cap->cap);
+ req->ht_cap.a_mpdu_param = ht_cap->ampdu_factor |
+ (ht_cap->ampdu_density <<
+ IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT);
+ for (i = 0; i < sizeof(ht_cap->mcs); i++)
+ req->ht_cap.mcs_rate[i] = ht_mcs[i];
+ req->ht_cap.ht_extended_capa = 0;
+ req->ht_cap.tx_beamforming_capa = 0;
+ req->ht_cap.asel_capa = 0;
+
+ req->vht_cap.vht_capa_info = cpu_to_le32(vht_cap->cap);
+ req->vht_cap.rx_highest = cpu_to_le16(vht_cap->vht_mcs.rx_highest);
+ req->vht_cap.rx_mcs_map = cpu_to_le16(vht_cap->vht_mcs.rx_mcs_map);
+ req->vht_cap.tx_highest = cpu_to_le16(vht_cap->vht_mcs.tx_highest);
+ req->vht_cap.tx_mcs_map = cpu_to_le16(vht_cap->vht_mcs.tx_mcs_map);
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0) && defined(CONFIG_ECRNX_HE)
+#ifdef CONFIG_ECRNX_5G
+ if (wiphy->bands[NL80211_BAND_5GHZ]->iftype_data != NULL) {
+ he_cap = &wiphy->bands[NL80211_BAND_5GHZ]->iftype_data->he_cap;
+#else
+ if (wiphy->bands[NL80211_BAND_2GHZ]->iftype_data != NULL) {
+ he_cap = &wiphy->bands[NL80211_BAND_2GHZ]->iftype_data->he_cap;
+#endif
+
+ req->he_supp = he_cap->has_he;
+ for (i = 0; i < ARRAY_SIZE(he_cap->he_cap_elem.mac_cap_info); i++) {
+ req->he_cap.mac_cap_info[i] = he_cap->he_cap_elem.mac_cap_info[i];
+ }
+ for (i = 0; i < ARRAY_SIZE(he_cap->he_cap_elem.phy_cap_info); i++) {
+ req->he_cap.phy_cap_info[i] = he_cap->he_cap_elem.phy_cap_info[i];
+ }
+ req->he_cap.mcs_supp.rx_mcs_80 = cpu_to_le16(he_cap->he_mcs_nss_supp.rx_mcs_80);
+ req->he_cap.mcs_supp.tx_mcs_80 = cpu_to_le16(he_cap->he_mcs_nss_supp.tx_mcs_80);
+ req->he_cap.mcs_supp.rx_mcs_160 = cpu_to_le16(he_cap->he_mcs_nss_supp.rx_mcs_160);
+ req->he_cap.mcs_supp.tx_mcs_160 = cpu_to_le16(he_cap->he_mcs_nss_supp.tx_mcs_160);
+ req->he_cap.mcs_supp.rx_mcs_80p80 = cpu_to_le16(he_cap->he_mcs_nss_supp.rx_mcs_80p80);
+ req->he_cap.mcs_supp.tx_mcs_80p80 = cpu_to_le16(he_cap->he_mcs_nss_supp.tx_mcs_80p80);
+ for (i = 0; i < MAC_HE_PPE_THRES_MAX_LEN; i++) {
+ req->he_cap.ppe_thres[i] = he_cap->ppe_thres[i];
+ }
+ req->he_ul_on = ecrnx_hw->mod_params->he_ul_on;
+ }
+#else
+ req->he_ul_on = false;
+
+ req->he_supp = ecrnx_he_cap.has_he;
+ for (i = 0; i < ARRAY_SIZE(ecrnx_he_cap.he_cap_elem.mac_cap_info); i++) {
+ req->he_cap.mac_cap_info[i] = ecrnx_he_cap.he_cap_elem.mac_cap_info[i];
+ }
+ for (i = 0; i < ARRAY_SIZE(ecrnx_he_cap.he_cap_elem.phy_cap_info); i++) {
+ req->he_cap.phy_cap_info[i] = ecrnx_he_cap.he_cap_elem.phy_cap_info[i];
+ }
+ req->he_cap.mcs_supp.rx_mcs_80 = cpu_to_le16(ecrnx_he_cap.he_mcs_nss_supp.rx_mcs_80);
+ req->he_cap.mcs_supp.tx_mcs_80 = cpu_to_le16(ecrnx_he_cap.he_mcs_nss_supp.tx_mcs_80);
+ req->he_cap.mcs_supp.rx_mcs_160 = cpu_to_le16(ecrnx_he_cap.he_mcs_nss_supp.rx_mcs_160);
+ req->he_cap.mcs_supp.tx_mcs_160 = cpu_to_le16(ecrnx_he_cap.he_mcs_nss_supp.tx_mcs_160);
+ req->he_cap.mcs_supp.rx_mcs_80p80 = cpu_to_le16(ecrnx_he_cap.he_mcs_nss_supp.rx_mcs_80p80);
+ req->he_cap.mcs_supp.tx_mcs_80p80 = cpu_to_le16(ecrnx_he_cap.he_mcs_nss_supp.tx_mcs_80p80);
+ for (i = 0; i < MAC_HE_PPE_THRES_MAX_LEN; i++) {
+ req->he_cap.ppe_thres[i] = ecrnx_he_cap.ppe_thres[i];
+ }
+#endif
+
+ req->ps_on = ecrnx_hw->mod_params->ps_on;
+ req->dpsm = ecrnx_hw->mod_params->dpsm;
+ /**
+ * set sleep_flag for sdio slave.
+ * bit0: MODEM_SLEEP
+ * bit1: WFI_SLEEP
+ * bit2: LIGHT_SLEEP
+ * bit3: DEEP_SLEEP
+ */
+ req->sleep_flag = 0x5;
+ req->tx_lft = ecrnx_hw->mod_params->tx_lft;
+ req->ant_div_on = ecrnx_hw->mod_params->ant_div;
+ if (ecrnx_hw->mod_params->use_80)
+ req->phy_bw_max = PHY_CHNL_BW_80;
+ else if (ecrnx_hw->mod_params->use_2040)
+ req->phy_bw_max = PHY_CHNL_BW_40;
+ else
+ req->phy_bw_max = PHY_CHNL_BW_20;
+
+ wiphy_info(wiphy, "HT supp %d, VHT supp %d, HE supp %d\n", req->ht_supp,
+ req->vht_supp,
+ req->he_supp);
+
+ /* Send the ME_CONFIG_REQ message to LMAC FW */
+ return ecrnx_send_msg(ecrnx_hw, req, 1, ME_CONFIG_CFM, NULL);
+}
+
+int ecrnx_send_me_chan_config_req(struct ecrnx_hw *ecrnx_hw)
+{
+ struct me_chan_config_req *req;
+ struct wiphy *wiphy = ecrnx_hw->wiphy;
+ int i;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ /* Build the ME_CHAN_CONFIG_REQ message */
+ req = ecrnx_msg_zalloc(ME_CHAN_CONFIG_REQ, TASK_ME, DRV_TASK_ID,
+ sizeof(struct me_chan_config_req));
+ if (!req)
+ return -ENOMEM;
+
+ req->chan2G4_cnt= 0;
+ if (wiphy->bands[NL80211_BAND_2GHZ] != NULL) {
+ struct ieee80211_supported_band *b = wiphy->bands[NL80211_BAND_2GHZ];
+ for (i = 0; i < b->n_channels; i++) {
+ req->chan2G4[req->chan2G4_cnt].flags = 0;
+ if (b->channels[i].flags & IEEE80211_CHAN_DISABLED)
+ req->chan2G4[req->chan2G4_cnt].flags |= CHAN_DISABLED;
+ req->chan2G4[req->chan2G4_cnt].flags |= get_chan_flags(b->channels[i].flags);
+ req->chan2G4[req->chan2G4_cnt].band = NL80211_BAND_2GHZ;
+ req->chan2G4[req->chan2G4_cnt].freq = b->channels[i].center_freq;
+ req->chan2G4[req->chan2G4_cnt].tx_power = chan_to_fw_pwr(b->channels[i].max_power);
+ req->chan2G4_cnt++;
+ if (req->chan2G4_cnt == MAC_DOMAINCHANNEL_24G_MAX)
+ break;
+ }
+ }
+
+ req->chan5G_cnt = 0;
+#ifdef CONFIG_ECRNX_5G
+ if (wiphy->bands[NL80211_BAND_5GHZ] != NULL) {
+ struct ieee80211_supported_band *b = wiphy->bands[NL80211_BAND_5GHZ];
+ for (i = 0; i < b->n_channels; i++) {
+ req->chan5G[req->chan5G_cnt].flags = 0;
+ if (b->channels[i].flags & IEEE80211_CHAN_DISABLED)
+ req->chan5G[req->chan5G_cnt].flags |= CHAN_DISABLED;
+ req->chan5G[req->chan5G_cnt].flags |= get_chan_flags(b->channels[i].flags);
+ req->chan5G[req->chan5G_cnt].band = NL80211_BAND_5GHZ;
+ req->chan5G[req->chan5G_cnt].freq = b->channels[i].center_freq;
+ req->chan5G[req->chan5G_cnt].tx_power = chan_to_fw_pwr(b->channels[i].max_power);
+ req->chan5G_cnt++;
+ if (req->chan5G_cnt == MAC_DOMAINCHANNEL_5G_MAX)
+ break;
+ }
+ }
+#endif
+ /* Send the ME_CHAN_CONFIG_REQ message to LMAC FW */
+ return ecrnx_send_msg(ecrnx_hw, req, 1, ME_CHAN_CONFIG_CFM, NULL);
+}
+
+int ecrnx_send_me_set_control_port_req(struct ecrnx_hw *ecrnx_hw, bool opened, u8 sta_idx)
+{
+ struct me_set_control_port_req *req;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ /* Build the ME_SET_CONTROL_PORT_REQ message */
+ req = ecrnx_msg_zalloc(ME_SET_CONTROL_PORT_REQ, TASK_ME, DRV_TASK_ID,
+ sizeof(struct me_set_control_port_req));
+ if (!req)
+ return -ENOMEM;
+
+ /* Set parameters for the ME_SET_CONTROL_PORT_REQ message */
+ req->sta_idx = sta_idx;
+ req->control_port_open = opened;
+
+ /* Send the ME_SET_CONTROL_PORT_REQ message to LMAC FW */
+ return ecrnx_send_msg(ecrnx_hw, req, 1, ME_SET_CONTROL_PORT_CFM, NULL);
+}
+
+int ecrnx_send_me_sta_add(struct ecrnx_hw *ecrnx_hw, struct station_parameters *params,
+ const u8 *mac, u8 inst_nbr, struct me_sta_add_cfm *cfm)
+{
+ struct me_sta_add_req *req;
+ u8 *ht_mcs = (u8 *)&params->link_sta_params.ht_capa->mcs;
+ int i;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ /* Build the MM_STA_ADD_REQ message */
+ req = ecrnx_msg_zalloc(ME_STA_ADD_REQ, TASK_ME, DRV_TASK_ID,
+ sizeof(struct me_sta_add_req));
+ if (!req)
+ return -ENOMEM;
+
+ /* Set parameters for the MM_STA_ADD_REQ message */
+ memcpy(&(req->mac_addr.array[0]), mac, ETH_ALEN);
+
+ req->rate_set.length = params->link_sta_params.supported_rates_len;
+ for (i = 0; i < params->link_sta_params.supported_rates_len; i++)
+ req->rate_set.array[i] = params->link_sta_params.supported_rates[i];
+
+ req->flags = 0;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+ if (params->capability & WLAN_CAPABILITY_SHORT_PREAMBLE){
+ req->flags |= STA_SHORT_PREAMBLE_CAPA;
+ }
+#endif
+
+ if (params->link_sta_params.ht_capa) {
+ const struct ieee80211_ht_cap *ht_capa = params->link_sta_params.ht_capa;
+
+ req->flags |= STA_HT_CAPA;
+ req->ht_cap.ht_capa_info = cpu_to_le16(ht_capa->cap_info);
+ req->ht_cap.a_mpdu_param = ht_capa->ampdu_params_info;
+ for (i = 0; i < sizeof(ht_capa->mcs); i++)
+ req->ht_cap.mcs_rate[i] = ht_mcs[i];
+ req->ht_cap.ht_extended_capa = cpu_to_le16(ht_capa->extended_ht_cap_info);
+ req->ht_cap.tx_beamforming_capa = cpu_to_le32(ht_capa->tx_BF_cap_info);
+ req->ht_cap.asel_capa = ht_capa->antenna_selection_info;
+ }
+
+ if (params->link_sta_params.vht_capa) {
+ const struct ieee80211_vht_cap *vht_capa = params->link_sta_params.vht_capa;
+
+ req->flags |= STA_VHT_CAPA;
+ req->vht_cap.vht_capa_info = cpu_to_le32(vht_capa->vht_cap_info);
+ req->vht_cap.rx_highest = cpu_to_le16(vht_capa->supp_mcs.rx_highest);
+ req->vht_cap.rx_mcs_map = cpu_to_le16(vht_capa->supp_mcs.rx_mcs_map);
+ req->vht_cap.tx_highest = cpu_to_le16(vht_capa->supp_mcs.tx_highest);
+ req->vht_cap.tx_mcs_map = cpu_to_le16(vht_capa->supp_mcs.tx_mcs_map);
+ }
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0)) && defined(CONFIG_ECRNX_HE)
+ if (params->link_sta_params.he_capa) {
+ const struct ieee80211_he_cap_elem *he_capa = params->link_sta_params.he_capa;
+ struct ieee80211_he_mcs_nss_supp *mcs_nss_supp =
+ (struct ieee80211_he_mcs_nss_supp *)(he_capa + 1);
+
+ req->flags |= STA_HE_CAPA;
+ for (i = 0; i < ARRAY_SIZE(he_capa->mac_cap_info); i++) {
+ req->he_cap.mac_cap_info[i] = he_capa->mac_cap_info[i];
+ }
+ for (i = 0; i < ARRAY_SIZE(he_capa->phy_cap_info); i++) {
+ req->he_cap.phy_cap_info[i] = he_capa->phy_cap_info[i];
+ }
+ req->he_cap.mcs_supp.rx_mcs_80 = mcs_nss_supp->rx_mcs_80;
+ req->he_cap.mcs_supp.tx_mcs_80 = mcs_nss_supp->tx_mcs_80;
+ req->he_cap.mcs_supp.rx_mcs_160 = mcs_nss_supp->rx_mcs_160;
+ req->he_cap.mcs_supp.tx_mcs_160 = mcs_nss_supp->tx_mcs_160;
+ req->he_cap.mcs_supp.rx_mcs_80p80 = mcs_nss_supp->rx_mcs_80p80;
+ req->he_cap.mcs_supp.tx_mcs_80p80 = mcs_nss_supp->tx_mcs_80p80;
+ }
+
+#endif
+
+ if (params->sta_flags_set & BIT(NL80211_STA_FLAG_WME))
+ req->flags |= STA_QOS_CAPA;
+
+ if (params->sta_flags_set & BIT(NL80211_STA_FLAG_MFP)) // if (sme->mfp == NL80211_MFP_REQUIRED || sme->mfp ==NL80211_MFP_OPTIONAL) //wfa must used NL80211_MFP_REQUIRED and NL80211_MFP_OPTIONAL
+ req->flags |= STA_MFP_CAPA;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
+ if (params->link_sta_params.opmode_notif_used) {
+ req->flags |= STA_OPMOD_NOTIF;
+ req->opmode = params->link_sta_params.opmode_notif;
+ }
+#endif
+
+ req->aid = cpu_to_le16(params->aid);
+ req->uapsd_queues = params->uapsd_queues;
+ req->max_sp_len = params->max_sp * 2;
+ req->vif_idx = inst_nbr;
+
+ if (params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) {
+ struct ecrnx_vif *ecrnx_vif = ecrnx_hw->vif_table[inst_nbr];
+ req->tdls_sta = true;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+ if ((params->ext_capab[3] & WLAN_EXT_CAPA4_TDLS_CHAN_SWITCH) &&
+ !ecrnx_vif->tdls_chsw_prohibited)
+ req->tdls_chsw_allowed = true;
+#endif
+ if (ecrnx_vif->tdls_status == TDLS_SETUP_RSP_TX)
+ req->tdls_sta_initiator = true;
+ }
+
+ /* Send the ME_STA_ADD_REQ message to LMAC FW */
+ return ecrnx_send_msg(ecrnx_hw, req, 1, ME_STA_ADD_CFM, cfm);
+}
+
+int ecrnx_send_me_sta_del(struct ecrnx_hw *ecrnx_hw, u8 sta_idx, bool tdls_sta)
+{
+ struct me_sta_del_req *req;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ /* Build the MM_STA_DEL_REQ message */
+ req = ecrnx_msg_zalloc(ME_STA_DEL_REQ, TASK_ME, DRV_TASK_ID,
+ sizeof(struct me_sta_del_req));
+ if (!req)
+ return -ENOMEM;
+
+ /* Set parameters for the MM_STA_DEL_REQ message */
+ req->sta_idx = sta_idx;
+ req->tdls_sta = tdls_sta;
+
+ /* Send the ME_STA_DEL_REQ message to LMAC FW */
+ return ecrnx_send_msg(ecrnx_hw, req, 1, ME_STA_DEL_CFM, NULL);
+}
+
+int ecrnx_send_me_traffic_ind(struct ecrnx_hw *ecrnx_hw, u8 sta_idx, bool uapsd, u8 tx_status)
+{
+ struct me_traffic_ind_req *req;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ /* Build the ME_UTRAFFIC_IND_REQ message */
+ req = ecrnx_msg_zalloc(ME_TRAFFIC_IND_REQ, TASK_ME, DRV_TASK_ID,
+ sizeof(struct me_traffic_ind_req));
+ if (!req)
+ return -ENOMEM;
+
+ /* Set parameters for the ME_TRAFFIC_IND_REQ message */
+ req->sta_idx = sta_idx;
+ req->tx_avail = tx_status;
+ req->uapsd = uapsd;
+
+ /* Send the ME_TRAFFIC_IND_REQ to UMAC FW */
+ return ecrnx_send_msg(ecrnx_hw, req, 1, ME_TRAFFIC_IND_CFM, NULL);
+}
+
+int ecrnx_send_twt_request(struct ecrnx_hw *ecrnx_hw,
+ u8 setup_type, u8 vif_idx,
+ struct twt_conf_tag *conf,
+ struct twt_setup_cfm *cfm)
+{
+ struct twt_setup_req *req;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ /* Build the TWT_SETUP_REQ message */
+ req = ecrnx_msg_zalloc(TWT_SETUP_REQ, TASK_TWT, DRV_TASK_ID,
+ sizeof(struct twt_setup_req));
+ if (!req)
+ return -ENOMEM;
+
+ memcpy(&req->conf, conf, sizeof(req->conf));
+ req->setup_type = setup_type;
+ req->vif_idx = vif_idx;
+
+ /* Send the TWT_SETUP_REQ message to LMAC FW */
+ return ecrnx_send_msg(ecrnx_hw, req, 1, TWT_SETUP_CFM, cfm);
+}
+
+int ecrnx_send_twt_teardown(struct ecrnx_hw *ecrnx_hw,
+ struct twt_teardown_req *twt_teardown,
+ struct twt_teardown_cfm *cfm)
+{
+ struct twt_teardown_req *req;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ /* Build the TWT_TEARDOWN_REQ message */
+ req = ecrnx_msg_zalloc(TWT_TEARDOWN_REQ, TASK_TWT, DRV_TASK_ID,
+ sizeof(struct twt_teardown_req));
+ if (!req)
+ return -ENOMEM;
+
+ memcpy(req, twt_teardown, sizeof(struct twt_teardown_req));
+
+ /* Send the TWT_TEARDOWN_REQ message to LMAC FW */
+ return ecrnx_send_msg(ecrnx_hw, req, 1, TWT_TEARDOWN_CFM, cfm);
+}
+int ecrnx_send_me_rc_stats(struct ecrnx_hw *ecrnx_hw,
+ u8 sta_idx,
+ struct me_rc_stats_cfm *cfm)
+{
+ struct me_rc_stats_req *req;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ /* Build the ME_RC_STATS_REQ message */
+ req = ecrnx_msg_zalloc(ME_RC_STATS_REQ, TASK_ME, DRV_TASK_ID,
+ sizeof(struct me_rc_stats_req));
+ if (!req)
+ return -ENOMEM;
+
+ /* Set parameters for the ME_RC_STATS_REQ message */
+ req->sta_idx = sta_idx;
+
+ /* Send the ME_RC_STATS_REQ message to LMAC FW */
+ return ecrnx_send_msg(ecrnx_hw, req, 1, ME_RC_STATS_CFM, cfm);
+}
+
+int ecrnx_send_me_rc_set_rate(struct ecrnx_hw *ecrnx_hw,
+ u8 sta_idx,
+ u16 rate_cfg)
+{
+ struct me_rc_set_rate_req *req;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ /* Build the ME_RC_SET_RATE_REQ message */
+ req = ecrnx_msg_zalloc(ME_RC_SET_RATE_REQ, TASK_ME, DRV_TASK_ID,
+ sizeof(struct me_rc_set_rate_req));
+ if (!req)
+ return -ENOMEM;
+
+ /* Set parameters for the ME_RC_SET_RATE_REQ message */
+ req->sta_idx = sta_idx;
+ req->fixed_rate_cfg = rate_cfg;
+
+ /* Send the ME_RC_SET_RATE_REQ message to FW */
+ return ecrnx_send_msg(ecrnx_hw, req, 0, 0, NULL);
+}
+
+int ecrnx_send_me_set_ps_mode(struct ecrnx_hw *ecrnx_hw, u8 ps_mode)
+{
+ struct me_set_ps_mode_req *req;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ /* Build the ME_SET_PS_MODE_REQ message */
+ req = ecrnx_msg_zalloc(ME_SET_PS_MODE_REQ, TASK_ME, DRV_TASK_ID,
+ sizeof(struct me_set_ps_mode_req));
+ if (!req)
+ return -ENOMEM;
+
+ /* Set parameters for the ME_SET_PS_MODE_REQ message */
+ req->ps_state = ps_mode;
+
+ /* Send the ME_SET_PS_MODE_REQ message to FW */
+ return ecrnx_send_msg(ecrnx_hw, req, 1, ME_SET_PS_MODE_CFM, NULL);
+}
+
+int ecrnx_send_sm_connect_req(struct ecrnx_hw *ecrnx_hw,
+ struct ecrnx_vif *ecrnx_vif,
+ struct cfg80211_connect_params *sme,
+ struct sm_connect_cfm *cfm)
+{
+ struct sm_connect_req *req;
+ int i, ie_len;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ ie_len = update_connect_req(ecrnx_vif, sme);
+ /* Build the SM_CONNECT_REQ message */
+ req = ecrnx_msg_zalloc(SM_CONNECT_REQ, TASK_SM, DRV_TASK_ID,
+ (sizeof(struct sm_connect_req) + ie_len));
+ if (!req)
+ return -ENOMEM;
+
+ /* Set parameters for the SM_CONNECT_REQ message */
+ if (sme->crypto.n_ciphers_pairwise &&
+ ((sme->crypto.ciphers_pairwise[0] == WLAN_CIPHER_SUITE_WEP40) ||
+ (sme->crypto.ciphers_pairwise[0] == WLAN_CIPHER_SUITE_TKIP) ||
+ (sme->crypto.ciphers_pairwise[0] == WLAN_CIPHER_SUITE_WEP104)))
+ req->flags |= DISABLE_HT;
+
+ if (sme->crypto.control_port)
+ req->flags |= CONTROL_PORT_HOST;
+
+ if (sme->crypto.control_port_no_encrypt)
+ req->flags |= CONTROL_PORT_NO_ENC;
+
+ if (use_pairwise_key(&sme->crypto))
+ req->flags |= WPA_WPA2_IN_USE;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+ if (sme->mfp == NL80211_MFP_REQUIRED)
+ req->flags |= MFP_IN_USE;
+#endif
+
+ req->ctrl_port_ethertype = sme->crypto.control_port_ethertype;
+
+ if (sme->bssid)
+ memcpy(&req->bssid, sme->bssid, ETH_ALEN);
+ else
+ req->bssid = mac_addr_bcst;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0)
+ if (sme->prev_bssid)
+ req->flags |= REASSOCIATION;
+#else
+ if (ecrnx_vif->sta.ap)
+ req->flags |= REASSOCIATION;
+#endif
+ if ((sme->auth_type == NL80211_AUTHTYPE_FT) && (ecrnx_vif->sta.flags & ECRNX_STA_FT_OVER_DS))
+ req->flags |= (REASSOCIATION | FT_OVER_DS);
+ req->vif_idx = ecrnx_vif->vif_index;
+ if (sme->channel) {
+ req->chan.band = sme->channel->band;
+ req->chan.freq = sme->channel->center_freq;
+ req->chan.flags = get_chan_flags(sme->channel->flags);
+ } else {
+ req->chan.freq = (u16_l)-1;
+ }
+ for (i = 0; i < sme->ssid_len; i++)
+ req->ssid.array[i] = sme->ssid[i];
+ req->ssid.length = sme->ssid_len;
+
+ req->listen_interval = ecrnx_mod_params.listen_itv;
+ req->dont_wait_bcmc = !ecrnx_mod_params.listen_bcmc;
+
+ /* Set auth_type */
+ if (sme->auth_type == NL80211_AUTHTYPE_AUTOMATIC)
+ req->auth_type = WLAN_AUTH_OPEN;
+ else if (sme->auth_type == NL80211_AUTHTYPE_OPEN_SYSTEM)
+ req->auth_type = WLAN_AUTH_OPEN;
+ else if (sme->auth_type == NL80211_AUTHTYPE_SHARED_KEY)
+ req->auth_type = WLAN_AUTH_SHARED_KEY;
+ else if (sme->auth_type == NL80211_AUTHTYPE_FT)
+ req->auth_type = WLAN_AUTH_FT;
+ else if (sme->auth_type == NL80211_AUTHTYPE_SAE)
+ req->auth_type = WLAN_AUTH_SAE;
+ else
+ goto invalid_param;
+ copy_connect_ies(ecrnx_vif, req, sme);
+
+ /* Set UAPSD queues */
+ req->uapsd_queues = ecrnx_mod_params.uapsd_queues;
+
+ /* Send the SM_CONNECT_REQ message to LMAC FW */
+ return ecrnx_send_msg(ecrnx_hw, req, 1, SM_CONNECT_CFM, cfm);
+
+invalid_param:
+ ecrnx_msg_free(ecrnx_hw, req);
+ return -EINVAL;
+}
+
+int ecrnx_send_sm_disconnect_req(struct ecrnx_hw *ecrnx_hw,
+ struct ecrnx_vif *ecrnx_vif,
+ u16 reason)
+{
+ struct sm_disconnect_req *req;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ /* Build the SM_DISCONNECT_REQ message */
+ req = ecrnx_msg_zalloc(SM_DISCONNECT_REQ, TASK_SM, DRV_TASK_ID,
+ sizeof(struct sm_disconnect_req));
+ if (!req)
+ return -ENOMEM;
+
+ /* Set parameters for the SM_DISCONNECT_REQ message */
+ req->reason_code = reason;
+ req->vif_idx = ecrnx_vif->vif_index;
+
+ /* Send the SM_DISCONNECT_REQ message to LMAC FW */
+ return ecrnx_send_msg(ecrnx_hw, req, 1, SM_DISCONNECT_CFM, NULL);
+}
+
+int ecrnx_send_sm_external_auth_required_rsp(struct ecrnx_hw *ecrnx_hw,
+ struct ecrnx_vif *ecrnx_vif,
+ u16 status)
+{
+ struct sm_external_auth_required_rsp *rsp;
+
+ /* Build the SM_EXTERNAL_AUTH_CFM message */
+ rsp = ecrnx_msg_zalloc(SM_EXTERNAL_AUTH_REQUIRED_RSP, TASK_SM, DRV_TASK_ID,
+ sizeof(struct sm_external_auth_required_rsp));
+ if (!rsp)
+ return -ENOMEM;
+
+ rsp->status = status;
+ rsp->vif_idx = ecrnx_vif->vif_index;
+
+ /* send the SM_EXTERNAL_AUTH_REQUIRED_RSP message UMAC FW */
+ return ecrnx_send_msg(ecrnx_hw, rsp, 0, 0, NULL);
+}
+int ecrnx_send_sm_ft_auth_rsp(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *ecrnx_vif,
+ uint8_t *ie, int ie_len)
+{
+ struct sm_connect_req *rsp;
+ rsp = ecrnx_msg_zalloc(SM_FT_AUTH_RSP, TASK_SM, DRV_TASK_ID,
+ (sizeof(struct sm_connect_req) + ie_len));
+ if (!rsp)
+ return -ENOMEM;
+ rsp->vif_idx = ecrnx_vif->vif_index;
+ rsp->ie_len = ie_len;
+ memcpy(rsp->ie_buf, ie, rsp->ie_len);
+ return ecrnx_send_msg(ecrnx_hw, rsp, 0, 0, NULL);
+}
+
+int ecrnx_send_apm_start_req(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *vif,
+ struct cfg80211_ap_settings *settings,
+ struct apm_start_cfm *cfm,
+ struct ecrnx_ipc_elem_var *elem)
+{
+ struct apm_start_req *req;
+ struct ecrnx_bcn *bcn = &vif->ap.bcn;
+ u8 *buf;
+ u32 flags = 0;
+ const u8 *rate_ie;
+ u8 rate_len = 0;
+ int var_offset = offsetof(struct ieee80211_mgmt, u.beacon.variable);
+ const u8 *var_pos;
+ int len, i, error;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ /* Build the APM_START_REQ message */
+ req = ecrnx_msg_zalloc(APM_START_REQ, TASK_APM, DRV_TASK_ID,
+ sizeof(struct apm_start_req));
+ if (!req)
+ return -ENOMEM;
+
+ // Build the beacon
+ bcn->dtim = (u8)settings->dtim_period;
+ buf = ecrnx_build_bcn(bcn, &settings->beacon);
+ if (!buf) {
+ ecrnx_msg_free(ecrnx_hw, req);
+ return -ENOMEM;
+ }
+
+ // Retrieve the basic rate set from the beacon buffer
+ len = bcn->len - var_offset;
+ var_pos = buf + var_offset;
+
+// Assume that rate higher that 54 Mbps are BSS membership
+#define IS_BASIC_RATE(r) (r & 0x80) && ((r & ~0x80) <= (54 * 2))
+
+ rate_ie = cfg80211_find_ie(WLAN_EID_SUPP_RATES, var_pos, len);
+ if (rate_ie) {
+ const u8 *rates = rate_ie + 2;
+ for (i = 0; (i < rate_ie[1]) && (rate_len < MAC_RATESET_LEN); i++) {
+ if (IS_BASIC_RATE(rates[i]))
+ req->basic_rates.array[rate_len++] = rates[i];
+ }
+ }
+ rate_ie = cfg80211_find_ie(WLAN_EID_EXT_SUPP_RATES, var_pos, len);
+ if (rate_ie) {
+ const u8 *rates = rate_ie + 2;
+ for (i = 0; (i < rate_ie[1]) && (rate_len < MAC_RATESET_LEN); i++) {
+ if (IS_BASIC_RATE(rates[i]))
+ req->basic_rates.array[rate_len++] = rates[i];
+ }
+ }
+ req->basic_rates.length = rate_len;
+#undef IS_BASIC_RATE
+
+ // Sync buffer for FW
+ if ((error = ecrnx_ipc_elem_var_allocs(ecrnx_hw, elem, bcn->len,
+ DMA_TO_DEVICE, buf, NULL, NULL))) {
+ return error;
+ }
+
+ /* Set parameters for the APM_START_REQ message */
+ req->vif_idx = vif->vif_index;
+ req->bcn_addr = elem->dma_addr;
+ req->bcn_len = bcn->len;
+ req->tim_oft = bcn->head_len;
+ req->tim_len = bcn->tim_len;
+ cfg80211_to_ecrnx_chan(&settings->chandef, &req->chan);
+ req->bcn_int = settings->beacon_interval;
+ if (settings->crypto.control_port)
+ flags |= CONTROL_PORT_HOST;
+
+ if (settings->crypto.control_port_no_encrypt)
+ flags |= CONTROL_PORT_NO_ENC;
+
+ if (use_pairwise_key(&settings->crypto))
+ flags |= WPA_WPA2_IN_USE;
+
+ if (settings->crypto.control_port_ethertype)
+ req->ctrl_port_ethertype = settings->crypto.control_port_ethertype;
+ else
+ req->ctrl_port_ethertype = ETH_P_PAE;
+ req->flags = flags;
+
+ /* Send the APM_START_REQ message to LMAC FW */
+ return ecrnx_send_msg(ecrnx_hw, req, 1, APM_START_CFM, cfm);
+}
+
+int ecrnx_send_apm_stop_req(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *vif)
+{
+ struct apm_stop_req *req;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ /* Build the APM_STOP_REQ message */
+ req = ecrnx_msg_zalloc(APM_STOP_REQ, TASK_APM, DRV_TASK_ID,
+ sizeof(struct apm_stop_req));
+ if (!req)
+ return -ENOMEM;
+
+ /* Set parameters for the APM_STOP_REQ message */
+ req->vif_idx = vif->vif_index;
+
+ /* Send the APM_STOP_REQ message to LMAC FW */
+ return ecrnx_send_msg(ecrnx_hw, req, 1, APM_STOP_CFM, NULL);
+}
+
+int ecrnx_send_apm_probe_req(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *vif,
+ struct ecrnx_sta *sta, struct apm_probe_client_cfm *cfm)
+{
+ struct apm_probe_client_req *req;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ req = ecrnx_msg_zalloc(APM_PROBE_CLIENT_REQ, TASK_APM, DRV_TASK_ID,
+ sizeof(struct apm_probe_client_req));
+ if (!req)
+ return -ENOMEM;
+
+ req->vif_idx = vif->vif_index;
+ req->sta_idx = sta->sta_idx;
+
+ /* Send the APM_PROBE_CLIENT_REQ message to UMAC FW */
+ return ecrnx_send_msg(ecrnx_hw, req, 1, APM_PROBE_CLIENT_CFM, cfm);
+}
+int ecrnx_send_scanu_req(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *ecrnx_vif,
+ struct cfg80211_scan_request *param)
+{
+ struct scanu_start_req *req;
+ int i, chan_num = 0;
+ uint8_t chan_flags = 0;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ /* Build the SCANU_START_REQ message */
+ req = ecrnx_msg_zalloc(SCANU_START_REQ, TASK_SCANU, DRV_TASK_ID,
+ sizeof(struct scanu_start_req));
+ if (!req)
+ return -ENOMEM;
+
+ /* Set parameters */
+ req->vif_idx = ecrnx_vif->vif_index;
+ req->chan_cnt = (u8)min_t(int, SCAN_CHANNEL_MAX, param->n_channels);
+ req->ssid_cnt = (u8)min_t(int, SCAN_SSID_MAX, param->n_ssids);
+ req->bssid = mac_addr_bcst;
+ req->no_cck = param->no_cck;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0)
+ if (param->duration_mandatory)
+ req->duration = ieee80211_tu_to_usec(param->duration);
+#endif
+
+ if(param->n_ssids > SCAN_SSID_MAX){
+ ECRNX_PRINT("%s:n_ssids: %d \n", __func__, param->n_ssids);
+ for (i = 0; i < param->n_ssids; i++){
+ print_hex_dump_bytes("[ecrnx]scan_req: ", DUMP_PREFIX_NONE, &param->ssids[i], param->ssids[i].ssid_len);
+ ECRNX_PRINT("i:%d, ssid_len:%d \n", i, param->ssids[i].ssid_len);
+ }
+ }
+
+ if (req->ssid_cnt == 0)
+ chan_flags |= CHAN_NO_IR;
+ for (i = 0; i < req->ssid_cnt; i++) {
+ int j;
+ for (j = 0; j < param->ssids[i].ssid_len; j++)
+ req->ssid[i].array[j] = param->ssids[i].ssid[j];
+ req->ssid[i].length = param->ssids[i].ssid_len;
+ }
+
+ if(req->ssid_cnt == 1 && param->ssids[0].ssid_len > 0)
+ {
+ if (strcmp(req->ssid[0].array, "DIRECT-"))
+ {
+ req->ssid_cnt = 2;
+ req->ssid[1].length = 0;
+ }
+ }
+
+ if (param->ie) {
+
+ if (ecrnx_ipc_elem_var_allocs(ecrnx_hw, &ecrnx_hw->scan_ie,
+ param->ie_len, DMA_TO_DEVICE,
+ NULL, param->ie, NULL))
+ goto error;
+ req->add_ie_len = param->ie_len;
+ req->add_ies = ecrnx_hw->scan_ie.dma_addr;
+ } else {
+ req->add_ie_len = 0;
+ req->add_ies = 0;
+ }
+
+ for (i = 0; i < req->chan_cnt; i++) {
+ struct ieee80211_channel *chan = param->channels[i];
+
+ if(chan->band){
+ continue;
+ }
+ req->chan[chan_num].band = chan->band;
+ req->chan[chan_num].freq = chan->center_freq;
+ req->chan[chan_num].flags = chan_flags | get_chan_flags(chan->flags);
+ req->chan[chan_num].tx_power = chan_to_fw_pwr(chan->max_reg_power);
+ chan_num++;
+ //printk("--%d set ch, %d,%d,%d,%d\n", i, req->chan[i].band, req->chan[i].freq, req->chan[i].flags, req->chan[i].tx_power);
+ }
+ req->chan_cnt = chan_num;
+ /* Send the SCANU_START_REQ message to LMAC FW */
+ return ecrnx_send_msg(ecrnx_hw, req, 0, 0, NULL);
+error:
+ if (req != NULL)
+ ecrnx_msg_free(ecrnx_hw, req);
+ return -ENOMEM;
+}
+
+int ecrnx_send_scanu_cancel_req(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *ecrnx_vif)
+{
+ struct scanu_cancel_req *req = NULL;
+
+ /* Build the SCANU_START_REQ message */
+ req = ecrnx_msg_zalloc(SCANU_CANCEL_REQ, TASK_SCANU, DRV_TASK_ID,
+ sizeof(struct scanu_cancel_req));
+ if (!req){
+ return -ENOMEM;
+ }
+
+ req->vif_idx = ecrnx_vif->vif_index;
+ ECRNX_PRINT("%s: vif_idx:%d; \n", __func__, req->vif_idx);
+ return ecrnx_send_msg(ecrnx_hw, req, 0, 0, NULL);
+}
+
+int ecrnx_send_apm_start_cac_req(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *vif,
+ struct cfg80211_chan_def *chandef,
+ struct apm_start_cac_cfm *cfm)
+{
+ struct apm_start_cac_req *req;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ /* Build the APM_START_CAC_REQ message */
+ req = ecrnx_msg_zalloc(APM_START_CAC_REQ, TASK_APM, DRV_TASK_ID,
+ sizeof(struct apm_start_cac_req));
+ if (!req)
+ return -ENOMEM;
+
+ /* Set parameters for the APM_START_CAC_REQ message */
+ req->vif_idx = vif->vif_index;
+ cfg80211_to_ecrnx_chan(chandef, &req->chan);
+
+ /* Send the APM_START_CAC_REQ message to LMAC FW */
+ return ecrnx_send_msg(ecrnx_hw, req, 1, APM_START_CAC_CFM, cfm);
+}
+
+int ecrnx_send_apm_stop_cac_req(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *vif)
+{
+ struct apm_stop_cac_req *req;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ /* Build the APM_STOP_CAC_REQ message */
+ req = ecrnx_msg_zalloc(APM_STOP_CAC_REQ, TASK_APM, DRV_TASK_ID,
+ sizeof(struct apm_stop_cac_req));
+ if (!req)
+ return -ENOMEM;
+
+ /* Set parameters for the APM_STOP_CAC_REQ message */
+ req->vif_idx = vif->vif_index;
+
+ /* Send the APM_STOP_CAC_REQ message to LMAC FW */
+ return ecrnx_send_msg(ecrnx_hw, req, 1, APM_STOP_CAC_CFM, NULL);
+}
+
+int ecrnx_send_mesh_start_req(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *vif,
+ const struct mesh_config *conf, const struct mesh_setup *setup,
+ struct mesh_start_cfm *cfm)
+{
+ // Message to send
+ struct mesh_start_req *req;
+ // Supported basic rates
+ struct ieee80211_supported_band *band = ecrnx_hw->wiphy->bands[setup->chandef.chan->band];
+ /* Counter */
+ int i;
+ /* Return status */
+ int status;
+ /* DMA Address to be unmapped after confirmation reception */
+ u32 dma_addr = 0;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ /* Build the MESH_START_REQ message */
+ req = ecrnx_msg_zalloc(MESH_START_REQ, TASK_MESH, DRV_TASK_ID,
+ sizeof(struct mesh_start_req));
+ if (!req) {
+ return -ENOMEM;
+ }
+
+ req->vif_index = vif->vif_index;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+ req->bcn_int = setup->beacon_interval;
+ req->dtim_period = setup->dtim_period;
+#endif
+ req->mesh_id_len = setup->mesh_id_len;
+
+ for (i = 0; i < setup->mesh_id_len; i++) {
+ req->mesh_id[i] = *(setup->mesh_id + i);
+ }
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+ req->user_mpm = setup->user_mpm;
+#endif
+ req->is_auth = setup->is_authenticated;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0)
+ req->auth_id = setup->auth_id;
+#endif
+ req->ie_len = setup->ie_len;
+
+ if (setup->ie_len) {
+ /*
+ * Need to provide a Virtual Address to the MAC so that it can download the
+ * additional information elements.
+ */
+ req->ie_addr = dma_map_single(ecrnx_hw->dev, (void *)setup->ie,
+ setup->ie_len, DMA_FROM_DEVICE);
+
+ /* Check DMA mapping result */
+ if (dma_mapping_error(ecrnx_hw->dev, req->ie_addr)) {
+ ECRNX_ERR(KERN_CRIT "%s - DMA Mapping error on additional IEs\n", __func__);
+
+ /* Consider there is no Additional IEs */
+ req->ie_len = 0;
+ } else {
+ /* Store DMA Address so that we can unmap the memory section once MESH_START_CFM is received */
+ dma_addr = req->ie_addr;
+ }
+ }
+
+ /* Provide rate information */
+ req->basic_rates.length = 0;
+ for (i = 0; i < band->n_bitrates; i++) {
+ u16 rate = band->bitrates[i].bitrate;
+
+ /* Read value is in in units of 100 Kbps, provided value is in units
+ * of 1Mbps, and multiplied by 2 so that 5.5 becomes 11 */
+ rate = (rate << 1) / 10;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0)
+ if (setup->basic_rates & CO_BIT(i)) {
+ rate |= 0x80;
+ }
+#endif
+
+ req->basic_rates.array[i] = (u8)rate;
+ req->basic_rates.length++;
+ }
+
+ /* Provide channel information */
+ cfg80211_to_ecrnx_chan(&setup->chandef, &req->chan);
+
+ /* Send the MESH_START_REQ message to UMAC FW */
+ status = ecrnx_send_msg(ecrnx_hw, req, 1, MESH_START_CFM, cfm);
+
+ /* Unmap DMA area */
+ if (setup->ie_len) {
+ dma_unmap_single(ecrnx_hw->dev, dma_addr, setup->ie_len, DMA_TO_DEVICE);
+ }
+
+ /* Return the status */
+ return (status);
+}
+
+int ecrnx_send_mesh_stop_req(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *vif,
+ struct mesh_stop_cfm *cfm)
+{
+ // Message to send
+ struct mesh_stop_req *req;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ /* Build the MESH_STOP_REQ message */
+ req = ecrnx_msg_zalloc(MESH_STOP_REQ, TASK_MESH, DRV_TASK_ID,
+ sizeof(struct mesh_stop_req));
+ if (!req) {
+ return -ENOMEM;
+ }
+
+ req->vif_idx = vif->vif_index;
+
+ /* Send the MESH_STOP_REQ message to UMAC FW */
+ return ecrnx_send_msg(ecrnx_hw, req, 1, MESH_STOP_CFM, cfm);
+}
+
+int ecrnx_send_mesh_update_req(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *vif,
+ u32 mask, const struct mesh_config *p_mconf, struct mesh_update_cfm *cfm)
+{
+ // Message to send
+ struct mesh_update_req *req;
+ // Keep only bit for fields which can be updated
+ u32 supp_mask = (mask << 1) & (CO_BIT(NL80211_MESHCONF_GATE_ANNOUNCEMENTS)
+ | CO_BIT(NL80211_MESHCONF_HWMP_ROOTMODE)
+ | CO_BIT(NL80211_MESHCONF_FORWARDING)
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+ | CO_BIT(NL80211_MESHCONF_POWER_MODE)
+#endif
+ );
+
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ if (!supp_mask) {
+ return -ENOENT;
+ }
+
+ /* Build the MESH_UPDATE_REQ message */
+ req = ecrnx_msg_zalloc(MESH_UPDATE_REQ, TASK_MESH, DRV_TASK_ID,
+ sizeof(struct mesh_update_req));
+
+ if (!req) {
+ return -ENOMEM;
+ }
+
+ req->vif_idx = vif->vif_index;
+
+ if (supp_mask & CO_BIT(NL80211_MESHCONF_GATE_ANNOUNCEMENTS))
+ {
+ req->flags |= CO_BIT(MESH_UPDATE_FLAGS_GATE_MODE_BIT);
+ req->gate_announ = p_mconf->dot11MeshGateAnnouncementProtocol;
+ }
+
+ if (supp_mask & CO_BIT(NL80211_MESHCONF_HWMP_ROOTMODE))
+ {
+ req->flags |= CO_BIT(MESH_UPDATE_FLAGS_ROOT_MODE_BIT);
+ req->root_mode = p_mconf->dot11MeshHWMPRootMode;
+ }
+
+ if (supp_mask & CO_BIT(NL80211_MESHCONF_FORWARDING))
+ {
+ req->flags |= CO_BIT(MESH_UPDATE_FLAGS_MESH_FWD_BIT);
+ req->mesh_forward = p_mconf->dot11MeshForwarding;
+ }
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+ if (supp_mask & CO_BIT(NL80211_MESHCONF_POWER_MODE))
+ {
+ req->flags |= CO_BIT(MESH_UPDATE_FLAGS_LOCAL_PSM_BIT);
+ req->local_ps_mode = p_mconf->power_mode;
+ }
+#endif
+ /* Send the MESH_UPDATE_REQ message to UMAC FW */
+ return ecrnx_send_msg(ecrnx_hw, req, 1, MESH_UPDATE_CFM, cfm);
+}
+
+int ecrnx_send_mesh_peer_info_req(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *vif,
+ u8 sta_idx, struct mesh_peer_info_cfm *cfm)
+{
+ // Message to send
+ struct mesh_peer_info_req *req;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ /* Build the MESH_PEER_INFO_REQ message */
+ req = ecrnx_msg_zalloc(MESH_PEER_INFO_REQ, TASK_MESH, DRV_TASK_ID,
+ sizeof(struct mesh_peer_info_req));
+ if (!req) {
+ return -ENOMEM;
+ }
+
+ req->sta_idx = sta_idx;
+
+ /* Send the MESH_PEER_INFO_REQ message to UMAC FW */
+ return ecrnx_send_msg(ecrnx_hw, req, 1, MESH_PEER_INFO_CFM, cfm);
+}
+
+void ecrnx_send_mesh_peer_update_ntf(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *vif,
+ u8 sta_idx, u8 mlink_state)
+{
+ // Message to send
+ struct mesh_peer_update_ntf *ntf;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ /* Build the MESH_PEER_UPDATE_NTF message */
+ ntf = ecrnx_msg_zalloc(MESH_PEER_UPDATE_NTF, TASK_MESH, DRV_TASK_ID,
+ sizeof(struct mesh_peer_update_ntf));
+
+ if (ntf) {
+ ntf->vif_idx = vif->vif_index;
+ ntf->sta_idx = sta_idx;
+ ntf->state = mlink_state;
+
+ /* Send the MESH_PEER_INFO_REQ message to UMAC FW */
+ ecrnx_send_msg(ecrnx_hw, ntf, 0, 0, NULL);
+ }
+}
+
+void ecrnx_send_mesh_path_create_req(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *vif, u8 *tgt_addr)
+{
+ struct mesh_path_create_req *req;
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ /* Check if we are already waiting for a confirmation */
+ if (vif->ap.flags & ECRNX_AP_CREATE_MESH_PATH)
+ return;
+
+ /* Build the MESH_PATH_CREATE_REQ message */
+ req = ecrnx_msg_zalloc(MESH_PATH_CREATE_REQ, TASK_MESH, DRV_TASK_ID,
+ sizeof(struct mesh_path_create_req));
+ if (!req)
+ return;
+
+ req->vif_idx = vif->vif_index;
+ memcpy(&req->tgt_mac_addr, tgt_addr, ETH_ALEN);
+
+ vif->ap.flags |= ECRNX_AP_CREATE_MESH_PATH;
+
+ /* Send the MESH_PATH_CREATE_REQ message to UMAC FW */
+ ecrnx_send_msg(ecrnx_hw, req, 0, 0, NULL);
+}
+
+int ecrnx_send_mesh_path_update_req(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *vif, const u8 *tgt_addr,
+ const u8 *p_nhop_addr, struct mesh_path_update_cfm *cfm)
+{
+ // Message to send
+ struct mesh_path_update_req *req;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ /* Build the MESH_PATH_UPDATE_REQ message */
+ req = ecrnx_msg_zalloc(MESH_PATH_UPDATE_REQ, TASK_MESH, DRV_TASK_ID,
+ sizeof(struct mesh_path_update_req));
+ if (!req) {
+ return -ENOMEM;
+ }
+
+ req->delete = (p_nhop_addr == NULL);
+ req->vif_idx = vif->vif_index;
+ memcpy(&req->tgt_mac_addr, tgt_addr, ETH_ALEN);
+
+ if (p_nhop_addr) {
+ memcpy(&req->nhop_mac_addr, p_nhop_addr, ETH_ALEN);
+ }
+
+ /* Send the MESH_PATH_UPDATE_REQ message to UMAC FW */
+ return ecrnx_send_msg(ecrnx_hw, req, 1, MESH_PATH_UPDATE_CFM, cfm);
+}
+
+void ecrnx_send_mesh_proxy_add_req(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *vif, u8 *ext_addr)
+{
+ // Message to send
+ struct mesh_proxy_add_req *req;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ /* Build the MESH_PROXY_ADD_REQ message */
+ req = ecrnx_msg_zalloc(MESH_PROXY_ADD_REQ, TASK_MESH, DRV_TASK_ID,
+ sizeof(struct mesh_proxy_add_req));
+
+ if (req) {
+ req->vif_idx = vif->vif_index;
+ memcpy(&req->ext_sta_addr, ext_addr, ETH_ALEN);
+
+ /* Send the MESH_PROXY_ADD_REQ message to UMAC FW */
+ ecrnx_send_msg(ecrnx_hw, req, 0, 0, NULL);
+ }
+}
+
+int ecrnx_send_tdls_peer_traffic_ind_req(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *ecrnx_vif)
+{
+ struct tdls_peer_traffic_ind_req *tdls_peer_traffic_ind_req;
+
+ if (!ecrnx_vif->sta.tdls_sta)
+ return -ENOLINK;
+
+ /* Build the TDLS_PEER_TRAFFIC_IND_REQ message */
+ tdls_peer_traffic_ind_req = ecrnx_msg_zalloc(TDLS_PEER_TRAFFIC_IND_REQ, TASK_TDLS, DRV_TASK_ID,
+ sizeof(struct tdls_peer_traffic_ind_req));
+
+ if (!tdls_peer_traffic_ind_req)
+ return -ENOMEM;
+
+ /* Set parameters for the TDLS_PEER_TRAFFIC_IND_REQ message */
+ tdls_peer_traffic_ind_req->vif_index = ecrnx_vif->vif_index;
+ tdls_peer_traffic_ind_req->sta_idx = ecrnx_vif->sta.tdls_sta->sta_idx;
+ memcpy(&(tdls_peer_traffic_ind_req->peer_mac_addr.array[0]),
+ ecrnx_vif->sta.tdls_sta->mac_addr, ETH_ALEN);
+ tdls_peer_traffic_ind_req->dialog_token = 0; // check dialog token value
+ tdls_peer_traffic_ind_req->last_tid = ecrnx_vif->sta.tdls_sta->tdls.last_tid;
+ tdls_peer_traffic_ind_req->last_sn = ecrnx_vif->sta.tdls_sta->tdls.last_sn;
+
+ /* Send the TDLS_PEER_TRAFFIC_IND_REQ message to LMAC FW */
+ return ecrnx_send_msg(ecrnx_hw, tdls_peer_traffic_ind_req, 0, 0, NULL);
+}
+
+int ecrnx_send_config_monitor_req(struct ecrnx_hw *ecrnx_hw,
+ struct cfg80211_chan_def *chandef,
+ struct me_config_monitor_cfm *cfm)
+{
+ struct me_config_monitor_req *req;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ /* Build the ME_CONFIG_MONITOR_REQ message */
+ req = ecrnx_msg_zalloc(ME_CONFIG_MONITOR_REQ, TASK_ME, DRV_TASK_ID,
+ sizeof(struct me_config_monitor_req));
+ if (!req)
+ return -ENOMEM;
+
+ if (chandef) {
+ req->chan_set = true;
+ cfg80211_to_ecrnx_chan(chandef, &req->chan);
+
+ if (ecrnx_hw->phy.limit_bw)
+ limit_chan_bw(&req->chan.type, req->chan.prim20_freq, &req->chan.center1_freq);
+ } else {
+ req->chan_set = false;
+ }
+
+ req->uf = ecrnx_hw->mod_params->uf;
+
+ /* Send the ME_CONFIG_MONITOR_REQ message to FW */
+ return ecrnx_send_msg(ecrnx_hw, req, 1, ME_CONFIG_MONITOR_CFM, cfm);
+}
+#endif /* CONFIG_ECRNX_FULLMAC */
+
+#ifdef CONFIG_ECRNX_P2P
+int ecrnx_send_p2p_start_listen_req(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *ecrnx_vif, unsigned int duration)
+{
+ struct p2p_listen_start_req *req;
+ struct ecrnx_p2p_listen *p2p_listen = &ecrnx_hw->p2p_listen;
+ int rc;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ if(p2p_listen->listen_started)
+ {
+ ECRNX_ERR("P2P listen already ongoing\n");
+ return -EBUSY;
+ }
+
+ p2p_listen->ecrnx_vif = ecrnx_vif;
+ p2p_listen->listen_duration = duration;
+
+ if(ecrnx_hw->scan_request)
+ {
+ ECRNX_ERR("Delaying p2p listen until scan done\n");
+ return 0;
+ }
+
+ /* Build the P2P_LISTEN_START_REQ message */
+ req = ecrnx_msg_zalloc(P2P_LISTEN_START_REQ, TASK_P2P_LISTEN, DRV_TASK_ID,
+ sizeof(struct p2p_listen_start_req));
+ if (!req)
+ return -ENOMEM;
+
+ req->vif_idx = ecrnx_vif->vif_index;
+
+ rc = ecrnx_send_msg(ecrnx_hw, req, 0, 0, NULL);
+ if(rc)
+ return rc;
+
+ p2p_listen->listen_started = 1;
+
+ return rc;
+}
+
+int ecrnx_send_p2p_cancel_listen_req(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *ecrnx_vif)
+{
+ struct p2p_cancel_listen_req *req;
+ struct ecrnx_p2p_listen *p2p_listen = &ecrnx_hw->p2p_listen;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ /* Build the P2P_CANCEL_LISTEN_REQ message */
+ req = ecrnx_msg_zalloc(P2P_CANCEL_LISTEN_REQ, TASK_P2P_LISTEN, DRV_TASK_ID,
+ sizeof(struct p2p_cancel_listen_req));
+ if (!req)
+ return -ENOMEM;
+
+ req->vif_idx = ecrnx_vif->vif_index;
+ p2p_listen->listen_started = 0;
+ //return rwnx_send_msg(rwnx_hw, req, 1, P2P_CANCEL_LISTEN_CFM, NULL);
+ ecrnx_send_msg(ecrnx_hw, req, 0, 0, NULL);
+
+ return 0;
+
+}
+#endif
+
+int ecrnx_send_tdls_chan_switch_req(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *ecrnx_vif,
+ struct ecrnx_sta *ecrnx_sta, bool sta_initiator,
+ u8 oper_class, struct cfg80211_chan_def *chandef,
+ struct tdls_chan_switch_cfm *cfm)
+{
+ struct tdls_chan_switch_req *tdls_chan_switch_req;
+
+#ifdef CONFIG_ECRNX_SOFTMAC
+ /* Check if channel switch already enabled on a TDLS peer */
+ if (ecrnx_hw->tdls_info.chsw_en) {
+ ECRNX_ERR("TDLS channel switch already enabled for another TDLS station\n");
+ return -ENOTSUPP;
+ }
+#endif
+
+ /* Build the TDLS_CHAN_SWITCH_REQ message */
+ tdls_chan_switch_req = ecrnx_msg_zalloc(TDLS_CHAN_SWITCH_REQ, TASK_TDLS, DRV_TASK_ID,
+ sizeof(struct tdls_chan_switch_req));
+
+ if (!tdls_chan_switch_req)
+ return -ENOMEM;
+
+ /* Set parameters for the TDLS_CHAN_SWITCH_REQ message */
+ tdls_chan_switch_req->vif_index = ecrnx_vif->vif_index;
+ tdls_chan_switch_req->sta_idx = ecrnx_sta->sta_idx;
+ memcpy(&(tdls_chan_switch_req->peer_mac_addr.array[0]),
+ ecrnx_sta_addr(ecrnx_sta), ETH_ALEN);
+ tdls_chan_switch_req->initiator = sta_initiator;
+ cfg80211_to_ecrnx_chan(chandef, &tdls_chan_switch_req->chan);
+ tdls_chan_switch_req->op_class = oper_class;
+
+ /* Send the TDLS_CHAN_SWITCH_REQ message to LMAC FW */
+ return ecrnx_send_msg(ecrnx_hw, tdls_chan_switch_req, 1, TDLS_CHAN_SWITCH_CFM, cfm);
+}
+
+int ecrnx_send_tdls_cancel_chan_switch_req(struct ecrnx_hw *ecrnx_hw,
+ struct ecrnx_vif *ecrnx_vif,
+ struct ecrnx_sta *ecrnx_sta,
+ struct tdls_cancel_chan_switch_cfm *cfm)
+{
+ struct tdls_cancel_chan_switch_req *tdls_cancel_chan_switch_req;
+
+ /* Build the TDLS_CHAN_SWITCH_REQ message */
+ tdls_cancel_chan_switch_req = ecrnx_msg_zalloc(TDLS_CANCEL_CHAN_SWITCH_REQ, TASK_TDLS, DRV_TASK_ID,
+ sizeof(struct tdls_cancel_chan_switch_req));
+ if (!tdls_cancel_chan_switch_req)
+ return -ENOMEM;
+
+ /* Set parameters for the TDLS_CHAN_SWITCH_REQ message */
+ tdls_cancel_chan_switch_req->vif_index = ecrnx_vif->vif_index;
+ tdls_cancel_chan_switch_req->sta_idx = ecrnx_sta->sta_idx;
+ memcpy(&(tdls_cancel_chan_switch_req->peer_mac_addr.array[0]),
+ ecrnx_sta_addr(ecrnx_sta), ETH_ALEN);
+
+ /* Send the TDLS_CHAN_SWITCH_REQ message to LMAC FW */
+ return ecrnx_send_msg(ecrnx_hw, tdls_cancel_chan_switch_req, 1, TDLS_CANCEL_CHAN_SWITCH_CFM, cfm);
+}
+
+#ifdef CONFIG_ECRNX_BFMER
+#ifdef CONFIG_ECRNX_SOFTMAC
+void ecrnx_send_bfmer_enable(struct ecrnx_hw *ecrnx_hw, struct ieee80211_sta *sta)
+#else
+void ecrnx_send_bfmer_enable(struct ecrnx_hw *ecrnx_hw, struct ecrnx_sta *ecrnx_sta,
+ const struct ieee80211_vht_cap *vht_cap)
+#endif /* CONFIG_ECRNX_SOFTMAC*/
+{
+ struct mm_bfmer_enable_req *bfmer_en_req;
+#ifdef CONFIG_ECRNX_SOFTMAC
+ struct ecrnx_sta *ecrnx_sta = (struct ecrnx_sta *)&sta->drv_priv;
+ u32 vht_capability;
+#else
+ __le32 vht_capability;
+ u8 rx_nss = 0;
+#endif /* CONFIG_ECRNX_SOFTMAC */
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+#ifdef CONFIG_ECRNX_SOFTMAC
+ if (!sta->vht_cap.vht_supported) {
+#else
+ if (!vht_cap) {
+#endif /* CONFIG_ECRNX_SOFTMAC */
+ goto end;
+ }
+
+#ifdef CONFIG_ECRNX_SOFTMAC
+ vht_capability = sta->vht_cap.cap;
+#else
+ vht_capability = vht_cap->vht_cap_info;
+#endif /* CONFIG_ECRNX_SOFTMAC */
+
+ if (!(vht_capability & IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE)) {
+ goto end;
+ }
+
+#ifdef CONFIG_ECRNX_FULLMAC
+ rx_nss = ecrnx_bfmer_get_rx_nss(vht_cap);
+#endif /* CONFIG_ECRNX_FULLMAC */
+
+ /* Allocate a structure that will contain the beamforming report */
+ if (ecrnx_bfmer_report_add(ecrnx_hw, ecrnx_sta, ECRNX_BFMER_REPORT_SPACE_SIZE))
+ {
+ goto end;
+ }
+
+ /* Build the MM_BFMER_ENABLE_REQ message */
+ bfmer_en_req = ecrnx_msg_zalloc(MM_BFMER_ENABLE_REQ, TASK_MM, DRV_TASK_ID,
+ sizeof(struct mm_bfmer_enable_req));
+
+ /* Check message allocation */
+ if (!bfmer_en_req) {
+ /* Free memory allocated for the report */
+ ecrnx_bfmer_report_del(ecrnx_hw, ecrnx_sta);
+
+ /* Do not use beamforming */
+ goto end;
+ }
+
+ /* Provide DMA address to the MAC */
+ bfmer_en_req->host_bfr_addr = ecrnx_sta->bfm_report->dma_addr;
+ bfmer_en_req->host_bfr_size = ECRNX_BFMER_REPORT_SPACE_SIZE;
+ bfmer_en_req->sta_idx = ecrnx_sta->sta_idx;
+#ifdef CONFIG_ECRNX_SOFTMAC
+ bfmer_en_req->aid = sta->aid;
+ bfmer_en_req->rx_nss = sta->rx_nss;
+#else
+ bfmer_en_req->aid = ecrnx_sta->aid;
+ bfmer_en_req->rx_nss = rx_nss;
+#endif /* CONFIG_ECRNX_SOFTMAC */
+
+ if (vht_capability & IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE) {
+ bfmer_en_req->vht_mu_bfmee = true;
+ } else {
+ bfmer_en_req->vht_mu_bfmee = false;
+ }
+
+ /* Send the MM_BFMER_EN_REQ message to LMAC FW */
+ ecrnx_send_msg(ecrnx_hw, bfmer_en_req, 0, 0, NULL);
+
+end:
+ return;
+}
+
+#ifdef CONFIG_ECRNX_MUMIMO_TX
+int ecrnx_send_mu_group_update_req(struct ecrnx_hw *ecrnx_hw, struct ecrnx_sta *ecrnx_sta)
+{
+ struct mm_mu_group_update_req *req;
+ int group_id, i = 0;
+ u64 map;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ /* Build the MM_MU_GROUP_UPDATE_REQ message */
+ req = ecrnx_msg_zalloc(MM_MU_GROUP_UPDATE_REQ, TASK_MM, DRV_TASK_ID,
+ sizeof(struct mm_mu_group_update_req) +
+ ecrnx_sta->group_info.cnt * sizeof(req->groups[0]));
+
+ /* Check message allocation */
+ if (!req)
+ return -ENOMEM;
+
+ /* Go through the groups the STA belongs to */
+ group_sta_for_each(ecrnx_sta, group_id, map) {
+ int user_pos = ecrnx_mu_group_sta_get_pos(ecrnx_hw, ecrnx_sta, group_id);
+
+ if (WARN((i >= ecrnx_sta->group_info.cnt),
+ "STA%d: Too much group (%d)\n",
+ ecrnx_sta->sta_idx, i + 1))
+ break;
+
+ req->groups[i].group_id = group_id;
+ req->groups[i].user_pos = user_pos;
+
+ i++;
+ }
+
+ req->group_cnt = ecrnx_sta->group_info.cnt;
+ req->sta_idx = ecrnx_sta->sta_idx;
+
+ /* Send the MM_MU_GROUP_UPDATE_REQ message to LMAC FW */
+ return ecrnx_send_msg(ecrnx_hw, req, 1, MM_MU_GROUP_UPDATE_CFM, NULL);
+}
+#endif /* CONFIG_ECRNX_MUMIMO_TX */
+#endif /* CONFIG_ECRNX_BFMER */
+
+/**********************************************************************
+ * Debug Messages
+ *********************************************************************/
+int ecrnx_send_dbg_trigger_req(struct ecrnx_hw *ecrnx_hw, char *msg)
+{
+ struct mm_dbg_trigger_req *req;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ /* Build the MM_DBG_TRIGGER_REQ message */
+ req = ecrnx_msg_zalloc(MM_DBG_TRIGGER_REQ, TASK_MM, DRV_TASK_ID,
+ sizeof(struct mm_dbg_trigger_req));
+ if (!req)
+ return -ENOMEM;
+
+ /* Set parameters for the MM_DBG_TRIGGER_REQ message */
+ strncpy(req->error, msg, sizeof(req->error));
+
+ /* Send the MM_DBG_TRIGGER_REQ message to LMAC FW */
+ return ecrnx_send_msg(ecrnx_hw, req, 0, -1, NULL);
+}
+
+int ecrnx_send_dbg_mem_read_req(struct ecrnx_hw *ecrnx_hw, u32 mem_addr,
+ struct dbg_mem_read_cfm *cfm)
+{
+ struct dbg_mem_read_req *mem_read_req;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ /* Build the DBG_MEM_READ_REQ message */
+ mem_read_req = ecrnx_msg_zalloc(DBG_MEM_READ_REQ, TASK_DBG, DRV_TASK_ID,
+ sizeof(struct dbg_mem_read_req));
+ if (!mem_read_req)
+ return -ENOMEM;
+
+ /* Set parameters for the DBG_MEM_READ_REQ message */
+ mem_read_req->memaddr = mem_addr;
+
+ /* Send the DBG_MEM_READ_REQ message to LMAC FW */
+ return ecrnx_send_msg(ecrnx_hw, mem_read_req, 1, DBG_MEM_READ_CFM, cfm);
+}
+
+int ecrnx_send_dbg_mem_write_req(struct ecrnx_hw *ecrnx_hw, u32 mem_addr,
+ u32 mem_data)
+{
+ struct dbg_mem_write_req *mem_write_req;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ /* Build the DBG_MEM_WRITE_REQ message */
+ mem_write_req = ecrnx_msg_zalloc(DBG_MEM_WRITE_REQ, TASK_DBG, DRV_TASK_ID,
+ sizeof(struct dbg_mem_write_req));
+ if (!mem_write_req)
+ return -ENOMEM;
+
+ /* Set parameters for the DBG_MEM_WRITE_REQ message */
+ mem_write_req->memaddr = mem_addr;
+ mem_write_req->memdata = mem_data;
+
+ /* Send the DBG_MEM_WRITE_REQ message to LMAC FW */
+ return ecrnx_send_msg(ecrnx_hw, mem_write_req, 1, DBG_MEM_WRITE_CFM, NULL);
+}
+
+int ecrnx_send_dbg_set_mod_filter_req(struct ecrnx_hw *ecrnx_hw, u32 filter)
+{
+ struct dbg_set_mod_filter_req *set_mod_filter_req;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ /* Build the DBG_SET_MOD_FILTER_REQ message */
+ set_mod_filter_req =
+ ecrnx_msg_zalloc(DBG_SET_MOD_FILTER_REQ, TASK_DBG, DRV_TASK_ID,
+ sizeof(struct dbg_set_mod_filter_req));
+ if (!set_mod_filter_req)
+ return -ENOMEM;
+
+ /* Set parameters for the DBG_SET_MOD_FILTER_REQ message */
+ set_mod_filter_req->mod_filter = filter;
+
+ /* Send the DBG_SET_MOD_FILTER_REQ message to LMAC FW */
+ return ecrnx_send_msg(ecrnx_hw, set_mod_filter_req, 1, DBG_SET_MOD_FILTER_CFM, NULL);
+}
+
+int ecrnx_send_dbg_set_sev_filter_req(struct ecrnx_hw *ecrnx_hw, u32 filter)
+{
+ struct dbg_set_sev_filter_req *set_sev_filter_req;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ /* Build the DBG_SET_SEV_FILTER_REQ message */
+ set_sev_filter_req =
+ ecrnx_msg_zalloc(DBG_SET_SEV_FILTER_REQ, TASK_DBG, DRV_TASK_ID,
+ sizeof(struct dbg_set_sev_filter_req));
+ if (!set_sev_filter_req)
+ return -ENOMEM;
+
+ /* Set parameters for the DBG_SET_SEV_FILTER_REQ message */
+ set_sev_filter_req->sev_filter = filter;
+
+ /* Send the DBG_SET_SEV_FILTER_REQ message to LMAC FW */
+ return ecrnx_send_msg(ecrnx_hw, set_sev_filter_req, 1, DBG_SET_SEV_FILTER_CFM, NULL);
+}
+
+int ecrnx_send_dbg_get_sys_stat_req(struct ecrnx_hw *ecrnx_hw,
+ struct dbg_get_sys_stat_cfm *cfm)
+{
+ void *req;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ /* Allocate the message */
+ req = ecrnx_msg_zalloc(DBG_GET_SYS_STAT_REQ, TASK_DBG, DRV_TASK_ID, 0);
+ if (!req)
+ return -ENOMEM;
+
+ /* Send the DBG_MEM_READ_REQ message to LMAC FW */
+ return ecrnx_send_msg(ecrnx_hw, req, 1, DBG_GET_SYS_STAT_CFM, cfm);
+}
+
+int ecrnx_send_cfg_rssi_req(struct ecrnx_hw *ecrnx_hw, u8 vif_index, int rssi_thold, u32 rssi_hyst)
+{
+ struct mm_cfg_rssi_req *req;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ /* Build the MM_CFG_RSSI_REQ message */
+ req = ecrnx_msg_zalloc(MM_CFG_RSSI_REQ, TASK_MM, DRV_TASK_ID,
+ sizeof(struct mm_cfg_rssi_req));
+ if (!req)
+ return -ENOMEM;
+
+ /* Set parameters for the MM_CFG_RSSI_REQ message */
+ req->vif_index = vif_index;
+ req->rssi_thold = (s8)rssi_thold;
+ req->rssi_hyst = (u8)rssi_hyst;
+
+ /* Send the MM_CFG_RSSI_REQ message to LMAC FW */
+ return ecrnx_send_msg(ecrnx_hw, req, 0, 0, NULL);
+}
+
+extern bool set_gain;
+int ecrnx_send_set_gain_delta_req(struct ecrnx_hw *ecrnx_hw)
+{
+ s8_l *delta;
+
+ if (set_gain != true)
+ return -ENOMEM;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ /* Build the MM_SET_GAIN_DELTA_REQ message */
+ delta = ecrnx_msg_zalloc(MM_SET_GAIN_DELTA_REQ, TASK_MM, DRV_TASK_ID,
+ GAIN_DELTA_CFG_BUF_SIZE);
+ if (!delta)
+ return -ENOMEM;
+
+ /* Set parameters for the MM_SET_GAIN_DELTA_REQ message */
+ memset(delta, 0, GAIN_DELTA_CFG_BUF_SIZE);
+ memcpy(delta, gain_delta, GAIN_DELTA_CFG_BUF_SIZE);
+
+ /* Send the MM_SET_GAIN_DELTA_REQ message to LMAC FW */
+ return ecrnx_send_msg(ecrnx_hw, delta, 0, MM_SET_GAIN_DELTA_CFM, NULL);
+}
+
+int ecrnx_send_cal_result_get_req(struct ecrnx_hw *ecrnx_hw, void *cfm)
+{
+ void *void_param;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+ /* calibration result get REQ has no parameter */
+ void_param = ecrnx_msg_zalloc(MM_GET_CAL_RESULT_REQ, TASK_MM, DRV_TASK_ID, 0);
+ if (!void_param)
+ return -ENOMEM;
+
+ return ecrnx_send_msg(ecrnx_hw, void_param, 1, MM_GET_CAL_RESULT_CFM, cfm);
+}
+
+
diff --git a/drivers/net/wireless/eswin/ecrnx_msg_tx.h b/drivers/net/wireless/eswin/ecrnx_msg_tx.h
new file mode 100644
index 000000000000..fc1946b9eff4
--- /dev/null
+++ b/drivers/net/wireless/eswin/ecrnx_msg_tx.h
@@ -0,0 +1,198 @@
+/**
+ ****************************************************************************************
+ *
+ * @file ecrnx_msg_tx.h
+ *
+ * @brief TX function declarations
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ****************************************************************************************
+ */
+
+#ifndef _ECRNX_MSG_TX_H_
+#define _ECRNX_MSG_TX_H_
+
+#include "ecrnx_defs.h"
+
+int ecrnx_send_reset(struct ecrnx_hw *ecrnx_hw);
+int ecrnx_send_start(struct ecrnx_hw *ecrnx_hw);
+int ecrnx_send_version_req(struct ecrnx_hw *ecrnx_hw, struct mm_version_cfm *cfm);
+int ecrnx_send_add_if(struct ecrnx_hw *ecrnx_hw, const unsigned char *mac,
+ enum nl80211_iftype iftype, bool p2p, struct mm_add_if_cfm *cfm);
+int ecrnx_send_remove_if(struct ecrnx_hw *ecrnx_hw, u8 vif_index);
+int ecrnx_send_set_channel(struct ecrnx_hw *ecrnx_hw, int phy_idx,
+ struct mm_set_channel_cfm *cfm);
+int ecrnx_send_key_add(struct ecrnx_hw *ecrnx_hw, u8 vif_idx, u8 sta_idx, bool pairwise,
+ u8 *key, u8 key_len, u8 key_idx, u8 cipher_suite,
+ struct mm_key_add_cfm *cfm);
+int ecrnx_send_key_del(struct ecrnx_hw *ecrnx_hw, uint8_t hw_key_idx);
+int ecrnx_send_bcn_change(struct ecrnx_hw *ecrnx_hw, u8 vif_idx, dma_addr_t bcn_addr,
+ u16 bcn_len, u16 tim_oft, u16 tim_len, u16 *csa_oft);
+int ecrnx_send_tim_update(struct ecrnx_hw *ecrnx_hw, u8 vif_idx, u16 aid,
+ u8 tx_status);
+int ecrnx_send_roc(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *vif,
+ struct ieee80211_channel *chan, unsigned int duration);
+int ecrnx_send_cancel_roc(struct ecrnx_hw *ecrnx_hw);
+int ecrnx_send_set_power(struct ecrnx_hw *ecrnx_hw, u8 vif_idx, s8 pwr,
+ struct mm_set_power_cfm *cfm);
+int ecrnx_send_set_edca(struct ecrnx_hw *ecrnx_hw, u8 hw_queue, u32 param,
+ bool uapsd, u8 inst_nbr);
+int ecrnx_send_tdls_chan_switch_req(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *ecrnx_vif,
+ struct ecrnx_sta *ecrnx_sta, bool sta_initiator,
+ u8 oper_class, struct cfg80211_chan_def *chandef,
+ struct tdls_chan_switch_cfm *cfm);
+int ecrnx_send_tdls_cancel_chan_switch_req(struct ecrnx_hw *ecrnx_hw,
+ struct ecrnx_vif *ecrnx_vif,
+ struct ecrnx_sta *ecrnx_sta,
+ struct tdls_cancel_chan_switch_cfm *cfm);
+
+#ifdef CONFIG_ECRNX_P2P_DEBUGFS
+int ecrnx_send_p2p_oppps_req(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *ecrnx_vif,
+ u8 ctw, struct mm_set_p2p_oppps_cfm *cfm);
+int ecrnx_send_p2p_noa_req(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *ecrnx_vif,
+ int count, int interval, int duration,
+ bool dyn_noa, struct mm_set_p2p_noa_cfm *cfm);
+#endif /* CONFIG_ECRNX_P2P_DEBUGFS */
+
+#ifdef CONFIG_ECRNX_SOFTMAC
+int ecrnx_send_sta_add(struct ecrnx_hw *ecrnx_hw, struct ieee80211_sta *sta,
+ u8 inst_nbr, struct mm_sta_add_cfm *cfm);
+int ecrnx_send_sta_del(struct ecrnx_hw *ecrnx_hw, u8 sta_idx);
+int ecrnx_send_set_filter(struct ecrnx_hw *ecrnx_hw, uint32_t filter);
+int ecrnx_send_add_chanctx(struct ecrnx_hw *ecrnx_hw,
+ struct ieee80211_chanctx_conf *ctx,
+ struct mm_chan_ctxt_add_cfm *cfm);
+int ecrnx_send_del_chanctx(struct ecrnx_hw *ecrnx_hw, u8 index);
+int ecrnx_send_link_chanctx(struct ecrnx_hw *ecrnx_hw, u8 vif_idx, u8 chan_idx,
+ u8 chan_switch);
+int ecrnx_send_unlink_chanctx(struct ecrnx_hw *ecrnx_hw, u8 vif_idx);
+int ecrnx_send_update_chanctx(struct ecrnx_hw *ecrnx_hw,
+ struct ieee80211_chanctx_conf *ctx);
+int ecrnx_send_sched_chanctx(struct ecrnx_hw *ecrnx_hw, u8 vif_idx, u8 chan_idx,
+ u8 type);
+
+int ecrnx_send_dtim_req(struct ecrnx_hw *ecrnx_hw, u8 dtim_period);
+int ecrnx_send_set_br(struct ecrnx_hw *ecrnx_hw, u32 basic_rates, u8 vif_idx, u8 band);
+int ecrnx_send_set_beacon_int(struct ecrnx_hw *ecrnx_hw, u16 beacon_int, u8 vif_idx);
+int ecrnx_send_set_bssid(struct ecrnx_hw *ecrnx_hw, const u8 *bssid, u8 vif_idx);
+int ecrnx_send_set_vif_state(struct ecrnx_hw *ecrnx_hw, bool active,
+ u16 aid, u8 vif_idx);
+int ecrnx_send_set_mode(struct ecrnx_hw *ecrnx_hw, u8 abgmode);
+int ecrnx_send_set_idle(struct ecrnx_hw *ecrnx_hw, int idle);
+int ecrnx_send_set_ps_mode(struct ecrnx_hw *ecrnx_hw, u8 ps_mode);
+int ecrnx_send_set_ps_options(struct ecrnx_hw *ecrnx_hw, bool listen_bcmc,
+ u16 listen_interval, u8 vif_idx);
+int ecrnx_send_set_slottime(struct ecrnx_hw *ecrnx_hw, int use_short_slot);
+int ecrnx_send_ba_add(struct ecrnx_hw *ecrnx_hw, uint8_t type, uint8_t sta_idx,
+ u16 tid, uint8_t bufsz, uint16_t ssn,
+ struct mm_ba_add_cfm *cfm);
+int ecrnx_send_ba_del(struct ecrnx_hw *ecrnx_hw, uint8_t sta_idx, u16 tid,
+ struct mm_ba_del_cfm *cfm);
+int ecrnx_send_scan_req(struct ecrnx_hw *ecrnx_hw, struct ieee80211_vif *vif,
+ struct cfg80211_scan_request *param,
+ struct scan_start_cfm *cfm);
+int ecrnx_send_scan_cancel_req(struct ecrnx_hw *ecrnx_hw,
+ struct scan_cancel_cfm *cfm);
+void ecrnx_send_tdls_ps(struct ecrnx_hw *ecrnx_hw, bool ps_mode);
+#endif /* CONFIG_ECRNX_SOFTMAC */
+
+#ifdef CONFIG_ECRNX_FULLMAC
+int ecrnx_send_me_config_req(struct ecrnx_hw *ecrnx_hw);
+int ecrnx_send_me_chan_config_req(struct ecrnx_hw *ecrnx_hw);
+int ecrnx_send_me_set_control_port_req(struct ecrnx_hw *ecrnx_hw, bool opened,
+ u8 sta_idx);
+int ecrnx_send_me_sta_add(struct ecrnx_hw *ecrnx_hw, struct station_parameters *params,
+ const u8 *mac, u8 inst_nbr, struct me_sta_add_cfm *cfm);
+int ecrnx_send_me_sta_del(struct ecrnx_hw *ecrnx_hw, u8 sta_idx, bool tdls_sta);
+int ecrnx_send_me_traffic_ind(struct ecrnx_hw *ecrnx_hw, u8 sta_idx, bool uapsd, u8 tx_status);
+int ecrnx_send_twt_request(struct ecrnx_hw *ecrnx_hw,
+ u8 setup_type, u8 vif_idx,
+ struct twt_conf_tag *conf,
+ struct twt_setup_cfm *cfm);
+int ecrnx_send_twt_teardown(struct ecrnx_hw *ecrnx_hw,
+ struct twt_teardown_req *twt_teardown,
+ struct twt_teardown_cfm *cfm);
+int ecrnx_send_me_rc_stats(struct ecrnx_hw *ecrnx_hw, u8 sta_idx,
+ struct me_rc_stats_cfm *cfm);
+int ecrnx_send_me_rc_set_rate(struct ecrnx_hw *ecrnx_hw,
+ u8 sta_idx,
+ u16 rate_idx);
+int ecrnx_send_me_set_ps_mode(struct ecrnx_hw *ecrnx_hw, u8 ps_mode);
+int ecrnx_send_sm_connect_req(struct ecrnx_hw *ecrnx_hw,
+ struct ecrnx_vif *ecrnx_vif,
+ struct cfg80211_connect_params *sme,
+ struct sm_connect_cfm *cfm);
+int ecrnx_send_sm_disconnect_req(struct ecrnx_hw *ecrnx_hw,
+ struct ecrnx_vif *ecrnx_vif,
+ u16 reason);
+int ecrnx_send_sm_external_auth_required_rsp(struct ecrnx_hw *ecrnx_hw,
+ struct ecrnx_vif *ecrnx_vif,
+ u16 status);
+int ecrnx_send_sm_ft_auth_rsp(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *ecrnx_vif,
+ uint8_t *ie, int ie_len);
+int ecrnx_send_apm_start_req(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *vif,
+ struct cfg80211_ap_settings *settings,
+ struct apm_start_cfm *cfm,
+ struct ecrnx_ipc_elem_var *elem);
+int ecrnx_send_apm_stop_req(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *vif);
+int ecrnx_send_apm_probe_req(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *vif,
+ struct ecrnx_sta *sta, struct apm_probe_client_cfm *cfm);
+int ecrnx_send_scanu_req(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *ecrnx_vif,
+ struct cfg80211_scan_request *param);
+int ecrnx_send_scanu_cancel_req(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *ecrnx_vif);
+int ecrnx_send_apm_start_cac_req(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *vif,
+ struct cfg80211_chan_def *chandef,
+ struct apm_start_cac_cfm *cfm);
+int ecrnx_send_apm_stop_cac_req(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *vif);
+int ecrnx_send_tdls_peer_traffic_ind_req(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *ecrnx_vif);
+int ecrnx_send_config_monitor_req(struct ecrnx_hw *ecrnx_hw,
+ struct cfg80211_chan_def *chandef,
+ struct me_config_monitor_cfm *cfm);
+int ecrnx_send_mesh_start_req(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *vif,
+ const struct mesh_config *conf, const struct mesh_setup *setup,
+ struct mesh_start_cfm *cfm);
+int ecrnx_send_mesh_stop_req(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *vif,
+ struct mesh_stop_cfm *cfm);
+int ecrnx_send_mesh_update_req(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *vif,
+ u32 mask, const struct mesh_config *p_mconf, struct mesh_update_cfm *cfm);
+int ecrnx_send_mesh_peer_info_req(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *vif,
+ u8 sta_idx, struct mesh_peer_info_cfm *cfm);
+void ecrnx_send_mesh_peer_update_ntf(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *vif,
+ u8 sta_idx, u8 mlink_state);
+void ecrnx_send_mesh_path_create_req(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *vif, u8 *tgt_addr);
+int ecrnx_send_mesh_path_update_req(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *vif, const u8 *tgt_addr,
+ const u8 *p_nhop_addr, struct mesh_path_update_cfm *cfm);
+void ecrnx_send_mesh_proxy_add_req(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *vif, u8 *ext_addr);
+#if defined(CONFIG_ECRNX_P2P)
+int ecrnx_send_p2p_start_listen_req(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *ecrnx_vif, unsigned int duration);
+int ecrnx_send_p2p_cancel_listen_req(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *ecrnx_vif);
+#endif
+#endif /* CONFIG_ECRNX_FULLMAC */
+
+#ifdef CONFIG_ECRNX_BFMER
+#ifdef CONFIG_ECRNX_SOFTMAC
+void ecrnx_send_bfmer_enable(struct ecrnx_hw *ecrnx_hw, struct ieee80211_sta *sta);
+#else
+void ecrnx_send_bfmer_enable(struct ecrnx_hw *ecrnx_hw, struct ecrnx_sta *ecrnx_sta,
+ const struct ieee80211_vht_cap *vht_cap);
+#endif /* CONFIG_ECRNX_SOFTMAC*/
+#ifdef CONFIG_ECRNX_MUMIMO_TX
+int ecrnx_send_mu_group_update_req(struct ecrnx_hw *ecrnx_hw, struct ecrnx_sta *ecrnx_sta);
+#endif /* CONFIG_ECRNX_MUMIMO_TX */
+#endif /* CONFIG_ECRNX_BFMER */
+
+/* Debug messages */
+int ecrnx_send_dbg_trigger_req(struct ecrnx_hw *ecrnx_hw, char *msg);
+int ecrnx_send_dbg_mem_read_req(struct ecrnx_hw *ecrnx_hw, u32 mem_addr,
+ struct dbg_mem_read_cfm *cfm);
+int ecrnx_send_dbg_mem_write_req(struct ecrnx_hw *ecrnx_hw, u32 mem_addr,
+ u32 mem_data);
+int ecrnx_send_dbg_set_mod_filter_req(struct ecrnx_hw *ecrnx_hw, u32 filter);
+int ecrnx_send_dbg_set_sev_filter_req(struct ecrnx_hw *ecrnx_hw, u32 filter);
+int ecrnx_send_dbg_get_sys_stat_req(struct ecrnx_hw *ecrnx_hw,
+ struct dbg_get_sys_stat_cfm *cfm);
+int ecrnx_send_cfg_rssi_req(struct ecrnx_hw *ecrnx_hw, u8 vif_index, int rssi_thold, u32 rssi_hyst);
+int ecrnx_send_set_gain_delta_req(struct ecrnx_hw *ecrnx_hw);
+int ecrnx_send_cal_result_get_req(struct ecrnx_hw *ecrnx_hw, void *cfm);
+#endif /* _ECRNX_MSG_TX_H_ */
diff --git a/drivers/net/wireless/eswin/ecrnx_mu_group.c b/drivers/net/wireless/eswin/ecrnx_mu_group.c
new file mode 100644
index 000000000000..2725cbd04c2d
--- /dev/null
+++ b/drivers/net/wireless/eswin/ecrnx_mu_group.c
@@ -0,0 +1,659 @@
+/**
+ ******************************************************************************
+ *
+ * @file ecrnx_mu_group.c
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+
+#include "ecrnx_defs.h"
+#include "ecrnx_msg_tx.h"
+#include "ecrnx_events.h"
+
+
+/**
+ * ecrnx_mu_group_sta_init - Initialize group information for a STA
+ *
+ * @sta: Sta to initialize
+ */
+void ecrnx_mu_group_sta_init(struct ecrnx_sta *sta,
+ const struct ieee80211_vht_cap *vht_cap)
+{
+ sta->group_info.map = 0;
+ sta->group_info.cnt = 0;
+ sta->group_info.active.next = LIST_POISON1;
+ sta->group_info.update.next = LIST_POISON1;
+ sta->group_info.last_update = 0;
+ sta->group_info.traffic = 0;
+ sta->group_info.group = 0;
+
+ if (!vht_cap ||
+ !(vht_cap->vht_cap_info & IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE)) {
+ sta->group_info.map = ECRNX_SU_GROUP;
+ }
+}
+
+/**
+ * ecrnx_mu_group_sta_del - Remove a sta from all MU group
+ *
+ * @ecrnx_hw: main driver data
+ * @sta: STA to remove
+ *
+ * Remove one sta from all the MU groups it belongs to.
+ */
+void ecrnx_mu_group_sta_del(struct ecrnx_hw *ecrnx_hw, struct ecrnx_sta *sta)
+{
+ struct ecrnx_mu_info *mu = &ecrnx_hw->mu;
+ int i, j, group_id;
+ bool lock_taken;
+ u64 map;
+
+ lock_taken = (down_interruptible(&mu->lock) == 0);
+
+ group_sta_for_each(sta, group_id, map) {
+ struct ecrnx_mu_group *group = ecrnx_mu_group_from_id(mu, group_id);
+
+ for (i = 0; i < CONFIG_USER_MAX; i++) {
+ if (group->users[i] == sta) {
+ group->users[i] = NULL;
+ group->user_cnt --;
+ /* Don't keep group with only one user */
+ if (group->user_cnt == 1) {
+ for (j = 0; j < CONFIG_USER_MAX; j++) {
+ if (group->users[j]) {
+ group->users[j]->group_info.cnt--;
+ group->users[j]->group_info.map &= ~BIT_ULL(group->group_id);
+ if (group->users[j]->group_info.group == group_id)
+ group->users[j]->group_info.group = 0;
+ group->user_cnt --;
+ break;
+ }
+ }
+ mu->group_cnt--;
+ trace_mu_group_delete(group->group_id);
+ } else {
+ trace_mu_group_update(group);
+ }
+ break;
+ }
+ }
+
+ WARN((i == CONFIG_USER_MAX), "sta %d doesn't belongs to group %d",
+ sta->sta_idx, group_id);
+ }
+
+ sta->group_info.map = 0;
+ sta->group_info.cnt = 0;
+ sta->group_info.traffic = 0;
+
+ if (sta->group_info.active.next != LIST_POISON1)
+ list_del(&sta->group_info.active);
+
+ if (sta->group_info.update.next != LIST_POISON1)
+ list_del(&sta->group_info.update);
+
+ if (lock_taken)
+ up(&mu->lock);
+}
+
+/**
+ * ecrnx_mu_group_sta_get_map - Get the list of group a STA belongs to
+ *
+ * @sta: pointer to the sta
+ *
+ * @return the list of group a STA belongs to as a bitfield
+ */
+u64 ecrnx_mu_group_sta_get_map(struct ecrnx_sta *sta)
+{
+ if (sta)
+ return sta->group_info.map;
+ return 0;
+}
+
+/**
+ * ecrnx_mu_group_sta_get_pos - Get sta position in a group
+ *
+ * @ecrnx_hw: main driver data
+ * @sta: pointer to the sta
+ * @group_id: Group id
+ *
+ * @return the positon of @sta in group @group_id or -1 if the sta
+ * doesn't belongs to the group (or group id is invalid)
+ */
+int ecrnx_mu_group_sta_get_pos(struct ecrnx_hw *ecrnx_hw, struct ecrnx_sta *sta,
+ int group_id)
+{
+ struct ecrnx_mu_group *group;
+ int i;
+
+ group = ecrnx_mu_group_from_id(&ecrnx_hw->mu, group_id);
+ if (!group)
+ return -1;
+
+ for (i = 0; i < CONFIG_USER_MAX; i++) {
+ if (group->users[i] == sta)
+ return i;
+ }
+
+ WARN(1, "sta %d doesn't belongs to group %d",
+ sta->sta_idx, group_id);
+ return -1;
+}
+
+/**
+ * ecrnx_mu_group_move_head - Move (or add) one element at the top of a list
+ *
+ * @list: list pointer
+ * @elem: element to move (or add) at the top of @list
+ *
+ */
+static inline
+void ecrnx_mu_group_move_head(struct list_head *list, struct list_head *elem)
+{
+ if (elem->next != LIST_POISON1) {
+ __list_del_entry(elem);
+ }
+ list_add(elem, list);
+}
+
+/**
+ * ecrnx_mu_group_remove_users - Remove all the users of a group
+ *
+ * @mu: pointer on MU info
+ * @group: pointer on group to remove users from
+ *
+ * Loop over all users one one group and remove this group from their
+ * map (and count).
+ * Each users is also added to the update_sta list, so that group info
+ * will be resent to fw for this user.
+ */
+static inline
+void ecrnx_mu_group_remove_users(struct ecrnx_mu_info *mu,
+ struct ecrnx_mu_group *group)
+{
+ struct ecrnx_sta *sta;
+ int i, group_id = group->group_id;
+
+ for (i = 0; i < CONFIG_USER_MAX; i++) {
+ if (group->users[i]) {
+ sta = group->users[i];
+ group->users[i] = NULL;
+ sta->group_info.cnt--;
+ sta->group_info.map &= ~BIT_ULL(group_id);
+ ecrnx_mu_group_move_head(&mu->update_sta,
+ &sta->group_info.update);
+ }
+ }
+
+ if (group->user_cnt)
+ mu->group_cnt--;
+ group->user_cnt = 0;
+ trace_mu_group_delete(group_id);
+}
+
+/**
+ * ecrnx_mu_group_add_users - Add users to a group
+ *
+ * @mu: pointer on MU info
+ * @group: pointer on group to add users in
+ * @nb_user: number of users to ad
+ * @users: table of user to add
+ *
+ * Add @nb_users to @group (which may already have users)
+ * Each new users is added to the first free position.
+ * It is assume that @group has at least @nb_user free position. If it is not
+ * case it only add the number of users needed to complete the group.
+ * Each users (effectively added to @group) is also added to the update_sta
+ * list, so that group info will be resent to fw for this user.
+ */
+static inline
+void ecrnx_mu_group_add_users(struct ecrnx_mu_info *mu,
+ struct ecrnx_mu_group *group,
+ int nb_user, struct ecrnx_sta **users)
+{
+ int i, j, group_id = group->group_id;
+
+ if (!group->user_cnt)
+ mu->group_cnt++;
+
+ j = 0;
+ for (i = 0; i < nb_user ; i++) {
+ for (; j < CONFIG_USER_MAX ; j++) {
+ if (group->users[j] == NULL) {
+ group->users[j] = users[i];
+ users[i]->group_info.cnt ++;
+ users[i]->group_info.map |= BIT_ULL(group_id);
+
+ ecrnx_mu_group_move_head(&(mu->update_sta),
+ &(users[i]->group_info.update));
+ group->user_cnt ++;
+ j ++;
+ break;
+ }
+
+ WARN(j == (CONFIG_USER_MAX - 1),
+ "Too many user for group %d (nb_user=%d)",
+ group_id, group->user_cnt + nb_user - i);
+ }
+ }
+
+ trace_mu_group_update(group);
+}
+
+
+/**
+ * ecrnx_mu_group_create_one - create on group with a specific group of user
+ *
+ * @mu: pointer on MU info
+ * @nb_user: number of user to include in the group (<= CONFIG_USER_MAX)
+ * @users: table of users
+ *
+ * Try to create a new group with a specific group of users.
+ * 1- First it checks if a group containing all this users already exists.
+ *
+ * 2- Then it checks if it is possible to complete a group which already
+ * contains at least one user.
+ *
+ * 3- Finally it create a new group. To do so, it take take the last group of
+ * the active_groups list, remove all its current users and add the new ones
+ *
+ * In all cases, the group selected is moved at the top of the active_groups
+ * list
+ *
+ * @return 1 if a new group has been created and 0 otherwise
+ */
+static
+int ecrnx_mu_group_create_one(struct ecrnx_mu_info *mu, int nb_user,
+ struct ecrnx_sta **users, int *nb_group_left)
+{
+ int i, group_id;
+ struct ecrnx_mu_group *group;
+ u64 group_match;
+ u64 group_avail;
+
+ group_match = users[0]->group_info.map;
+ group_avail = users[0]->group_info.map;
+ for (i = 1; i < nb_user ; i++) {
+ group_match &= users[i]->group_info.map;
+ group_avail |= users[i]->group_info.map;
+
+ }
+
+ if (group_match) {
+ /* a group (or more) with all the users already exist */
+ group_id = ECRNX_GET_FIRST_GROUP_ID(group_match);
+ group = ecrnx_mu_group_from_id(mu, group_id);
+ ecrnx_mu_group_move_head(&mu->active_groups, &group->list);
+ return 0;
+ }
+
+#if CONFIG_USER_MAX > 2
+ if (group_avail) {
+ /* check if we can complete a group */
+ struct ecrnx_sta *users2[CONFIG_USER_MAX];
+ int nb_user2;
+
+ group_for_each(group_id, group_avail) {
+ group = ecrnx_mu_group_from_id(mu, group_id);
+ if (group->user_cnt == CONFIG_USER_MAX)
+ continue;
+
+ nb_user2 = 0;
+ for (i = 0; i < nb_user ; i++) {
+ if (!(users[i]->group_info.map & BIT_ULL(group_id))) {
+ users2[nb_user2] = users[i];
+ nb_user2++;
+ }
+ }
+
+ if ((group->user_cnt + nb_user2) <= CONFIG_USER_MAX) {
+ ecrnx_mu_group_add_users(mu, group, nb_user2, users2);
+ ecrnx_mu_group_move_head(&mu->active_groups, &group->list);
+ return 0;
+ }
+ }
+ }
+#endif /* CONFIG_USER_MAX > 2*/
+
+ /* create a new group */
+ group = list_last_entry(&mu->active_groups, struct ecrnx_mu_group, list);
+ ecrnx_mu_group_remove_users(mu, group);
+ ecrnx_mu_group_add_users(mu, group, nb_user, users);
+ ecrnx_mu_group_move_head(&mu->active_groups, &group->list);
+ (*nb_group_left)--;
+
+ return 1;
+}
+
+/**
+ * ecrnx_mu_group_create - Create new groups containing one specific sta
+ *
+ * @mu: pointer on MU info
+ * @sta: sta to add in each group
+ * @nb_group_left: maximum number to new group allowed. (updated on exit)
+ *
+ * This will try to create "all the possible" group with a specific sta being
+ * a member of all these group.
+ * The function simply loops over the @active_sta list (starting from @sta).
+ * When it has (CONFIG_USER_MAX - 1) users it try to create a new group with
+ * these users (plus @sta).
+ * Loops end when there is no more users, or no more new group is allowed
+ *
+ */
+static
+void ecrnx_mu_group_create(struct ecrnx_mu_info *mu, struct ecrnx_sta *sta,
+ int *nb_group_left)
+{
+ struct ecrnx_sta *user_sta = sta;
+ struct ecrnx_sta *users[CONFIG_USER_MAX];
+ int nb_user = 1;
+
+ users[0] = sta;
+ while (*nb_group_left) {
+
+ list_for_each_entry_continue(user_sta, &mu->active_sta, group_info.active) {
+ users[nb_user] = user_sta;
+ if (++nb_user == CONFIG_USER_MAX) {
+ break;
+ }
+ }
+
+ if (nb_user > 1) {
+ if (ecrnx_mu_group_create_one(mu, nb_user, users, nb_group_left))
+ (*nb_group_left)--;
+
+ if (nb_user < CONFIG_USER_MAX)
+ break;
+ else
+ nb_user = 1;
+ } else
+ break;
+ }
+}
+
+/**
+ * ecrnx_mu_group_work - process function of the "group_work"
+ *
+ * The work is scheduled when several sta (MU beamformee capable) are active.
+ * When called, the @active_sta contains the list of the active sta (starting
+ * from the most recent one), and @active_groups is the list of all possible
+ * groups ordered so that the first one is the most recently used.
+ *
+ * This function will create new groups, starting from group containing the
+ * most "active" sta.
+ * For example if the list of sta is :
+ * sta8 -> sta3 -> sta4 -> sta7 -> sta1
+ * and the number of user per group is 3, it will create grooups :
+ * - sta8 / sta3 / sta4
+ * - sta8 / sta7 / sta1
+ * - sta3 / sta4 / sta7
+ * - sta3 / sta1
+ * - sta4 / sta7 / sta1
+ * - sta7 / sta1
+ *
+ * To create new group, the least used group are first selected.
+ * It is only allowed to create NX_MU_GROUP_MAX per iteration.
+ *
+ * Once groups have been updated, mu group information is update to the fw.
+ * To do so it use the @update_sta list to know which sta has been affected.
+ * As it is necessary to wait for fw confirmation before using this new group
+ * MU is temporarily disabled during group update
+ *
+ * Work is then rescheduled.
+ *
+ * At the end of the function, both @active_sta and @update_sta list are empty.
+ *
+ * Note:
+ * - This is still a WIP, and will require more tuning
+ * - not all combinations are created, to avoid to much processing.
+ * - reschedule delay should be adaptative
+ */
+void ecrnx_mu_group_work(struct work_struct *ws)
+{
+ struct delayed_work *dw = container_of(ws, struct delayed_work, work);
+ struct ecrnx_mu_info *mu = container_of(dw, struct ecrnx_mu_info, group_work);
+ struct ecrnx_hw *ecrnx_hw = container_of(mu, struct ecrnx_hw, mu);
+ struct ecrnx_sta *sta, *next;
+ int nb_group_left = NX_MU_GROUP_MAX;
+
+ if (WARN(!ecrnx_hw->mod_params->mutx,
+ "In group formation work, but mutx disabled"))
+ return;
+
+ if (down_interruptible(&mu->lock) != 0)
+ return;
+
+ mu->update_count++;
+ if (!mu->update_count)
+ mu->update_count++;
+
+ list_for_each_entry_safe(sta, next, &mu->active_sta, group_info.active) {
+ if (nb_group_left)
+ ecrnx_mu_group_create(mu, sta, &nb_group_left);
+
+ sta->group_info.last_update = mu->update_count;
+ list_del(&sta->group_info.active);
+ }
+
+ if (! list_empty(&mu->update_sta)) {
+ list_for_each_entry_safe(sta, next, &mu->update_sta, group_info.update) {
+ ecrnx_send_mu_group_update_req(ecrnx_hw, sta);
+ list_del(&sta->group_info.update);
+ }
+ }
+
+ mu->next_group_select = jiffies;
+ ecrnx_mu_group_sta_select(ecrnx_hw);
+ up(&mu->lock);
+
+ return;
+}
+
+/**
+ * ecrnx_mu_group_init - Initialize MU groups
+ *
+ * @ecrnx_hw: main driver data
+ *
+ * Initialize all MU group
+ */
+void ecrnx_mu_group_init(struct ecrnx_hw *ecrnx_hw)
+{
+ struct ecrnx_mu_info *mu = &ecrnx_hw->mu;
+ int i;
+
+ INIT_LIST_HEAD(&mu->active_groups);
+ INIT_LIST_HEAD(&mu->active_sta);
+ INIT_LIST_HEAD(&mu->update_sta);
+
+ for (i = 0; i < NX_MU_GROUP_MAX; i++) {
+ int j;
+ mu->groups[i].user_cnt = 0;
+ mu->groups[i].group_id = i + 1;
+ for (j = 0; j < CONFIG_USER_MAX; j++) {
+ mu->groups[i].users[j] = NULL;
+ }
+ list_add(&mu->groups[i].list, &mu->active_groups);
+ }
+
+ mu->update_count = 1;
+ mu->group_cnt = 0;
+ mu->next_group_select = jiffies;
+ INIT_DELAYED_WORK(&mu->group_work, ecrnx_mu_group_work);
+ sema_init(&mu->lock, 1);
+}
+
+/**
+ * ecrnx_mu_set_active_sta - mark a STA as active
+ *
+ * @ecrnx_hw: main driver data
+ * @sta: pointer to the sta
+ * @traffic: Number of buffers to add in the sta's traffic counter
+ *
+ * If @sta is MU beamformee capable (and MU-MIMO tx is enabled) move the
+ * sta at the top of the @active_sta list.
+ * It also schedule the group_work if not already scheduled and the list
+ * contains more than one sta.
+ *
+ * If a STA was already in the list during the last group update
+ * (i.e. sta->group_info.last_update == mu->update_count) it is not added
+ * back to the list until a sta that wasn't active during the last update is
+ * added. This is to avoid scheduling group update with a list of sta that
+ * were all already in the list during previous update.
+ *
+ * It is called with mu->lock taken.
+ */
+void ecrnx_mu_set_active_sta(struct ecrnx_hw *ecrnx_hw, struct ecrnx_sta *sta,
+ int traffic)
+{
+ struct ecrnx_mu_info *mu = &ecrnx_hw->mu;
+
+ if (!sta || (sta->group_info.map & ECRNX_SU_GROUP))
+ return;
+
+ sta->group_info.traffic += traffic;
+
+ if ((sta->group_info.last_update != mu->update_count) ||
+ !list_empty(&mu->active_sta)) {
+
+ ecrnx_mu_group_move_head(&mu->active_sta, &sta->group_info.active);
+
+ if (!delayed_work_pending(&mu->group_work) &&
+ !list_is_singular(&mu->active_sta)) {
+ schedule_delayed_work(&mu->group_work,
+ msecs_to_jiffies(ECRNX_MU_GROUP_INTERVAL));
+ }
+ }
+}
+
+/**
+ * ecrnx_mu_set_active_group - mark a MU group as active
+ *
+ * @ecrnx_hw: main driver data
+ * @group_id: Group id
+ *
+ * move a group at the top of the @active_groups list
+ */
+void ecrnx_mu_set_active_group(struct ecrnx_hw *ecrnx_hw, int group_id)
+{
+ struct ecrnx_mu_info *mu = &ecrnx_hw->mu;
+ struct ecrnx_mu_group *group = ecrnx_mu_group_from_id(mu, group_id);
+
+ ecrnx_mu_group_move_head(&mu->active_groups, &group->list);
+}
+
+
+/**
+ * ecrnx_mu_group_sta_select - Select the best group for MU stas
+ *
+ * @ecrnx_hw: main driver data
+ *
+ * For each MU capable client of AP interfaces this function tries to select
+ * the best group to use.
+ *
+ * In first pass, gather information from all stations to form statistics
+ * for each group for the previous @ECRNX_MU_GROUP_SELECT_INTERVAL interval:
+ * - number of buffers transmitted
+ * - number of user
+ *
+ * Then groups with more than 2 active users, are assigned after being ordered
+ * by traffic :
+ * - group with highest traffic is selected: set this group for all its users
+ * - update nb_users for all others group (as one sta may be in several groups)
+ * - select the next group that have still mor than 2 users and assign it.
+ * - continue until all group are processed
+ *
+ */
+void ecrnx_mu_group_sta_select(struct ecrnx_hw *ecrnx_hw)
+{
+ struct ecrnx_mu_info *mu = &ecrnx_hw->mu;
+ int nb_users[NX_MU_GROUP_MAX + 1];
+ int traffic[NX_MU_GROUP_MAX + 1];
+ int order[NX_MU_GROUP_MAX + 1];
+ struct ecrnx_sta *sta;
+ struct ecrnx_vif *vif;
+ struct list_head *head;
+ u64 map;
+ int i, j, update, group_id, tmp, cnt = 0;
+
+ if (!mu->group_cnt || time_before(jiffies, mu->next_group_select))
+ return;
+
+ list_for_each_entry(vif, &ecrnx_hw->vifs, list) {
+
+ if (ECRNX_VIF_TYPE(vif) != NL80211_IFTYPE_AP)
+ continue;
+
+#ifdef CONFIG_ECRNX_FULLMAC
+ head = &vif->ap.sta_list;
+#else
+ head = &vif->stations;
+#endif /* CONFIG_ECRNX_FULLMAC */
+
+ memset(nb_users, 0, sizeof(nb_users));
+ memset(traffic, 0, sizeof(traffic));
+ list_for_each_entry(sta, head, list) {
+ int sta_traffic = sta->group_info.traffic;
+
+ /* reset statistics for next selection */
+ sta->group_info.traffic = 0;
+ if (sta->group_info.group)
+ trace_mu_group_selection(sta, 0);
+ sta->group_info.group = 0;
+
+ if (sta->group_info.cnt == 0 ||
+ sta_traffic < ECRNX_MU_GROUP_MIN_TRAFFIC)
+ continue;
+
+ group_sta_for_each(sta, group_id, map) {
+ nb_users[group_id]++;
+ traffic[group_id] += sta_traffic;
+
+ /* list group with 2 users or more */
+ if (nb_users[group_id] == 2)
+ order[cnt++] = group_id;
+ }
+ }
+
+ /* reorder list of group with more that 2 users */
+ update = 1;
+ while(update) {
+ update = 0;
+ for (i = 0; i < cnt - 1; i++) {
+ if (traffic[order[i]] < traffic[order[i + 1]]) {
+ tmp = order[i];
+ order[i] = order[i + 1];
+ order[i + 1] = tmp;
+ update = 1;
+ }
+ }
+ }
+
+ /* now assign group in traffic order */
+ for (i = 0; i < cnt ; i ++) {
+ struct ecrnx_mu_group *group;
+ group_id = order[i];
+
+ if (nb_users[group_id] < 2)
+ continue;
+
+ group = ecrnx_mu_group_from_id(mu, group_id);
+ for (j = 0; j < CONFIG_USER_MAX ; j++) {
+ if (group->users[j]) {
+ trace_mu_group_selection(group->users[j], group_id);
+ group->users[j]->group_info.group = group_id;
+
+ group_sta_for_each(group->users[j], tmp, map) {
+ if (group_id != tmp)
+ nb_users[tmp]--;
+ }
+ }
+ }
+ }
+ }
+
+ mu->next_group_select = jiffies +
+ msecs_to_jiffies(ECRNX_MU_GROUP_SELECT_INTERVAL);
+ mu->next_group_select |= 1;
+}
diff --git a/drivers/net/wireless/eswin/ecrnx_mu_group.h b/drivers/net/wireless/eswin/ecrnx_mu_group.h
new file mode 100644
index 000000000000..1251986dbc29
--- /dev/null
+++ b/drivers/net/wireless/eswin/ecrnx_mu_group.h
@@ -0,0 +1,179 @@
+/**
+ ******************************************************************************
+ *
+ * @file ecrnx_mu_group.h
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+#ifndef _ECRNX_MU_GROUP_H_
+#define _ECRNX_MU_GROUP_H_
+
+#include <linux/workqueue.h>
+#include <linux/semaphore.h>
+
+struct ecrnx_hw;
+struct ecrnx_sta;
+
+#ifdef CONFIG_ECRNX_MUMIMO_TX
+
+/**
+ * struct ecrnx_sta_group_info - Group Information for a STA
+ *
+ * @active: node for @mu->active_sta list
+ * @update: node for @mu->update_sta list
+ * @cnt: Number of groups the STA belongs to
+ * @map: Bitfield of groups the sta belongs to
+ * @traffic: Number of buffers sent since previous group selection
+ * @group: Id of the group selected by previous group selection
+ * (cf @ecrnx_mu_group_sta_select)
+ */
+struct ecrnx_sta_group_info {
+ struct list_head active;
+ struct list_head update;
+ u16 last_update;
+ int cnt;
+ u64 map;
+ int traffic;
+ u8 group;
+};
+
+/**
+ * struct mu_group_info - Information about the users of a group
+ *
+ * @list: node for mu->active_groups
+ * @group_id: Group identifier
+ * @user_cnt: Number of the users in the group
+ * @users: Pointer to the sta, ordered by user position
+ */
+struct ecrnx_mu_group {
+ struct list_head list;
+ int group_id;
+ int user_cnt;
+ struct ecrnx_sta *users[CONFIG_USER_MAX];
+};
+
+/**
+ * struct ecrnx_mu_info - Information about all MU group
+ *
+ * @active_groups: List of all possible groups. Ordered from the most recently
+ * used one to the least one (and possibly never used)
+ * @active_sta: List of MU beamformee sta that have been active (since previous
+ * group update). Ordered from the most recently active.
+ * @update_sta: List of sta whose group information has changed and need to be
+ * updated at fw level
+ * @groups: Table of all groups
+ * @group_work: Work item used to schedule group update
+ * @update_count: Counter used to identify the last group formation update.
+ * (cf ecrnx_sta_group_info.last_update)
+ * @lock: Lock taken during group update. If tx happens lock is taken, then tx
+ * will not used MU.
+ * @next_group_assign: Next time the group selection should be run
+ * (ref @ecrnx_mu_group_sta_select)
+ * @group_cnt: Number of group created
+ */
+struct ecrnx_mu_info {
+ struct list_head active_groups;
+ struct list_head active_sta;
+ struct list_head update_sta;
+ struct ecrnx_mu_group groups[NX_MU_GROUP_MAX];
+ struct delayed_work group_work;
+ u16 update_count;
+ struct semaphore lock;
+ unsigned long next_group_select;
+ u8 group_cnt;
+};
+
+#define ECRNX_SU_GROUP BIT_ULL(0)
+#define ECRNX_MU_GROUP_MASK 0x7ffffffffffffffeULL
+#define ECRNX_MU_GROUP_INTERVAL 200 /* in ms */
+#define ECRNX_MU_GROUP_SELECT_INTERVAL 100 /* in ms */
+// minimum traffic in a ECRNX_MU_GROUP_SELECT_INTERVAL to consider the sta
+#define ECRNX_MU_GROUP_MIN_TRAFFIC 50 /* in number of packet */
+
+
+#define ECRNX_GET_FIRST_GROUP_ID(map) (fls64(map) - 1)
+
+#define group_sta_for_each(sta, id, map) \
+ map = sta->group_info.map & ECRNX_MU_GROUP_MASK; \
+ for (id = (fls64(map) - 1) ; id > 0 ; \
+ map &= ~(u64)BIT_ULL(id), id = (fls64(map) - 1))
+
+#define group_for_each(id, map) \
+ for (id = (fls64(map) - 1) ; id > 0 ; \
+ map &= ~(u64)BIT_ULL(id), id = (fls64(map) - 1))
+
+#define ECRNX_MUMIMO_INFO_POS_ID(info) (((info) >> 6) & 0x3)
+#define ECRNX_MUMIMO_INFO_GROUP_ID(info) ((info) & 0x3f)
+
+static inline
+struct ecrnx_mu_group *ecrnx_mu_group_from_id(struct ecrnx_mu_info *mu, int id)
+{
+ if (id > NX_MU_GROUP_MAX)
+ return NULL;
+
+ return &mu->groups[id - 1];
+}
+
+
+void ecrnx_mu_group_sta_init(struct ecrnx_sta *sta,
+ const struct ieee80211_vht_cap *vht_cap);
+void ecrnx_mu_group_sta_del(struct ecrnx_hw *ecrnx_hw, struct ecrnx_sta *sta);
+u64 ecrnx_mu_group_sta_get_map(struct ecrnx_sta *sta);
+int ecrnx_mu_group_sta_get_pos(struct ecrnx_hw *ecrnx_hw, struct ecrnx_sta *sta,
+ int group_id);
+
+void ecrnx_mu_group_init(struct ecrnx_hw *ecrnx_hw);
+
+void ecrnx_mu_set_active_sta(struct ecrnx_hw *ecrnx_hw, struct ecrnx_sta *sta,
+ int traffic);
+void ecrnx_mu_set_active_group(struct ecrnx_hw *ecrnx_hw, int group_id);
+void ecrnx_mu_group_sta_select(struct ecrnx_hw *ecrnx_hw);
+
+
+#else /* ! CONFIG_ECRNX_MUMIMO_TX */
+
+static inline
+void ecrnx_mu_group_sta_init(struct ecrnx_sta *sta,
+ const struct ieee80211_vht_cap *vht_cap)
+{}
+
+static inline
+void ecrnx_mu_group_sta_del(struct ecrnx_hw *ecrnx_hw, struct ecrnx_sta *sta)
+{}
+
+static inline
+u64 ecrnx_mu_group_sta_get_map(struct ecrnx_sta *sta)
+{
+ return 0;
+}
+
+static inline
+int ecrnx_mu_group_sta_get_pos(struct ecrnx_hw *ecrnx_hw, struct ecrnx_sta *sta,
+ int group_id)
+{
+ return 0;
+}
+
+static inline
+void ecrnx_mu_group_init(struct ecrnx_hw *ecrnx_hw)
+{}
+
+static inline
+void ecrnx_mu_set_active_sta(struct ecrnx_hw *ecrnx_hw, struct ecrnx_sta *sta,
+ int traffic)
+{}
+
+static inline
+void ecrnx_mu_set_active_group(struct ecrnx_hw *ecrnx_hw, int group_id)
+{}
+
+static inline
+void ecrnx_mu_group_sta_select(struct ecrnx_hw *ecrnx_hw)
+{}
+
+#endif /* CONFIG_ECRNX_MUMIMO_TX */
+
+#endif /* _ECRNX_MU_GROUP_H_ */
+
diff --git a/drivers/net/wireless/eswin/ecrnx_platform.c b/drivers/net/wireless/eswin/ecrnx_platform.c
new file mode 100644
index 000000000000..76295c238105
--- /dev/null
+++ b/drivers/net/wireless/eswin/ecrnx_platform.c
@@ -0,0 +1,1107 @@
+/**
+ ******************************************************************************
+ *
+ * @file ecrnx_platform.c
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+
+#include <linux/module.h>
+#include <linux/firmware.h>
+#include <linux/delay.h>
+
+#include "ecrnx_platform.h"
+#include "reg_access.h"
+#include "hal_desc.h"
+#include "ecrnx_main.h"
+
+#ifndef CONFIG_ECRNX_ESWIN
+#include "ecrnx_pci.h"
+#ifndef CONFIG_ECRNX_FHOST
+#include "ipc_host.h"
+#endif /* !CONFIG_ECRNX_FHOST */
+#endif /* CONFIG_ECRNX_ESWIN */
+
+#if defined(CONFIG_ECRNX_ESWIN_SDIO)
+#include "sdio.h"
+#include "ecrnx_sdio.h"
+#elif defined(CONFIG_ECRNX_ESWIN_USB)
+#include "usb.h"
+#include "ecrnx_usb.h"
+#endif
+#ifdef CONFIG_ECRNX_WIFO_CAIL
+#include "core.h"
+#include "ecrnx_amt.h"
+#endif
+
+#ifdef CONFIG_ECRNX_TL4
+/**
+ * ecrnx_plat_tl4_fw_upload() - Load the requested FW into embedded side.
+ *
+ * @ecrnx_plat: pointer to platform structure
+ * @fw_addr: Virtual address where the fw must be loaded
+ * @filename: Name of the fw.
+ *
+ * Load a fw, stored as a hex file, into the specified address
+ */
+static int ecrnx_plat_tl4_fw_upload(struct ecrnx_plat *ecrnx_plat, u8* fw_addr,
+ char *filename)
+{
+ struct device *dev = ecrnx_platform_get_dev(ecrnx_plat);
+ const struct firmware *fw;
+ int err = 0;
+ u32 *dst;
+ u8 const *file_data;
+ char typ0, typ1;
+ u32 addr0, addr1;
+ u32 dat0, dat1;
+ int remain;
+
+ err = request_firmware(&fw, filename, dev);
+ if (err) {
+ return err;
+ }
+ file_data = fw->data;
+ remain = fw->size;
+
+ /* Copy the file on the Embedded side */
+ dev_dbg(dev, "\n### Now copy %s firmware, @ = %p\n", filename, fw_addr);
+
+ /* Walk through all the lines of the configuration file */
+ while (remain >= 16) {
+ u32 data, offset;
+
+ if (sscanf(file_data, "%c:%08X %04X", &typ0, &addr0, &dat0) != 3)
+ break;
+ if ((addr0 & 0x01) != 0) {
+ addr0 = addr0 - 1;
+ dat0 = 0;
+ } else {
+ file_data += 16;
+ remain -= 16;
+ }
+ if ((remain < 16) ||
+ (sscanf(file_data, "%c:%08X %04X", &typ1, &addr1, &dat1) != 3) ||
+ (typ1 != typ0) || (addr1 != (addr0 + 1))) {
+ typ1 = typ0;
+ addr1 = addr0 + 1;
+ dat1 = 0;
+ } else {
+ file_data += 16;
+ remain -= 16;
+ }
+
+ if (typ0 == 'C') {
+ offset = 0x00200000;
+ if ((addr1 % 4) == 3)
+ offset += 2*(addr1 - 3);
+ else
+ offset += 2*(addr1 + 1);
+
+ data = dat1 | (dat0 << 16);
+ } else {
+ offset = 2*(addr1 - 1);
+ data = dat0 | (dat1 << 16);
+ }
+ dst = (u32 *)(fw_addr + offset);
+ *dst = data;
+ }
+
+ release_firmware(fw);
+
+ return err;
+}
+#endif
+
+/**
+ * ecrnx_plat_bin_fw_upload() - Load the requested binary FW into embedded side.
+ *
+ * @ecrnx_plat: pointer to platform structure
+ * @fw_addr: Virtual address where the fw must be loaded
+ * @filename: Name of the fw.
+ *
+ * Load a fw, stored as a binary file, into the specified address
+ */
+#ifndef CONFIG_ECRNX_ESWIN
+static int ecrnx_plat_bin_fw_upload(struct ecrnx_plat *ecrnx_plat, u8* fw_addr,
+ char *filename)
+{
+ const struct firmware *fw;
+ struct device *dev = ecrnx_platform_get_dev(ecrnx_plat);
+ int err = 0;
+ unsigned int i, size;
+ u32 *src, *dst;
+
+ err = request_firmware(&fw, filename, dev);
+ if (err) {
+ return err;
+ }
+
+ /* Copy the file on the Embedded side */
+ dev_dbg(dev, "\n### Now copy %s firmware, @ = %p\n", filename, fw_addr);
+
+ src = (u32 *)fw->data;
+ dst = (u32 *)fw_addr;
+ size = (unsigned int)fw->size;
+
+ /* check potential platform bug on multiple stores vs memcpy */
+ for (i = 0; i < size; i += 4) {
+ *dst++ = *src++;
+ }
+
+ release_firmware(fw);
+
+ return err;
+}
+#endif
+
+#ifndef CONFIG_ECRNX_TL4
+#define IHEX_REC_DATA 0
+#define IHEX_REC_EOF 1
+#define IHEX_REC_EXT_SEG_ADD 2
+#define IHEX_REC_START_SEG_ADD 3
+#define IHEX_REC_EXT_LIN_ADD 4
+#define IHEX_REC_START_LIN_ADD 5
+
+/**
+ * ecrnx_plat_ihex_fw_upload() - Load the requested intel hex 8 FW into embedded side.
+ *
+ * @ecrnx_plat: pointer to platform structure
+ * @fw_addr: Virtual address where the fw must be loaded
+ * @filename: Name of the fw.
+ *
+ * Load a fw, stored as a ihex file, into the specified address.
+ */
+#ifndef CONFIG_ECRNX_ESWIN
+static int ecrnx_plat_ihex_fw_upload(struct ecrnx_plat *ecrnx_plat, u8* fw_addr,
+ char *filename)
+{
+ const struct firmware *fw;
+ struct device *dev = ecrnx_platform_get_dev(ecrnx_plat);
+ u8 const *src, *end;
+ u32 *dst;
+ u16 haddr, segaddr, addr;
+ u32 hwaddr;
+ u8 load_fw, byte_count, checksum, csum, rec_type;
+ int err, rec_idx;
+ char hex_buff[9];
+
+ err = request_firmware(&fw, filename, dev);
+ if (err) {
+ return err;
+ }
+
+ /* Copy the file on the Embedded side */
+ dev_dbg(dev, "\n### Now copy %s firmware, @ = %p\n", filename, fw_addr);
+
+ src = fw->data;
+ end = src + (unsigned int)fw->size;
+ haddr = 0;
+ segaddr = 0;
+ load_fw = 1;
+ err = -EINVAL;
+ rec_idx = 0;
+ hwaddr = 0;
+
+#define IHEX_READ8(_val, _cs) { \
+ hex_buff[2] = 0; \
+ strncpy(hex_buff, src, 2); \
+ if (kstrtou8(hex_buff, 16, &_val)) \
+ goto end; \
+ src += 2; \
+ if (_cs) \
+ csum += _val; \
+ }
+
+#define IHEX_READ16(_val) { \
+ hex_buff[4] = 0; \
+ strncpy(hex_buff, src, 4); \
+ if (kstrtou16(hex_buff, 16, &_val)) \
+ goto end; \
+ src += 4; \
+ csum += (_val & 0xff) + (_val >> 8); \
+ }
+
+#define IHEX_READ32(_val) { \
+ hex_buff[8] = 0; \
+ strncpy(hex_buff, src, 8); \
+ if (kstrtouint(hex_buff, 16, &_val)) \
+ goto end; \
+ src += 8; \
+ csum += (_val & 0xff) + ((_val >> 8) & 0xff) + \
+ ((_val >> 16) & 0xff) + (_val >> 24); \
+ }
+
+#define IHEX_READ32_PAD(_val, _nb) { \
+ memset(hex_buff, '0', 8); \
+ hex_buff[8] = 0; \
+ strncpy(hex_buff, src, (2 * _nb)); \
+ if (kstrtouint(hex_buff, 16, &_val)) \
+ goto end; \
+ src += (2 * _nb); \
+ csum += (_val & 0xff) + ((_val >> 8) & 0xff) + \
+ ((_val >> 16) & 0xff) + (_val >> 24); \
+}
+
+ /* loop until end of file is read*/
+ while (load_fw) {
+ rec_idx++;
+ csum = 0;
+
+ /* Find next colon start code */
+ while (*src != ':') {
+ src++;
+ if ((src + 3) >= end) /* 3 = : + rec_len */
+ goto end;
+ }
+ src++;
+
+ /* Read record len */
+ IHEX_READ8(byte_count, 1);
+ if ((src + (byte_count * 2) + 8) >= end) /* 8 = rec_addr + rec_type + chksum */
+ goto end;
+
+ /* Read record addr */
+ IHEX_READ16(addr);
+
+ /* Read record type */
+ IHEX_READ8(rec_type, 1);
+
+ switch(rec_type) {
+ case IHEX_REC_DATA:
+ {
+ /* Update destination address */
+ dst = (u32 *) (fw_addr + hwaddr + addr);
+
+ while (byte_count) {
+ u32 val;
+ if (byte_count >= 4) {
+ IHEX_READ32(val);
+ byte_count -= 4;
+ } else {
+ IHEX_READ32_PAD(val, byte_count);
+ byte_count = 0;
+ }
+ *dst++ = __swab32(val);
+ }
+ break;
+ }
+ case IHEX_REC_EOF:
+ {
+ load_fw = 0;
+ err = 0;
+ break;
+ }
+ case IHEX_REC_EXT_SEG_ADD: /* Extended Segment Address */
+ {
+ IHEX_READ16(segaddr);
+ hwaddr = (haddr << 16) + (segaddr << 4);
+ break;
+ }
+ case IHEX_REC_EXT_LIN_ADD: /* Extended Linear Address */
+ {
+ IHEX_READ16(haddr);
+ hwaddr = (haddr << 16) + (segaddr << 4);
+ break;
+ }
+ case IHEX_REC_START_LIN_ADD: /* Start Linear Address */
+ {
+ u32 val;
+ IHEX_READ32(val); /* need to read for checksum */
+ break;
+ }
+ case IHEX_REC_START_SEG_ADD:
+ default:
+ {
+ dev_err(dev, "ihex: record type %d not supported\n", rec_type);
+ load_fw = 0;
+ }
+ }
+
+ /* Read and compare checksum */
+ IHEX_READ8(checksum, 0);
+ if (checksum != (u8)(~csum + 1))
+ goto end;
+ }
+
+#undef IHEX_READ8
+#undef IHEX_READ16
+#undef IHEX_READ32
+#undef IHEX_READ32_PAD
+
+ end:
+ release_firmware(fw);
+
+ if (err)
+ dev_err(dev, "%s: Invalid ihex record around line %d\n", filename, rec_idx);
+
+ return err;
+}
+#endif /* CONFIG_ECRNX_TL4 */
+#endif
+
+#ifndef CONFIG_ECRNX_ESWIN
+#ifndef CONFIG_ECRNX_SDM
+/**
+ * ecrnx_plat_get_rf() - Retrun the RF used in the platform
+ *
+ * @ecrnx_plat: pointer to platform structure
+ */
+static u32 ecrnx_plat_get_rf(struct ecrnx_plat *ecrnx_plat)
+{
+ u32 ver;
+ ver = ECRNX_REG_READ(ecrnx_plat, ECRNX_ADDR_SYSTEM, MDM_HDMCONFIG_ADDR);
+
+ ver = __MDM_PHYCFG_FROM_VERS(ver);
+ WARN(((ver != MDM_PHY_CONFIG_TRIDENT) &&
+ (ver != MDM_PHY_CONFIG_CATAXIA) &&
+ (ver != MDM_PHY_CONFIG_KARST)),
+ "Unknown PHY version 0x%08x\n", ver);
+
+ return ver;
+}
+#endif
+
+/**
+ * ecrnx_plat_get_clkctrl_addr() - Return the clock control register address
+ *
+ * @ecrnx_plat: platform data
+ */
+#ifndef CONFIG_ECRNX_SDM
+#ifndef CONFIG_ECRNX_ESWIN
+static u32 ecrnx_plat_get_clkctrl_addr(struct ecrnx_plat *ecrnx_plat)
+{
+ u32 regval;
+ if (ecrnx_plat_get_rf(ecrnx_plat) == MDM_PHY_CONFIG_TRIDENT)
+ return MDM_MEMCLKCTRL0_ADDR;
+ regval = ECRNX_REG_READ(ecrnx_plat, ECRNX_ADDR_SYSTEM, SYSCTRL_SIGNATURE_ADDR);
+ if (__FPGA_TYPE(regval) == 0xC0CA)
+ return CRM_CLKGATEFCTRL0_ADDR;
+ else
+ return MDM_CLKGATEFCTRL0_ADDR;
+}
+#endif /* CONFIG_ECRNX_SDM */
+#endif
+
+/**
+ * ecrnx_plat_stop_agcfsm() - Stop a AGC state machine
+ *
+ * @ecrnx_plat: pointer to platform structure
+ * @agg_reg: Address of the agccntl register (within ECRNX_ADDR_SYSTEM)
+ * @agcctl: Updated with value of the agccntl rgister before stop
+ * @memclk: Updated with value of the clock register before stop
+ * @agc_ver: Version of the AGC load procedure
+ * @clkctrladdr: Indicates which AGC clock register should be accessed
+ */
+#ifndef CONFIG_ECRNX_ESWIN
+static void ecrnx_plat_stop_agcfsm(struct ecrnx_plat *ecrnx_plat, int agc_reg,
+ u32 *agcctl, u32 *memclk, u8 agc_ver,
+ u32 clkctrladdr)
+{
+ /* First read agcctnl and clock registers */
+ *memclk = ECRNX_REG_READ(ecrnx_plat, ECRNX_ADDR_SYSTEM, clkctrladdr);
+
+ /* Stop state machine : xxAGCCNTL0[AGCFSMRESET]=1 */
+ *agcctl = ECRNX_REG_READ(ecrnx_plat, ECRNX_ADDR_SYSTEM, agc_reg);
+ ECRNX_REG_WRITE((*agcctl) | BIT(12), ecrnx_plat, ECRNX_ADDR_SYSTEM, agc_reg);
+
+ /* Force clock */
+ if (agc_ver > 0) {
+ /* CLKGATEFCTRL0[AGCCLKFORCE]=1 */
+ ECRNX_REG_WRITE((*memclk) | BIT(29), ecrnx_plat, ECRNX_ADDR_SYSTEM,
+ clkctrladdr);
+ } else {
+ /* MEMCLKCTRL0[AGCMEMCLKCTRL]=0 */
+ ECRNX_REG_WRITE((*memclk) & ~BIT(3), ecrnx_plat, ECRNX_ADDR_SYSTEM,
+ clkctrladdr);
+ }
+}
+#endif
+
+/**
+ * ecrnx_plat_start_agcfsm() - Restart a AGC state machine
+ *
+ * @ecrnx_plat: pointer to platform structure
+ * @agg_reg: Address of the agccntl register (within ECRNX_ADDR_SYSTEM)
+ * @agcctl: value of the agccntl register to restore
+ * @memclk: value of the clock register to restore
+ * @agc_ver: Version of the AGC load procedure
+ * @clkctrladdr: Indicates which AGC clock register should be accessed
+ */
+#ifndef CONFIG_ECRNX_ESWIN
+static void ecrnx_plat_start_agcfsm(struct ecrnx_plat *ecrnx_plat, int agc_reg,
+ u32 agcctl, u32 memclk, u8 agc_ver,
+ u32 clkctrladdr)
+{
+
+ /* Release clock */
+ if (agc_ver > 0)
+ /* CLKGATEFCTRL0[AGCCLKFORCE]=0 */
+ ECRNX_REG_WRITE(memclk & ~BIT(29), ecrnx_plat, ECRNX_ADDR_SYSTEM,
+ clkctrladdr);
+ else
+ /* MEMCLKCTRL0[AGCMEMCLKCTRL]=1 */
+ ECRNX_REG_WRITE(memclk | BIT(3), ecrnx_plat, ECRNX_ADDR_SYSTEM,
+ clkctrladdr);
+
+ /* Restart state machine: xxAGCCNTL0[AGCFSMRESET]=0 */
+ ECRNX_REG_WRITE(agcctl & ~BIT(12), ecrnx_plat, ECRNX_ADDR_SYSTEM, agc_reg);
+}
+#endif
+#endif
+
+/**
+ * ecrnx_plat_get_agc_load_version() - Return the agc load protocol version and the
+ * address of the clock control register
+ *
+ * @ecrnx_plat: platform data
+ * @rf: rf in used
+ * @clkctrladdr: returned clock control register address
+ *
+ */
+#ifndef CONFIG_ECRNX_ESWIN
+#ifndef CONFIG_ECRNX_SDM
+static u8 ecrnx_plat_get_agc_load_version(struct ecrnx_plat *ecrnx_plat, u32 rf,
+ u32 *clkctrladdr)
+{
+ u8 agc_load_ver = 0;
+ u32 agc_ver;
+ u32 regval;
+
+ *clkctrladdr = ecrnx_plat_get_clkctrl_addr(ecrnx_plat);
+ /* Trident and Elma PHY use old method */
+ if (rf == MDM_PHY_CONFIG_TRIDENT)
+ return 0;
+
+ /* Get the FPGA signature */
+ regval = ECRNX_REG_READ(ecrnx_plat, ECRNX_ADDR_SYSTEM, SYSCTRL_SIGNATURE_ADDR);
+
+
+ /* Read RIU version register */
+ agc_ver = ECRNX_REG_READ(ecrnx_plat, ECRNX_ADDR_SYSTEM, RIU_ECRNXVERSION_ADDR);
+ agc_load_ver = __RIU_AGCLOAD_FROM_VERS(agc_ver);
+
+ return agc_load_ver;
+}
+#endif /* CONFIG_ECRNX_SDM */
+#endif
+
+/**
+ * ecrnx_plat_agc_load() - Load AGC ucode
+ *
+ * @ecrnx_plat: platform data
+ */
+#ifndef CONFIG_ECRNX_ESWIN
+static int ecrnx_plat_agc_load(struct ecrnx_plat *ecrnx_plat)
+{
+ int ret = 0;
+#ifndef CONFIG_ECRNX_SDM
+ u32 agc = 0, agcctl, memclk;
+ u32 clkctrladdr;
+ u32 rf = ecrnx_plat_get_rf(ecrnx_plat);
+ u8 agc_ver;
+
+ switch (rf) {
+ case MDM_PHY_CONFIG_TRIDENT:
+ agc = AGC_ECRNXAGCCNTL_ADDR;
+ break;
+ case MDM_PHY_CONFIG_CATAXIA:
+ case MDM_PHY_CONFIG_KARST:
+ agc = RIU_ECRNXAGCCNTL_ADDR;
+ break;
+ default:
+ return -1;
+ }
+
+ agc_ver = ecrnx_plat_get_agc_load_version(ecrnx_plat, rf, &clkctrladdr);
+
+ ecrnx_plat_stop_agcfsm(ecrnx_plat, agc, &agcctl, &memclk, agc_ver, clkctrladdr);
+
+ ret = ecrnx_plat_bin_fw_upload(ecrnx_plat,
+ ECRNX_ADDR(ecrnx_plat, ECRNX_ADDR_SYSTEM, PHY_AGC_UCODE_ADDR),
+ ECRNX_AGC_FW_NAME);
+
+ if (!ret && (agc_ver == 1)) {
+ /* Run BIST to ensure that the AGC RAM was correctly loaded */
+ ECRNX_REG_WRITE(BIT(28), ecrnx_plat, ECRNX_ADDR_SYSTEM,
+ RIU_ECRNXDYNAMICCONFIG_ADDR);
+ while (ECRNX_REG_READ(ecrnx_plat, ECRNX_ADDR_SYSTEM,
+ RIU_ECRNXDYNAMICCONFIG_ADDR) & BIT(28));
+
+ if (!(ECRNX_REG_READ(ecrnx_plat, ECRNX_ADDR_SYSTEM,
+ RIU_AGCMEMBISTSTAT_ADDR) & BIT(0))) {
+ dev_err(ecrnx_platform_get_dev(ecrnx_plat),
+ "AGC RAM not loaded correctly 0x%08x\n",
+ ECRNX_REG_READ(ecrnx_plat, ECRNX_ADDR_SYSTEM,
+ RIU_AGCMEMSIGNATURESTAT_ADDR));
+ ret = -EIO;
+ }
+ }
+
+ ecrnx_plat_start_agcfsm(ecrnx_plat, agc, agcctl, memclk, agc_ver, clkctrladdr);
+
+#endif
+ return ret;
+}
+#endif
+
+/**
+ * ecrnx_ldpc_load() - Load LDPC RAM
+ *
+ * @ecrnx_hw: Main driver data
+ */
+#ifndef CONFIG_ECRNX_ESWIN
+static int ecrnx_ldpc_load(struct ecrnx_hw *ecrnx_hw)
+{
+#ifndef CONFIG_ECRNX_SDM
+ struct ecrnx_plat *ecrnx_plat = ecrnx_hw->plat;
+ u32 rf = ecrnx_plat_get_rf(ecrnx_plat);
+ u32 phy_feat = ECRNX_REG_READ(ecrnx_plat, ECRNX_ADDR_SYSTEM, MDM_HDMCONFIG_ADDR);
+ u32 phy_vers = ECRNX_REG_READ(ecrnx_plat, ECRNX_ADDR_SYSTEM, MDM_HDMVERSION_ADDR);
+
+ if (((rf != MDM_PHY_CONFIG_KARST) && (rf != MDM_PHY_CONFIG_CATAXIA)) ||
+ (phy_feat & (MDM_LDPCDEC_BIT | MDM_LDPCENC_BIT)) !=
+ (MDM_LDPCDEC_BIT | MDM_LDPCENC_BIT)) {
+ goto disable_ldpc;
+ }
+ if (__MDM_VERSION(phy_vers) > 30) {
+ return 0;
+ }
+
+ if (ecrnx_plat_bin_fw_upload(ecrnx_plat,
+ ECRNX_ADDR(ecrnx_plat, ECRNX_ADDR_SYSTEM, PHY_LDPC_RAM_ADDR),
+ ECRNX_LDPC_RAM_NAME)) {
+ goto disable_ldpc;
+ }
+
+ return 0;
+
+ disable_ldpc:
+ ecrnx_hw->mod_params->ldpc_on = false;
+
+#endif /* CONFIG_ECRNX_SDM */
+ return 0;
+}
+#endif
+/**
+ * ecrnx_plat_lmac_load() - Load FW code
+ *
+ * @ecrnx_plat: platform data
+ */
+#ifndef CONFIG_ECRNX_ESWIN
+static int ecrnx_plat_lmac_load(struct ecrnx_plat *ecrnx_plat)
+{
+ int ret;
+
+ #ifdef CONFIG_ECRNX_TL4
+ ret = ecrnx_plat_tl4_fw_upload(ecrnx_plat,
+ ECRNX_ADDR(ecrnx_plat, ECRNX_ADDR_CPU, RAM_LMAC_FW_ADDR),
+ ECRNX_MAC_FW_NAME);
+ #else
+ ret = ecrnx_plat_ihex_fw_upload(ecrnx_plat,
+ ECRNX_ADDR(ecrnx_plat, ECRNX_ADDR_CPU, RAM_LMAC_FW_ADDR),
+ ECRNX_MAC_FW_NAME);
+ if (ret == -ENOENT)
+ {
+ ret = ecrnx_plat_bin_fw_upload(ecrnx_plat,
+ ECRNX_ADDR(ecrnx_plat, ECRNX_ADDR_CPU, RAM_LMAC_FW_ADDR),
+ ECRNX_MAC_FW_NAME2);
+ }
+ #endif
+
+ return ret;
+}
+#endif
+
+/**
+ * ecrnx_rf_fw_load() - Load RF FW if any
+ *
+ * @ecrnx_hw: Main driver data
+ */
+#ifndef CONFIG_ECRNX_ESWIN
+static int ecrnx_plat_rf_fw_load(struct ecrnx_hw *ecrnx_hw)
+{
+#ifndef CONFIG_ECRNX_SDM
+ struct ecrnx_plat *ecrnx_plat = ecrnx_hw->plat;
+ u32 rf = ecrnx_plat_get_rf(ecrnx_plat);
+ struct device *dev = ecrnx_platform_get_dev(ecrnx_plat);
+ const struct firmware *fw;
+ int err = 0;
+ u8 const *file_data;
+ int remain;
+ u32 clkforce;
+ u32 clkctrladdr;
+
+ // Today only Cataxia has a FW to load
+ if (rf != MDM_PHY_CONFIG_CATAXIA)
+ return 0;
+
+ err = request_firmware(&fw, ECRNX_CATAXIA_FW_NAME, dev);
+ if (err)
+ {
+ dev_err(dev, "Make sure your board has up-to-date packages.");
+ dev_err(dev, "Run \"sudo smart update\" \"sudo smart upgrade\" commands.\n");
+ return err;
+ }
+
+ file_data = fw->data;
+ remain = fw->size;
+
+ // Get address of clock control register
+ clkctrladdr = ecrnx_plat_get_clkctrl_addr(ecrnx_plat);
+
+ // Force RC clock
+ clkforce = ECRNX_REG_READ(ecrnx_plat, ECRNX_ADDR_SYSTEM, clkctrladdr);
+ ECRNX_REG_WRITE(clkforce | BIT(27), ecrnx_plat, ECRNX_ADDR_SYSTEM, clkctrladdr);
+ mdelay(1);
+
+ // Reset RC
+ ECRNX_REG_WRITE(0x00003100, ecrnx_plat, ECRNX_ADDR_SYSTEM, RC_SYSTEM_CONFIGURATION_ADDR);
+ mdelay(20);
+
+ // Reset RF
+ ECRNX_REG_WRITE(0x00133100, ecrnx_plat, ECRNX_ADDR_SYSTEM, RC_SYSTEM_CONFIGURATION_ADDR);
+ mdelay(20);
+
+ // Select trx 2 HB
+ ECRNX_REG_WRITE(0x00103100, ecrnx_plat, ECRNX_ADDR_SYSTEM, RC_SYSTEM_CONFIGURATION_ADDR);
+ mdelay(1);
+
+ // Set ASP freeze
+ ECRNX_REG_WRITE(0xC1010001, ecrnx_plat, ECRNX_ADDR_SYSTEM, RC_ACCES_TO_CATAXIA_REG_ADDR);
+ mdelay(1);
+
+ /* Walk through all the lines of the FW file */
+ while (remain >= 10) {
+ u32 data;
+
+ if (sscanf(file_data, "0x%08X", &data) != 1)
+ {
+ // Corrupted FW file
+ err = -1;
+ break;
+ }
+ file_data += 11;
+ remain -= 11;
+
+ ECRNX_REG_WRITE(data, ecrnx_plat, ECRNX_ADDR_SYSTEM, RC_ACCES_TO_CATAXIA_REG_ADDR);
+ udelay(50);
+ }
+
+ // Clear ASP freeze
+ ECRNX_REG_WRITE(0xE0010011, ecrnx_plat, ECRNX_ADDR_SYSTEM, RC_ACCES_TO_CATAXIA_REG_ADDR);
+ mdelay(1);
+
+ // Unforce RC clock
+ ECRNX_REG_WRITE(clkforce, ecrnx_plat, ECRNX_ADDR_SYSTEM, clkctrladdr);
+
+ release_firmware(fw);
+
+#endif /* CONFIG_ECRNX_SDM */
+ return err;
+}
+#endif
+
+/**
+ * ecrnx_plat_mpif_sel() - Select the MPIF according to the FPGA signature
+ *
+ * @ecrnx_plat: platform data
+ */
+#ifndef CONFIG_ECRNX_ESWIN
+static void ecrnx_plat_mpif_sel(struct ecrnx_plat *ecrnx_plat)
+{
+#ifndef CONFIG_ECRNX_SDM
+ u32 regval;
+ u32 type;
+
+ /* Get the FPGA signature */
+ regval = ECRNX_REG_READ(ecrnx_plat, ECRNX_ADDR_SYSTEM, SYSCTRL_SIGNATURE_ADDR);
+ type = __FPGA_TYPE(regval);
+
+ /* Check if we need to switch to the old MPIF or not */
+ if ((type != 0xCAFE) && (type != 0XC0CA) && (regval & 0xF) < 0x3)
+ {
+ /* A old FPGA A is used, so configure the FPGA B to use the old MPIF */
+ ECRNX_REG_WRITE(0x3, ecrnx_plat, ECRNX_ADDR_SYSTEM, FPGAB_MPIF_SEL_ADDR);
+ }
+#endif
+}
+#endif
+
+/**
+ * ecrnx_platform_reset() - Reset the platform
+ *
+ * @ecrnx_plat: platform data
+ */
+#ifndef CONFIG_ECRNX_ESWIN
+static int ecrnx_platform_reset(struct ecrnx_plat *ecrnx_plat)
+{
+ u32 regval;
+
+ /* the doc states that SOFT implies FPGA_B_RESET
+ * adding FPGA_B_RESET is clearer */
+ ECRNX_REG_WRITE(SOFT_RESET | FPGA_B_RESET, ecrnx_plat,
+ ECRNX_ADDR_SYSTEM, SYSCTRL_MISC_CNTL_ADDR);
+ msleep(100);
+
+ regval = ECRNX_REG_READ(ecrnx_plat, ECRNX_ADDR_SYSTEM, SYSCTRL_MISC_CNTL_ADDR);
+
+ if (regval & SOFT_RESET) {
+ dev_err(ecrnx_platform_get_dev(ecrnx_plat), "reset: failed\n");
+ return -EIO;
+ }
+
+ ECRNX_REG_WRITE(regval & ~FPGA_B_RESET, ecrnx_plat,
+ ECRNX_ADDR_SYSTEM, SYSCTRL_MISC_CNTL_ADDR);
+ msleep(100);
+ return 0;
+}
+#endif
+
+/**
+ * rwmx_platform_save_config() - Save hardware config before reload
+ *
+ * @ecrnx_plat: Pointer to platform data
+ *
+ * Return configuration registers values.
+ */
+#ifndef CONFIG_ECRNX_ESWIN
+static void* ecrnx_term_save_config(struct ecrnx_plat *ecrnx_plat)
+{
+ const u32 *reg_list;
+ u32 *reg_value, *res;
+ int i, size = 0;
+
+ if (ecrnx_plat->get_config_reg) {
+ size = ecrnx_plat->get_config_reg(ecrnx_plat, &reg_list);
+ }
+
+ if (size <= 0)
+ return NULL;
+
+ res = kmalloc(sizeof(u32) * size, GFP_KERNEL);
+ if (!res)
+ return NULL;
+
+ reg_value = res;
+ for (i = 0; i < size; i++) {
+ *reg_value++ = ECRNX_REG_READ(ecrnx_plat, ECRNX_ADDR_SYSTEM,
+ *reg_list++);
+ }
+
+ return res;
+}
+#endif
+
+/**
+ * rwmx_platform_restore_config() - Restore hardware config after reload
+ *
+ * @ecrnx_plat: Pointer to platform data
+ * @reg_value: Pointer of value to restore
+ * (obtained with rwmx_platform_save_config())
+ *
+ * Restore configuration registers value.
+ */
+#ifndef CONFIG_ECRNX_ESWIN
+static void ecrnx_term_restore_config(struct ecrnx_plat *ecrnx_plat,
+ u32 *reg_value)
+{
+ const u32 *reg_list;
+ int i, size = 0;
+
+ if (!reg_value || !ecrnx_plat->get_config_reg)
+ return;
+
+ size = ecrnx_plat->get_config_reg(ecrnx_plat, &reg_list);
+
+ for (i = 0; i < size; i++) {
+ ECRNX_REG_WRITE(*reg_value++, ecrnx_plat, ECRNX_ADDR_SYSTEM,
+ *reg_list++);
+ }
+}
+#endif
+
+#ifndef CONFIG_ECRNX_ESWIN
+static int ecrnx_check_fw_compatibility(struct ecrnx_hw *ecrnx_hw)
+{
+ int res = 0;
+
+ struct ipc_shared_env_tag *shared = ecrnx_hw->ipc_env->shared;
+ #ifdef CONFIG_ECRNX_SOFTMAC
+ struct wiphy *wiphy = ecrnx_hw->hw->wiphy;
+ #else //CONFIG_ECRNX_SOFTMAC
+ struct wiphy *wiphy = ecrnx_hw->wiphy;
+ #endif //CONFIG_ECRNX_SOFTMAC
+ #ifdef CONFIG_ECRNX_OLD_IPC
+ int ipc_shared_version = 10;
+ #else //CONFIG_ECRNX_OLD_IPC
+ int ipc_shared_version = 11;
+ #endif //CONFIG_ECRNX_OLD_IPC
+
+ if(shared->comp_info.ipc_shared_version != ipc_shared_version)
+ {
+ wiphy_err(wiphy, "Different versions of IPC shared version between driver and FW (%d != %d)\n ",
+ ipc_shared_version, shared->comp_info.ipc_shared_version);
+ res = -1;
+ }
+
+ if(shared->comp_info.radarbuf_cnt != IPC_RADARBUF_CNT)
+ {
+ wiphy_err(wiphy, "Different number of host buffers available for Radar events handling "\
+ "between driver and FW (%d != %d)\n", IPC_RADARBUF_CNT,
+ shared->comp_info.radarbuf_cnt);
+ res = -1;
+ }
+
+ if(shared->comp_info.unsuprxvecbuf_cnt != IPC_UNSUPRXVECBUF_CNT)
+ {
+ wiphy_err(wiphy, "Different number of host buffers available for unsupported Rx vectors "\
+ "handling between driver and FW (%d != %d)\n", IPC_UNSUPRXVECBUF_CNT,
+ shared->comp_info.unsuprxvecbuf_cnt);
+ res = -1;
+ }
+
+ #ifdef CONFIG_ECRNX_FULLMAC
+ if(shared->comp_info.rxdesc_cnt != IPC_RXDESC_CNT)
+ {
+ wiphy_err(wiphy, "Different number of shared descriptors available for Data RX handling "\
+ "between driver and FW (%d != %d)\n", IPC_RXDESC_CNT,
+ shared->comp_info.rxdesc_cnt);
+ res = -1;
+ }
+ #endif /* CONFIG_ECRNX_FULLMAC */
+
+ if(shared->comp_info.rxbuf_cnt != IPC_RXBUF_CNT)
+ {
+ wiphy_err(wiphy, "Different number of host buffers available for Data Rx handling "\
+ "between driver and FW (%d != %d)\n", IPC_RXBUF_CNT,
+ shared->comp_info.rxbuf_cnt);
+ res = -1;
+ }
+
+ if(shared->comp_info.msge2a_buf_cnt != IPC_MSGE2A_BUF_CNT)
+ {
+ wiphy_err(wiphy, "Different number of host buffers available for Emb->App MSGs "\
+ "sending between driver and FW (%d != %d)\n", IPC_MSGE2A_BUF_CNT,
+ shared->comp_info.msge2a_buf_cnt);
+ res = -1;
+ }
+
+ if(shared->comp_info.dbgbuf_cnt != IPC_DBGBUF_CNT)
+ {
+ wiphy_err(wiphy, "Different number of host buffers available for debug messages "\
+ "sending between driver and FW (%d != %d)\n", IPC_DBGBUF_CNT,
+ shared->comp_info.dbgbuf_cnt);
+ res = -1;
+ }
+
+ if(shared->comp_info.bk_txq != NX_TXDESC_CNT0)
+ {
+ wiphy_err(wiphy, "Driver and FW have different sizes of BK TX queue (%d != %d)\n",
+ NX_TXDESC_CNT0, shared->comp_info.bk_txq);
+ res = -1;
+ }
+
+ if(shared->comp_info.be_txq != NX_TXDESC_CNT1)
+ {
+ wiphy_err(wiphy, "Driver and FW have different sizes of BE TX queue (%d != %d)\n",
+ NX_TXDESC_CNT1, shared->comp_info.be_txq);
+ res = -1;
+ }
+
+ if(shared->comp_info.vi_txq != NX_TXDESC_CNT2)
+ {
+ wiphy_err(wiphy, "Driver and FW have different sizes of VI TX queue (%d != %d)\n",
+ NX_TXDESC_CNT2, shared->comp_info.vi_txq);
+ res = -1;
+ }
+
+ if(shared->comp_info.vo_txq != NX_TXDESC_CNT3)
+ {
+ wiphy_err(wiphy, "Driver and FW have different sizes of VO TX queue (%d != %d)\n",
+ NX_TXDESC_CNT3, shared->comp_info.vo_txq);
+ res = -1;
+ }
+
+ #if NX_TXQ_CNT == 5
+ if(shared->comp_info.bcn_txq != NX_TXDESC_CNT4)
+ {
+ wiphy_err(wiphy, "Driver and FW have different sizes of BCN TX queue (%d != %d)\n",
+ NX_TXDESC_CNT4, shared->comp_info.bcn_txq);
+ res = -1;
+ }
+ #else
+ if (shared->comp_info.bcn_txq > 0)
+ {
+ wiphy_err(wiphy, "BCMC enabled in firmware but disabled in driver\n");
+ res = -1;
+ }
+ #endif /* NX_TXQ_CNT == 5 */
+
+ if(shared->comp_info.ipc_shared_size != sizeof(ipc_shared_env))
+ {
+ wiphy_err(wiphy, "Different sizes of IPC shared between driver and FW (%zd != %d)\n",
+ sizeof(ipc_shared_env), shared->comp_info.ipc_shared_size);
+ res = -1;
+ }
+
+ if(shared->comp_info.msg_api != MSG_API_VER)
+ {
+ wiphy_err(wiphy, "Different supported message API versions between "\
+ "driver and FW (%d != %d)\n", MSG_API_VER, shared->comp_info.msg_api);
+ res = -1;
+ }
+
+ return res;
+}
+#endif /* !CONFIG_ECRNX_ESWIN */
+
+/**
+ * ecrnx_platform_on() - Start the platform
+ *
+ * @ecrnx_hw: Main driver data
+ * @config: Config to restore (NULL if nothing to restore)
+ *
+ * It starts the platform :
+ * - load fw and ucodes
+ * - initialize IPC
+ * - boot the fw
+ * - enable link communication/IRQ
+ *
+ * Called by 802.11 part
+ */
+int ecrnx_platform_on(struct ecrnx_hw *ecrnx_hw, void *config)
+{
+ u8 *shared_ram;
+ int ret;
+
+ ECRNX_DBG("%s entry!!", __func__);
+ shared_ram = kzalloc(sizeof(struct ipc_shared_env_tag), GFP_KERNEL);
+ if (!shared_ram)
+ return -ENOMEM;
+
+ if ((ret = ecrnx_ipc_init(ecrnx_hw, shared_ram)))
+ return ret;
+
+ ECRNX_DBG("%s exit!!", __func__);
+ return 0;
+}
+
+/**
+ * ecrnx_platform_off() - Stop the platform
+ *
+ * @ecrnx_hw: Main driver data
+ * @config: Updated with pointer to config, to be able to restore it with
+ * ecrnx_platform_on(). It's up to the caller to free the config. Set to NULL
+ * if configuration is not needed.
+ *
+ * Called by 802.11 part
+ */
+void ecrnx_platform_off(struct ecrnx_hw *ecrnx_hw, void **config)
+{
+ ecrnx_ipc_deinit(ecrnx_hw);
+#if defined(CONFIG_ECRNX_ESWIN_SDIO)
+ ecrnx_sdio_deinit(ecrnx_hw);
+#elif defined(CONFIG_ECRNX_ESWIN_USB)
+ ecrnx_usb_deinit(ecrnx_hw);
+#else
+ #error "config error drv";
+#endif
+}
+
+/**
+ * ecrnx_platform_init() - Initialize the platform
+ *
+ * @ecrnx_plat: platform data (already updated by platform driver)
+ * @platform_data: Pointer to store the main driver data pointer (aka ecrnx_hw)
+ * That will be set as driver data for the platform driver
+ * Return: 0 on success, < 0 otherwise
+ *
+ * Called by the platform driver after it has been probed
+ */
+int ecrnx_platform_init(void *ecrnx_plat, void **platform_data)
+{
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+#if defined CONFIG_ECRNX_SOFTMAC
+ return ecrnx_mac80211_init(ecrnx_plat, platform_data);
+#elif defined CONFIG_ECRNX_FULLMAC
+#ifdef CONFIG_ECRNX_WIFO_CAIL
+ if (amt_mode == true) {
+ return ecrnx_amt_init();
+ }
+ else
+#endif
+ return ecrnx_cfg80211_init(ecrnx_plat, platform_data);
+#elif defined CONFIG_ECRNX_FHOST
+ return ecrnx_fhost_init(ecrnx_plat, platform_data);
+#endif
+}
+
+/**
+ * ecrnx_platform_deinit() - Deinitialize the platform
+ *
+ * @ecrnx_hw: main driver data
+ *
+ * Called by the platform driver after it is removed
+ */
+void ecrnx_platform_deinit(struct ecrnx_hw *ecrnx_hw)
+{
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+#if defined CONFIG_ECRNX_SOFTMAC
+ ecrnx_mac80211_deinit(ecrnx_hw);
+#elif defined CONFIG_ECRNX_FULLMAC
+#ifdef CONFIG_ECRNX_WIFO_CAIL
+ if (amt_mode == true) {
+ ecrnx_amt_deinit();
+ }
+ else
+#endif
+ ecrnx_cfg80211_deinit(ecrnx_hw);
+#elif defined CONFIG_ECRNX_FHOST
+ ecrnx_fhost_deinit(ecrnx_hw);
+#endif
+}
+
+
+/**
+ * ecrnx_platform_register_drv() - Register all possible platform drivers
+ */
+int ecrnx_platform_register_drv(void)
+{
+#if defined(CONFIG_ECRNX_ESWIN_SDIO)
+ return ecrnx_sdio_register_drv();
+#elif defined(CONFIG_ECRNX_ESWIN_USB)
+ return ecrnx_usb_register_drv();
+#else
+ #error "config error drv"
+#endif
+}
+
+
+/**
+ * ecrnx_platform_unregister_drv() - Unegister all platform drivers
+ */
+void ecrnx_platform_unregister_drv(void)
+{
+#if defined(CONFIG_ECRNX_ESWIN_SDIO)
+ return ecrnx_sdio_unregister_drv();
+#elif defined(CONFIG_ECRNX_ESWIN_USB)
+ return ecrnx_usb_unregister_drv();
+#else
+ #error "config error drv"
+#endif
+}
+
+
+#ifndef CONFIG_ECRNX_SDM
+MODULE_FIRMWARE(ECRNX_AGC_FW_NAME);
+MODULE_FIRMWARE(ECRNX_FCU_FW_NAME);
+MODULE_FIRMWARE(ECRNX_LDPC_RAM_NAME);
+#endif
+MODULE_FIRMWARE(ECRNX_MAC_FW_NAME);
+#ifndef CONFIG_ECRNX_TL4
+MODULE_FIRMWARE(ECRNX_MAC_FW_NAME2);
+#endif
diff --git a/drivers/net/wireless/eswin/ecrnx_platform.h b/drivers/net/wireless/eswin/ecrnx_platform.h
new file mode 100644
index 000000000000..2c84c5a6e3b6
--- /dev/null
+++ b/drivers/net/wireless/eswin/ecrnx_platform.h
@@ -0,0 +1,142 @@
+/**
+ ******************************************************************************
+ *
+ * @file ecrnx_platorm.h
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+
+#ifndef _ECRNX_PLAT_H_
+#define _ECRNX_PLAT_H_
+
+#include <linux/pci.h>
+
+#define ECRNX_CONFIG_FW_NAME "wifi_ecr6600u.cfg"
+#define ECRNX_PHY_CONFIG_TRD_NAME "ecrnx_trident.ini"
+#define ECRNX_PHY_CONFIG_KARST_NAME "ecrnx_karst.ini"
+#define ECRNX_AGC_FW_NAME "agcram.bin"
+#define ECRNX_LDPC_RAM_NAME "ldpcram.bin"
+#define ECRNX_CATAXIA_FW_NAME "cataxia.fw"
+#ifdef CONFIG_ECRNX_SOFTMAC
+#define ECRNX_MAC_FW_BASE_NAME "lmacfw"
+#elif defined CONFIG_ECRNX_FULLMAC
+#define ECRNX_MAC_FW_BASE_NAME "fmacfw"
+#elif defined CONFIG_ECRNX_FHOST
+#define ECRNX_MAC_FW_BASE_NAME "fhostfw"
+#endif /* CONFIG_ECRNX_SOFTMAC */
+
+#ifdef CONFIG_ECRNX_TL4
+#define ECRNX_MAC_FW_NAME ECRNX_MAC_FW_BASE_NAME".hex"
+#else
+#define ECRNX_MAC_FW_NAME ECRNX_MAC_FW_BASE_NAME".ihex"
+#define ECRNX_MAC_FW_NAME2 ECRNX_MAC_FW_BASE_NAME".bin"
+#endif
+
+#define ECRNX_FCU_FW_NAME "fcuram.bin"
+
+/**
+ * Type of memory to access (cf ecrnx_plat.get_address)
+ *
+ * @ECRNX_ADDR_CPU To access memory of the embedded CPU
+ * @ECRNX_ADDR_SYSTEM To access memory/registers of one subsystem of the
+ * embedded system
+ *
+ */
+enum ecrnx_platform_addr {
+ ECRNX_ADDR_CPU,
+ ECRNX_ADDR_SYSTEM,
+ ECRNX_ADDR_MAX,
+};
+
+struct ecrnx_hw;
+
+/**
+ * struct ecrnx_plat - Operation pointers for ECRNX PCI platform
+ *
+ * @pci_dev: pointer to pci dev
+ * @enabled: Set if embedded platform has been enabled (i.e. fw loaded and
+ * ipc started)
+ * @enable: Configure communication with the fw (i.e. configure the transfers
+ * enable and register interrupt)
+ * @disable: Stop communication with the fw
+ * @deinit: Free all ressources allocated for the embedded platform
+ * @get_address: Return the virtual address to access the requested address on
+ * the platform.
+ * @ack_irq: Acknowledge the irq at link level.
+ * @get_config_reg: Return the list (size + pointer) of registers to restore in
+ * order to reload the platform while keeping the current configuration.
+ *
+ * @priv Private data for the link driver
+ */
+struct ecrnx_plat {
+ struct pci_dev *pci_dev;
+ bool enabled;
+
+ int (*enable)(struct ecrnx_hw *ecrnx_hw);
+ int (*disable)(struct ecrnx_hw *ecrnx_hw);
+ void (*deinit)(struct ecrnx_plat *ecrnx_plat);
+ u8* (*get_address)(struct ecrnx_plat *ecrnx_plat, int addr_name,
+ unsigned int offset);
+ void (*ack_irq)(struct ecrnx_plat *ecrnx_plat);
+ int (*get_config_reg)(struct ecrnx_plat *ecrnx_plat, const u32 **list);
+
+ u8 priv[0] __aligned(sizeof(void *));
+};
+
+#define ECRNX_ADDR(plat, base, offset) \
+ plat->get_address(plat, base, offset)
+
+#define ECRNX_REG_READ(plat, base, offset) \
+ readl(plat->get_address(plat, base, offset))
+
+#define ECRNX_REG_WRITE(val, plat, base, offset) \
+ writel(val, plat->get_address(plat, base, offset))
+
+#ifdef CONFIG_ECRNX_ESWIN
+int ecrnx_platform_init(void *ecrnx_plat, void **platform_data);
+#else
+int ecrnx_platform_init(struct ecrnx_plat *ecrnx_plat, void **platform_data);
+#endif
+void ecrnx_platform_deinit(struct ecrnx_hw *ecrnx_hw);
+
+int ecrnx_platform_on(struct ecrnx_hw *ecrnx_hw, void *config);
+void ecrnx_platform_off(struct ecrnx_hw *ecrnx_hw, void **config);
+
+int ecrnx_platform_register_drv(void);
+void ecrnx_platform_unregister_drv(void);
+
+#ifndef CONFIG_ECRNX_ESWIN
+static inline struct device *ecrnx_platform_get_dev(struct ecrnx_plat *ecrnx_plat)
+{
+ return &(ecrnx_plat->pci_dev->dev);
+}
+
+static inline unsigned int ecrnx_platform_get_irq(struct ecrnx_plat *ecrnx_plat)
+{
+ return ecrnx_plat->pci_dev->irq;
+}
+#else
+
+
+#ifdef CONFIG_ECRNX_ESWIN_SDIO
+extern struct device *eswin_sdio_get_dev(void *plat);
+static inline struct device *ecrnx_platform_get_dev(void *ecrnx_plat)
+{
+ return eswin_sdio_get_dev(ecrnx_plat);
+}
+#endif
+
+
+#ifdef CONFIG_ECRNX_ESWIN_USB
+struct device *eswin_usb_get_dev(void *plat);
+static inline struct device *ecrnx_platform_get_dev(void *ecrnx_plat)
+{
+ return eswin_usb_get_dev(ecrnx_plat);
+}
+#endif
+
+
+#endif
+#endif /* _ECRNX_PLAT_H_ */
diff --git a/drivers/net/wireless/eswin/ecrnx_prof.h b/drivers/net/wireless/eswin/ecrnx_prof.h
new file mode 100644
index 000000000000..e828fd2437bf
--- /dev/null
+++ b/drivers/net/wireless/eswin/ecrnx_prof.h
@@ -0,0 +1,136 @@
+/**
+ ****************************************************************************************
+ *
+ * @file ecrnx_prof.h
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ****************************************************************************************
+ */
+
+#ifndef _ECRNX_PROF_H_
+#define _ECRNX_PROF_H_
+
+#include "reg_access.h"
+#include "ecrnx_platform.h"
+
+static inline void ecrnx_prof_set(struct ecrnx_hw *ecrnx_hw, int val)
+{
+ struct ecrnx_plat *ecrnx_plat = ecrnx_hw->plat;
+ ECRNX_REG_WRITE(val, ecrnx_plat, ECRNX_ADDR_SYSTEM, NXMAC_SW_SET_PROFILING_ADDR);
+}
+
+static inline void ecrnx_prof_clear(struct ecrnx_hw *ecrnx_hw, int val)
+{
+ struct ecrnx_plat *ecrnx_plat = ecrnx_hw->plat;
+ ECRNX_REG_WRITE(val, ecrnx_plat, ECRNX_ADDR_SYSTEM, NXMAC_SW_CLEAR_PROFILING_ADDR);
+}
+
+#if 0
+/* Defines for SW Profiling registers values */
+enum {
+ TX_IPC_IRQ,
+ TX_IPC_EVT,
+ TX_PREP_EVT,
+ TX_DMA_IRQ,
+ TX_MAC_IRQ,
+ TX_PAYL_HDL,
+ TX_CFM_EVT,
+ TX_IPC_CFM,
+ RX_MAC_IRQ, // 8
+ RX_TRIGGER_EVT,
+ RX_DMA_IRQ,
+ RX_DMA_EVT,
+ RX_IPC_IND,
+ RX_MPDU_XFER,
+ DBG_PROF_MAX
+};
+#endif
+
+enum {
+ SW_PROF_HOSTBUF_IDX = 12,
+ /****** IPC IRQs related signals ******/
+ /* E2A direction */
+ SW_PROF_IRQ_E2A_RXDESC = 16, // to make sure we let 16 bits available for LMAC FW
+ SW_PROF_IRQ_E2A_TXCFM,
+ SW_PROF_IRQ_E2A_DBG,
+ SW_PROF_IRQ_E2A_MSG,
+ SW_PROF_IPC_MSGPUSH,
+ SW_PROF_MSGALLOC,
+ SW_PROF_MSGIND,
+ SW_PROF_DBGIND,
+
+ /* A2E direction */
+ SW_PROF_IRQ_A2E_TXCFM_BACK,
+
+ /****** Driver functions related signals ******/
+ SW_PROF_WAIT_QUEUE_STOP,
+ SW_PROF_WAIT_QUEUE_WAKEUP,
+ SW_PROF_ECRNXDATAIND,
+ SW_PROF_ECRNX_IPC_IRQ_HDLR,
+ SW_PROF_ECRNX_IPC_THR_IRQ_HDLR,
+ SW_PROF_IEEE80211RX,
+ SW_PROF_ECRNX_PATTERN,
+ SW_PROF_MAX
+};
+
+// [LT]For debug purpose only
+#if (0)
+#define SW_PROF_CHAN_CTXT_CFM_HDL_BIT (21)
+#define SW_PROF_CHAN_CTXT_CFM_BIT (22)
+#define SW_PROF_CHAN_CTXT_CFM_SWDONE_BIT (23)
+#define SW_PROF_CHAN_CTXT_PUSH_BIT (24)
+#define SW_PROF_CHAN_CTXT_QUEUE_BIT (25)
+#define SW_PROF_CHAN_CTXT_TX_BIT (26)
+#define SW_PROF_CHAN_CTXT_TX_PAUSE_BIT (27)
+#define SW_PROF_CHAN_CTXT_PSWTCH_BIT (28)
+#define SW_PROF_CHAN_CTXT_SWTCH_BIT (29)
+
+// TO DO: update this
+
+#define REG_SW_SET_PROFILING_CHAN(env, bit) \
+ ecrnx_prof_set((struct ecrnx_hw*)env, BIT(bit))
+
+#define REG_SW_CLEAR_PROFILING_CHAN(env, bit) \
+ ecrnx_prof_clear((struct ecrnx_hw*)env, BIT(bit))
+
+#else
+#define SW_PROF_CHAN_CTXT_CFM_HDL_BIT (0)
+#define SW_PROF_CHAN_CTXT_CFM_BIT (0)
+#define SW_PROF_CHAN_CTXT_CFM_SWDONE_BIT (0)
+#define SW_PROF_CHAN_CTXT_PUSH_BIT (0)
+#define SW_PROF_CHAN_CTXT_QUEUE_BIT (0)
+#define SW_PROF_CHAN_CTXT_TX_BIT (0)
+#define SW_PROF_CHAN_CTXT_TX_PAUSE_BIT (0)
+#define SW_PROF_CHAN_CTXT_PSWTCH_BIT (0)
+#define SW_PROF_CHAN_CTXT_SWTCH_BIT (0)
+
+#define REG_SW_SET_PROFILING_CHAN(env, bit) do {} while (0)
+#define REG_SW_CLEAR_PROFILING_CHAN(env, bit) do {} while (0)
+#endif
+
+#ifdef CONFIG_ECRNX_ESWIN
+#undef CONFIG_ECRNX_SW_PROFILING
+#endif
+#ifdef CONFIG_ECRNX_SW_PROFILING
+/* Macros for SW PRofiling registers access */
+#define REG_SW_SET_PROFILING(env, bit) \
+ ecrnx_prof_set((struct ecrnx_hw*)env, BIT(bit))
+
+#define REG_SW_SET_HOSTBUF_IDX_PROFILING(env, val) \
+ ecrnx_prof_set((struct ecrnx_hw*)env, val<<(SW_PROF_HOSTBUF_IDX))
+
+#define REG_SW_CLEAR_PROFILING(env, bit) \
+ ecrnx_prof_clear((struct ecrnx_hw*)env, BIT(bit))
+
+#define REG_SW_CLEAR_HOSTBUF_IDX_PROFILING(env) \
+ ecrnx_prof_clear((struct ecrnx_hw*)env,0x0F<<(SW_PROF_HOSTBUF_IDX))
+
+#else
+#define REG_SW_SET_PROFILING(env, value) do {} while (0)
+#define REG_SW_CLEAR_PROFILING(env, value) do {} while (0)
+#define REG_SW_SET_HOSTBUF_IDX_PROFILING(env, val) do {} while (0)
+#define REG_SW_CLEAR_HOSTBUF_IDX_PROFILING(env) do {} while (0)
+#endif
+
+#endif /* _ECRNX_PROF_H_ */
diff --git a/drivers/net/wireless/eswin/ecrnx_radar.c b/drivers/net/wireless/eswin/ecrnx_radar.c
new file mode 100644
index 000000000000..fca6ce8afa19
--- /dev/null
+++ b/drivers/net/wireless/eswin/ecrnx_radar.c
@@ -0,0 +1,1647 @@
+/**
+******************************************************************************
+ *
+ * @file ecrnx_radar.c
+ *
+ * @brief Functions to handle radar detection
+ * Radar detection is copied (and adapted) from ath driver source code.
+ *
+ * Copyright (c) 2012 Neratec Solutions AG
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+#include <linux/list.h>
+#include <linux/kernel.h>
+#include <linux/ktime.h>
+#include <net/mac80211.h>
+
+#include "ecrnx_radar.h"
+#include "ecrnx_defs.h"
+#include "ecrnx_msg_tx.h"
+#include "ecrnx_events.h"
+#include "ecrnx_compat.h"
+
+/*
+ * tolerated deviation of radar time stamp in usecs on both sides
+ * TODO: this might need to be HW-dependent
+ */
+#define PRI_TOLERANCE 16
+
+/**
+ * struct radar_types - contains array of patterns defined for one DFS domain
+ * @domain: DFS regulatory domain
+ * @num_radar_types: number of radar types to follow
+ * @radar_types: radar types array
+ */
+struct radar_types {
+ enum nl80211_dfs_regions region;
+ u32 num_radar_types;
+ const struct radar_detector_specs *spec_riu;
+ const struct radar_detector_specs *spec_fcu;
+};
+
+/**
+ * Type of radar waveform:
+ * RADAR_WAVEFORM_SHORT : waveform defined by
+ * - pulse width
+ * - pulse interval in a burst (pri)
+ * - number of pulses in a burst (ppb)
+ *
+ * RADAR_WAVEFORM_WEATHER :
+ * same than SHORT except that ppb is dependent of pri
+ *
+ * RADAR_WAVEFORM_INTERLEAVED :
+ * same than SHORT except there are several value of pri (interleaved)
+ *
+ * RADAR_WAVEFORM_LONG :
+ *
+ */
+enum radar_waveform_type {
+ RADAR_WAVEFORM_SHORT,
+ RADAR_WAVEFORM_WEATHER,
+ RADAR_WAVEFORM_INTERLEAVED,
+ RADAR_WAVEFORM_LONG
+};
+
+/**
+ * struct radar_detector_specs - detector specs for a radar pattern type
+ * @type_id: pattern type, as defined by regulatory
+ * @width_min: minimum radar pulse width in [us]
+ * @width_max: maximum radar pulse width in [us]
+ * @pri_min: minimum pulse repetition interval in [us] (including tolerance)
+ * @pri_max: minimum pri in [us] (including tolerance)
+ * @num_pri: maximum number of different pri for this type
+ * @ppb: pulses per bursts for this type
+ * @ppb_thresh: number of pulses required to trigger detection
+ * @max_pri_tolerance: pulse time stamp tolerance on both sides [us]
+ * @type: Type of radar waveform
+ */
+struct radar_detector_specs {
+ u8 type_id;
+ u8 width_min;
+ u8 width_max;
+ u16 pri_min;
+ u16 pri_max;
+ u8 num_pri;
+ u8 ppb;
+ u8 ppb_thresh;
+ u8 max_pri_tolerance;
+ enum radar_waveform_type type;
+};
+
+
+/* percentage on ppb threshold to trigger detection */
+#define MIN_PPB_THRESH 50
+#define PPB_THRESH(PPB) ((PPB * MIN_PPB_THRESH + 50) / 100)
+#define PRF2PRI(PRF) ((1000000 + PRF / 2) / PRF)
+
+/* width tolerance */
+#define WIDTH_TOLERANCE 2
+#define WIDTH_LOWER(X) (X)
+#define WIDTH_UPPER(X) (X)
+
+#define ETSI_PATTERN_SHORT(ID, WMIN, WMAX, PMIN, PMAX, PPB) \
+ { \
+ ID, WIDTH_LOWER(WMIN), WIDTH_UPPER(WMAX), \
+ (PRF2PRI(PMAX) - PRI_TOLERANCE), \
+ (PRF2PRI(PMIN) + PRI_TOLERANCE), 1, PPB, \
+ PPB_THRESH(PPB), PRI_TOLERANCE, RADAR_WAVEFORM_SHORT \
+ }
+
+#define ETSI_PATTERN_INTERLEAVED(ID, WMIN, WMAX, PMIN, PMAX, PRFMIN, PRFMAX, PPB) \
+ { \
+ ID, WIDTH_LOWER(WMIN), WIDTH_UPPER(WMAX), \
+ (PRF2PRI(PMAX) * PRFMIN- PRI_TOLERANCE), \
+ (PRF2PRI(PMIN) * PRFMAX + PRI_TOLERANCE), \
+ PRFMAX, PPB * PRFMAX, \
+ PPB_THRESH(PPB), PRI_TOLERANCE, RADAR_WAVEFORM_INTERLEAVED \
+ }
+
+/* radar types as defined by ETSI EN-301-893 v1.7.1 */
+static const struct radar_detector_specs etsi_radar_ref_types_v17_riu[] = {
+ ETSI_PATTERN_SHORT(0, 0, 8, 700, 700, 18),
+ ETSI_PATTERN_SHORT(1, 0, 10, 200, 1000, 10),
+ ETSI_PATTERN_SHORT(2, 0, 22, 200, 1600, 15),
+ ETSI_PATTERN_SHORT(3, 0, 22, 2300, 4000, 25),
+ ETSI_PATTERN_SHORT(4, 20, 38, 2000, 4000, 20),
+ ETSI_PATTERN_INTERLEAVED(5, 0, 8, 300, 400, 2, 3, 10),
+ ETSI_PATTERN_INTERLEAVED(6, 0, 8, 400, 1200, 2, 3, 15),
+};
+
+static const struct radar_detector_specs etsi_radar_ref_types_v17_fcu[] = {
+ ETSI_PATTERN_SHORT(0, 0, 8, 700, 700, 18),
+ ETSI_PATTERN_SHORT(1, 0, 8, 200, 1000, 10),
+ ETSI_PATTERN_SHORT(2, 0, 16, 200, 1600, 15),
+ ETSI_PATTERN_SHORT(3, 0, 16, 2300, 4000, 25),
+ ETSI_PATTERN_SHORT(4, 20, 34, 2000, 4000, 20),
+ ETSI_PATTERN_INTERLEAVED(5, 0, 8, 300, 400, 2, 3, 10),
+ ETSI_PATTERN_INTERLEAVED(6, 0, 8, 400, 1200, 2, 3, 15),
+};
+
+static const struct radar_types etsi_radar_types_v17 = {
+ .region = NL80211_DFS_ETSI,
+ .num_radar_types = ARRAY_SIZE(etsi_radar_ref_types_v17_riu),
+ .spec_riu = etsi_radar_ref_types_v17_riu,
+ .spec_fcu = etsi_radar_ref_types_v17_fcu,
+};
+
+#define FCC_PATTERN(ID, WMIN, WMAX, PMIN, PMAX, PRF, PPB, TYPE) \
+ { \
+ ID, WIDTH_LOWER(WMIN), WIDTH_UPPER(WMAX), \
+ PMIN - PRI_TOLERANCE, \
+ PMAX * PRF + PRI_TOLERANCE, PRF, PPB * PRF, \
+ PPB_THRESH(PPB), PRI_TOLERANCE, TYPE \
+ }
+
+static const struct radar_detector_specs fcc_radar_ref_types_riu[] = {
+ FCC_PATTERN(0, 0, 8, 1428, 1428, 1, 18, RADAR_WAVEFORM_SHORT),
+ FCC_PATTERN(1, 0, 8, 518, 3066, 1, 102, RADAR_WAVEFORM_WEATHER),
+ FCC_PATTERN(2, 0, 8, 150, 230, 1, 23, RADAR_WAVEFORM_SHORT),
+ FCC_PATTERN(3, 6, 20, 200, 500, 1, 16, RADAR_WAVEFORM_SHORT),
+ FCC_PATTERN(4, 10, 28, 200, 500, 1, 12, RADAR_WAVEFORM_SHORT),
+ FCC_PATTERN(5, 50, 110, 1000, 2000, 1, 8, RADAR_WAVEFORM_LONG),
+ FCC_PATTERN(6, 0, 8, 333, 333, 1, 9, RADAR_WAVEFORM_SHORT),
+};
+
+static const struct radar_detector_specs fcc_radar_ref_types_fcu[] = {
+ FCC_PATTERN(0, 0, 8, 1428, 1428, 1, 18, RADAR_WAVEFORM_SHORT),
+ FCC_PATTERN(1, 0, 8, 518, 3066, 1, 102, RADAR_WAVEFORM_WEATHER),
+ FCC_PATTERN(2, 0, 8, 150, 230, 1, 23, RADAR_WAVEFORM_SHORT),
+ FCC_PATTERN(3, 6, 12, 200, 500, 1, 16, RADAR_WAVEFORM_SHORT),
+ FCC_PATTERN(4, 10, 22, 200, 500, 1, 12, RADAR_WAVEFORM_SHORT),
+ FCC_PATTERN(5, 50, 104, 1000, 2000, 1, 8, RADAR_WAVEFORM_LONG),
+ FCC_PATTERN(6, 0, 8, 333, 333, 1, 9, RADAR_WAVEFORM_SHORT),
+};
+
+static const struct radar_types fcc_radar_types = {
+ .region = NL80211_DFS_FCC,
+ .num_radar_types = ARRAY_SIZE(fcc_radar_ref_types_riu),
+ .spec_riu = fcc_radar_ref_types_riu,
+ .spec_fcu = fcc_radar_ref_types_fcu,
+};
+
+#define JP_PATTERN FCC_PATTERN
+static const struct radar_detector_specs jp_radar_ref_types_riu[] = {
+ JP_PATTERN(0, 0, 8, 1428, 1428, 1, 18, RADAR_WAVEFORM_SHORT),
+ JP_PATTERN(1, 2, 8, 3846, 3846, 1, 18, RADAR_WAVEFORM_SHORT),
+ JP_PATTERN(2, 0, 8, 1388, 1388, 1, 18, RADAR_WAVEFORM_SHORT),
+ JP_PATTERN(3, 0, 8, 4000, 4000, 1, 18, RADAR_WAVEFORM_SHORT),
+ JP_PATTERN(4, 0, 8, 150, 230, 1, 23, RADAR_WAVEFORM_SHORT),
+ JP_PATTERN(5, 6, 20, 200, 500, 1, 16, RADAR_WAVEFORM_SHORT),
+ JP_PATTERN(6, 10, 28, 200, 500, 1, 12, RADAR_WAVEFORM_SHORT),
+ JP_PATTERN(7, 50, 110, 1000, 2000, 1, 8, RADAR_WAVEFORM_LONG),
+ JP_PATTERN(8, 0, 8, 333, 333, 1, 9, RADAR_WAVEFORM_SHORT),
+};
+
+static const struct radar_detector_specs jp_radar_ref_types_fcu[] = {
+ JP_PATTERN(0, 0, 8, 1428, 1428, 1, 18, RADAR_WAVEFORM_SHORT),
+ JP_PATTERN(1, 2, 6, 3846, 3846, 1, 18, RADAR_WAVEFORM_SHORT),
+ JP_PATTERN(2, 0, 8, 1388, 1388, 1, 18, RADAR_WAVEFORM_SHORT),
+ JP_PATTERN(3, 2, 2, 4000, 4000, 1, 18, RADAR_WAVEFORM_SHORT),
+ JP_PATTERN(4, 0, 8, 150, 230, 1, 23, RADAR_WAVEFORM_SHORT),
+ JP_PATTERN(5, 6, 12, 200, 500, 1, 16, RADAR_WAVEFORM_SHORT),
+ JP_PATTERN(6, 10, 22, 200, 500, 1, 12, RADAR_WAVEFORM_SHORT),
+ JP_PATTERN(7, 50, 104, 1000, 2000, 1, 8, RADAR_WAVEFORM_LONG),
+ JP_PATTERN(8, 0, 8, 333, 333, 1, 9, RADAR_WAVEFORM_SHORT),
+};
+
+static const struct radar_types jp_radar_types = {
+ .region = NL80211_DFS_JP,
+ .num_radar_types = ARRAY_SIZE(jp_radar_ref_types_riu),
+ .spec_riu = jp_radar_ref_types_riu,
+ .spec_fcu = jp_radar_ref_types_fcu,
+};
+
+static const struct radar_types *dfs_domains[] = {
+ &etsi_radar_types_v17,
+ &fcc_radar_types,
+ &jp_radar_types,
+};
+
+
+/**
+ * struct pri_sequence - sequence of pulses matching one PRI
+ * @head: list_head
+ * @pri: pulse repetition interval (PRI) in usecs
+ * @dur: duration of sequence in usecs
+ * @count: number of pulses in this sequence
+ * @count_falses: number of not matching pulses in this sequence
+ * @first_ts: time stamp of first pulse in usecs
+ * @last_ts: time stamp of last pulse in usecs
+ * @deadline_ts: deadline when this sequence becomes invalid (first_ts + dur)
+ * @ppb_thresh: Number of pulses to validate detection
+ * (need for weather radar whose value depends of pri)
+ */
+struct pri_sequence {
+ struct list_head head;
+ u32 pri;
+ u32 dur;
+ u32 count;
+ u32 count_falses;
+ u64 first_ts;
+ u64 last_ts;
+ u64 deadline_ts;
+ u8 ppb_thresh;
+};
+
+
+/**
+ * struct pulse_elem - elements in pulse queue
+ * @ts: time stamp in usecs
+ */
+struct pulse_elem {
+ struct list_head head;
+ u64 ts;
+};
+
+/**
+ * struct pri_detector - PRI detector element for a dedicated radar type
+ * @head:
+ * @rs: detector specs for this detector element
+ * @last_ts: last pulse time stamp considered for this element in usecs
+ * @sequences: list_head holding potential pulse sequences
+ * @pulses: list connecting pulse_elem objects
+ * @count: number of pulses in queue
+ * @max_count: maximum number of pulses to be queued
+ * @window_size: window size back from newest pulse time stamp in usecs
+ * @freq:
+ */
+struct pri_detector {
+ struct list_head head;
+ const struct radar_detector_specs *rs;
+ u64 last_ts;
+ struct list_head sequences;
+ struct list_head pulses;
+ u32 count;
+ u32 max_count;
+ u32 window_size;
+ struct pri_detector_ops *ops;
+ u16 freq;
+};
+
+/**
+ * struct pri_detector_ops - PRI detector ops (dependent of waveform type)
+ * @init : Initialize pri_detector structure
+ * @add_pulse : Add a pulse to the pri-detector
+ * @reset_on_pri_overflow : Should the pri_detector be resetted when pri overflow
+ */
+struct pri_detector_ops {
+ void (*init)(struct pri_detector *pde);
+ struct pri_sequence * (*add_pulse)(struct pri_detector *pde, u16 len, u64 ts, u16 pri);
+ int reset_on_pri_overflow;
+};
+
+
+/******************************************************************************
+ * PRI (pulse repetition interval) sequence detection
+ *****************************************************************************/
+/**
+ * Singleton Pulse and Sequence Pools
+ *
+ * Instances of pri_sequence and pulse_elem are kept in singleton pools to
+ * reduce the number of dynamic allocations. They are shared between all
+ * instances and grow up to the peak number of simultaneously used objects.
+ *
+ * Memory is freed after all references to the pools are released.
+ */
+static u32 singleton_pool_references;
+static LIST_HEAD(pulse_pool);
+static LIST_HEAD(pseq_pool);
+static DEFINE_SPINLOCK(pool_lock);
+
+static void pool_register_ref(void)
+{
+ spin_lock_bh(&pool_lock);
+ singleton_pool_references++;
+ spin_unlock_bh(&pool_lock);
+}
+
+static void pool_deregister_ref(void)
+{
+ spin_lock_bh(&pool_lock);
+ singleton_pool_references--;
+ if (singleton_pool_references == 0) {
+ /* free singleton pools with no references left */
+ struct pri_sequence *ps, *ps0;
+ struct pulse_elem *p, *p0;
+
+ list_for_each_entry_safe(p, p0, &pulse_pool, head) {
+ list_del(&p->head);
+ kfree(p);
+ }
+ list_for_each_entry_safe(ps, ps0, &pseq_pool, head) {
+ list_del(&ps->head);
+ kfree(ps);
+ }
+ }
+ spin_unlock_bh(&pool_lock);
+}
+
+static void pool_put_pulse_elem(struct pulse_elem *pe)
+{
+ spin_lock_bh(&pool_lock);
+ list_add(&pe->head, &pulse_pool);
+ spin_unlock_bh(&pool_lock);
+}
+
+static void pool_put_pseq_elem(struct pri_sequence *pse)
+{
+ spin_lock_bh(&pool_lock);
+ list_add(&pse->head, &pseq_pool);
+ spin_unlock_bh(&pool_lock);
+}
+
+static struct pri_sequence *pool_get_pseq_elem(void)
+{
+ struct pri_sequence *pse = NULL;
+ spin_lock_bh(&pool_lock);
+ if (!list_empty(&pseq_pool)) {
+ pse = list_first_entry(&pseq_pool, struct pri_sequence, head);
+ list_del(&pse->head);
+ }
+ spin_unlock_bh(&pool_lock);
+
+ if (pse == NULL) {
+ pse = kmalloc(sizeof(*pse), GFP_ATOMIC);
+ }
+
+ return pse;
+}
+
+static struct pulse_elem *pool_get_pulse_elem(void)
+{
+ struct pulse_elem *pe = NULL;
+ spin_lock_bh(&pool_lock);
+ if (!list_empty(&pulse_pool)) {
+ pe = list_first_entry(&pulse_pool, struct pulse_elem, head);
+ list_del(&pe->head);
+ }
+ spin_unlock_bh(&pool_lock);
+ return pe;
+}
+
+static struct pulse_elem *pulse_queue_get_tail(struct pri_detector *pde)
+{
+ struct list_head *l = &pde->pulses;
+ if (list_empty(l))
+ return NULL;
+ return list_entry(l->prev, struct pulse_elem, head);
+}
+
+static bool pulse_queue_dequeue(struct pri_detector *pde)
+{
+ struct pulse_elem *p = pulse_queue_get_tail(pde);
+ if (p != NULL) {
+ list_del_init(&p->head);
+ pde->count--;
+ /* give it back to pool */
+ pool_put_pulse_elem(p);
+ }
+ return (pde->count > 0);
+}
+
+/**
+ * pulse_queue_check_window - remove pulses older than window
+ * @pde: pointer on pri_detector
+ *
+ * dequeue pulse that are too old.
+ */
+static
+void pulse_queue_check_window(struct pri_detector *pde)
+{
+ u64 min_valid_ts;
+ struct pulse_elem *p;
+
+ /* there is no delta time with less than 2 pulses */
+ if (pde->count < 2)
+ return;
+
+ if (pde->last_ts <= pde->window_size)
+ return;
+
+ min_valid_ts = pde->last_ts - pde->window_size;
+ while ((p = pulse_queue_get_tail(pde)) != NULL) {
+ if (p->ts >= min_valid_ts)
+ return;
+ pulse_queue_dequeue(pde);
+ }
+}
+
+/**
+ * pulse_queue_enqueue - Queue one pulse
+ * @pde: pointer on pri_detector
+ *
+ * Add one pulse to the list. If the maximum number of pulses
+ * if reached, remove oldest one.
+ */
+static
+bool pulse_queue_enqueue(struct pri_detector *pde, u64 ts)
+{
+ struct pulse_elem *p = pool_get_pulse_elem();
+ if (p == NULL) {
+ p = kmalloc(sizeof(*p), GFP_ATOMIC);
+ if (p == NULL) {
+ return false;
+ }
+ }
+ INIT_LIST_HEAD(&p->head);
+ p->ts = ts;
+ list_add(&p->head, &pde->pulses);
+ pde->count++;
+ pde->last_ts = ts;
+ pulse_queue_check_window(pde);
+ if (pde->count >= pde->max_count)
+ pulse_queue_dequeue(pde);
+
+ return true;
+}
+
+
+/***************************************************************************
+ * Short waveform
+ **************************************************************************/
+/**
+ * pde_get_multiple() - get number of multiples considering a given tolerance
+ * @return factor if abs(val - factor*fraction) <= tolerance, 0 otherwise
+ */
+static
+u32 pde_get_multiple(u32 val, u32 fraction, u32 tolerance)
+{
+ u32 remainder;
+ u32 factor;
+ u32 delta;
+
+ if (fraction == 0)
+ return 0;
+
+ delta = (val < fraction) ? (fraction - val) : (val - fraction);
+
+ if (delta <= tolerance)
+ /* val and fraction are within tolerance */
+ return 1;
+
+ factor = val / fraction;
+ remainder = val % fraction;
+ if (remainder > tolerance) {
+ /* no exact match */
+ if ((fraction - remainder) <= tolerance)
+ /* remainder is within tolerance */
+ factor++;
+ else
+ factor = 0;
+ }
+ return factor;
+}
+
+/**
+ * pde_short_create_sequences - create_sequences function for
+ * SHORT/WEATHER/INTERLEAVED radar waveform
+ * @pde: pointer on pri_detector
+ * @ts: timestamp of the pulse
+ * @min_count: Minimum number of pulse to be present in the sequence.
+ * (With this pulse there is already a sequence with @min_count
+ * pulse, so if we can't create a sequence with more pulse don't
+ * create it)
+ * @return: false if an error occured (memory allocation) true otherwise
+ *
+ * For each pulses queued check if we can create a sequence with
+ * pri = (ts - pulse_queued.ts) which contains more than @min_count pulses.
+ *
+ */
+static
+bool pde_short_create_sequences(struct pri_detector *pde,
+ u64 ts, u32 min_count)
+{
+ struct pulse_elem *p;
+ u16 pulse_idx = 0;
+
+ list_for_each_entry(p, &pde->pulses, head) {
+ struct pri_sequence ps, *new_ps;
+ struct pulse_elem *p2;
+ u32 tmp_false_count;
+ u64 min_valid_ts;
+ u32 delta_ts = ts - p->ts;
+ pulse_idx++;
+
+ if (delta_ts < pde->rs->pri_min)
+ /* ignore too small pri */
+ continue;
+
+ if (delta_ts > pde->rs->pri_max)
+ /* stop on too large pri (sorted list) */
+ break;
+
+ /* build a new sequence with new potential pri */
+ ps.count = 2;
+ ps.count_falses = pulse_idx - 1;
+ ps.first_ts = p->ts;
+ ps.last_ts = ts;
+ ps.pri = ts - p->ts;
+ ps.dur = ps.pri * (pde->rs->ppb - 1)
+ + 2 * pde->rs->max_pri_tolerance;
+
+ p2 = p;
+ tmp_false_count = 0;
+ if (ps.dur > ts)
+ min_valid_ts = 0;
+ else
+ min_valid_ts = ts - ps.dur;
+ /* check which past pulses are candidates for new sequence */
+ list_for_each_entry_continue(p2, &pde->pulses, head) {
+ u32 factor;
+ if (p2->ts < min_valid_ts)
+ /* stop on crossing window border */
+ break;
+ /* check if pulse match (multi)PRI */
+ factor = pde_get_multiple(ps.last_ts - p2->ts, ps.pri,
+ pde->rs->max_pri_tolerance);
+ if (factor > 0) {
+ ps.count++;
+ ps.first_ts = p2->ts;
+ /*
+ * on match, add the intermediate falses
+ * and reset counter
+ */
+ ps.count_falses += tmp_false_count;
+ tmp_false_count = 0;
+ } else {
+ /* this is a potential false one */
+ tmp_false_count++;
+ }
+ }
+ if (ps.count <= min_count) {
+ /* did not reach minimum count, drop sequence */
+ continue;
+ }
+ /* this is a valid one, add it */
+ ps.deadline_ts = ps.first_ts + ps.dur;
+ if (pde->rs->type == RADAR_WAVEFORM_WEATHER) {
+ ps.ppb_thresh = 19000000 / (360 * ps.pri);
+ ps.ppb_thresh = PPB_THRESH(ps.ppb_thresh);
+ } else {
+ ps.ppb_thresh = pde->rs->ppb_thresh;
+ }
+
+ new_ps = pool_get_pseq_elem();
+ if (new_ps == NULL) {
+ return false;
+ }
+ memcpy(new_ps, &ps, sizeof(ps));
+ INIT_LIST_HEAD(&new_ps->head);
+ list_add(&new_ps->head, &pde->sequences);
+ }
+ return true;
+}
+
+/**
+ * pde_short_add_to_existing_seqs - add_to_existing_seqs function for
+ * SHORT/WEATHER/INTERLEAVED radar waveform
+ * @pde: pointer on pri_detector
+ * @ts: timestamp of the pulse
+ *
+ * Check all sequemces created for this pde.
+ * - If the sequence is too old delete it.
+ * - Else if the delta with the previous pulse match the pri of the sequence
+ * add the pulse to this sequence. If the pulse cannot be added it is added
+ * to the false pulses for this sequence
+ *
+ * @return the length of the longest sequence in which the pulse has been added
+ */
+static
+u32 pde_short_add_to_existing_seqs(struct pri_detector *pde, u64 ts)
+{
+ u32 max_count = 0;
+ struct pri_sequence *ps, *ps2;
+ list_for_each_entry_safe(ps, ps2, &pde->sequences, head) {
+ u32 delta_ts;
+ u32 factor;
+
+ /* first ensure that sequence is within window */
+ if (ts > ps->deadline_ts) {
+ list_del_init(&ps->head);
+ pool_put_pseq_elem(ps);
+ continue;
+ }
+
+ delta_ts = ts - ps->last_ts;
+ factor = pde_get_multiple(delta_ts, ps->pri,
+ pde->rs->max_pri_tolerance);
+
+ if (factor > 0) {
+ ps->last_ts = ts;
+ ps->count++;
+
+ if (max_count < ps->count)
+ max_count = ps->count;
+ } else {
+ ps->count_falses++;
+ }
+ }
+ return max_count;
+}
+
+
+/**
+ * pde_short_check_detection - check_detection function for
+ * SHORT/WEATHER/INTERLEAVED radar waveform
+ * @pde: pointer on pri_detector
+ *
+ * Check all sequemces created for this pde.
+ * - If a sequence contains more pulses than the threshold and more matching
+ * that false pulses.
+ *
+ * @return The first complete sequence, and NULL if no sequence is complete.
+ */
+static
+struct pri_sequence * pde_short_check_detection(struct pri_detector *pde)
+{
+ struct pri_sequence *ps;
+
+ if (list_empty(&pde->sequences))
+ return NULL;
+
+ list_for_each_entry(ps, &pde->sequences, head) {
+ /*
+ * we assume to have enough matching confidence if we
+ * 1) have enough pulses
+ * 2) have more matching than false pulses
+ */
+ if ((ps->count >= ps->ppb_thresh) &&
+ (ps->count * pde->rs->num_pri > ps->count_falses)) {
+ return ps;
+ }
+ }
+ return NULL;
+}
+
+/**
+ * pde_short_init - init function for
+ * SHORT/WEATHER/INTERLEAVED radar waveform
+ * @pde: pointer on pri_detector
+ *
+ * Initialize pri_detector window size to the maximun size of one burst
+ * for the radar specification associated.
+ */
+static
+void pde_short_init(struct pri_detector *pde)
+{
+ pde->window_size = pde->rs->pri_max * pde->rs->ppb * pde->rs->num_pri;
+ pde->max_count = pde->rs->ppb * 2;
+}
+
+static void pri_detector_reset(struct pri_detector *pde, u64 ts);
+/**
+ * pde_short_add_pulse - Add pulse to a pri_detector for
+ * SHORT/WEATHER/INTERLEAVED radar waveform
+ *
+ * @pde : pointer on pri_detector
+ * @len : width of the pulse
+ * @ts : timestamp of the pulse received
+ * @pri : Delta in us with the previous pulse.
+ * (0 means that delta in bigger than 65535 us)
+ *
+ * Process on pulse within this pri_detector
+ * - First try to add it to existing sequence
+ * - Then try to create a new and longest sequence
+ * - Check if this pulse complete a sequence
+ * - If not save this pulse in the list
+ */
+static
+struct pri_sequence *pde_short_add_pulse(struct pri_detector *pde,
+ u16 len, u64 ts, u16 pri)
+{
+ u32 max_updated_seq;
+ struct pri_sequence *ps;
+ const struct radar_detector_specs *rs = pde->rs;
+
+ if (pde->count == 0) {
+ /* This is the first pulse after reset, no need to check sequences */
+ pulse_queue_enqueue(pde, ts);
+ return NULL;
+ }
+
+ if ((ts - pde->last_ts) < rs->max_pri_tolerance) {
+ /* if delta to last pulse is too short, don't use this pulse */
+ return NULL;
+ }
+
+ max_updated_seq = pde_short_add_to_existing_seqs(pde, ts);
+
+ if (!pde_short_create_sequences(pde, ts, max_updated_seq)) {
+ pri_detector_reset(pde, ts);
+ return NULL;
+ }
+
+ ps = pde_short_check_detection(pde);
+
+ if (ps == NULL)
+ pulse_queue_enqueue(pde, ts);
+
+ return ps;
+}
+
+
+
+/**
+ * pri detector ops to detect short radar waveform
+ * A Short waveform is defined by :
+ * The width of pulses.
+ * The interval between two pulses inside a burst (called pri)
+ * (some waveform may have or 2/3 interleaved pri)
+ * The number of pulses per burst (ppb)
+ */
+static struct pri_detector_ops pri_detector_short = {
+ .init = pde_short_init,
+ .add_pulse = pde_short_add_pulse,
+ .reset_on_pri_overflow = 1,
+};
+
+
+/***************************************************************************
+ * Long waveform
+ **************************************************************************/
+#define LONG_RADAR_DURATION 12000000
+#define LONG_RADAR_BURST_MIN_DURATION (12000000 / 20)
+#define LONG_RADAR_MAX_BURST 20
+
+/**
+ * pde_long_init - init function for LONG radar waveform
+ * @pde: pointer on pri_detector
+ *
+ * Initialize pri_detector window size to the long waveform radar
+ * waveform (ie. 12s) and max_count
+ */
+static
+void pde_long_init(struct pri_detector *pde)
+{
+ pde->window_size = LONG_RADAR_DURATION;
+ pde->max_count = LONG_RADAR_MAX_BURST; /* only count burst not pulses */
+}
+
+
+/**
+ * pde_long_add_pulse - Add pulse to a pri_detector for
+ * LONG radar waveform
+ *
+ * @pde : pointer on pri_detector
+ * @len : width of the pulse
+ * @ts : timestamp of the pulse received
+ * @pri : Delta in us with the previous pulse.
+ *
+ *
+ * For long pulse we only handle one sequence. Since each burst
+ * have a different set of parameters (number of pulse, pri) than
+ * the previous one we only use pulse width to add the pulse in the
+ * sequence.
+ * We only queue one pulse per burst and valid the radar when enough burst
+ * has been detected.
+ */
+static
+struct pri_sequence *pde_long_add_pulse(struct pri_detector *pde,
+ u16 len, u64 ts, u16 pri)
+{
+ struct pri_sequence *ps;
+ const struct radar_detector_specs *rs = pde->rs;
+
+ if (list_empty(&pde->sequences)) {
+ /* First pulse, create a new sequence */
+ ps = pool_get_pseq_elem();
+ if (ps == NULL) {
+ return NULL;
+ }
+
+ /*For long waveform, "count" represents the number of burst detected */
+ ps->count = 1;
+ /*"count_false" represents the number of pulse in the current burst */
+ ps->count_falses = 1;
+ ps->first_ts = ts;
+ ps->last_ts = ts;
+ ps->deadline_ts = ts + pde->window_size;
+ ps->pri = 0;
+ INIT_LIST_HEAD(&ps->head);
+ list_add(&ps->head, &pde->sequences);
+ pulse_queue_enqueue(pde, ts);
+ } else {
+ u32 delta_ts;
+
+ ps = (struct pri_sequence *)pde->sequences.next;
+
+ delta_ts = ts - ps->last_ts;
+ ps->last_ts = ts;
+
+ if (delta_ts < rs->pri_max) {
+ /* ignore pulse too close from previous one */
+ } else if ((delta_ts >= rs->pri_min) &&
+ (delta_ts <= rs->pri_max)) {
+ /* this is a new pulse in the current burst, ignore it
+ (i.e don't queue it) */
+ ps->count_falses++;
+ } else if ((ps->count > 2) &&
+ (ps->dur + delta_ts) < LONG_RADAR_BURST_MIN_DURATION) {
+ /* not enough time between burst, ignore pulse */
+ } else {
+ /* a new burst */
+ ps->count++;
+ ps->count_falses = 1;
+
+ /* reset the start of the sequence if deadline reached */
+ if (ts > ps->deadline_ts) {
+ struct pulse_elem *p;
+ u64 min_valid_ts;
+
+ min_valid_ts = ts - pde->window_size;
+ while ((p = pulse_queue_get_tail(pde)) != NULL) {
+ if (p->ts >= min_valid_ts) {
+ ps->first_ts = p->ts;
+ ps->deadline_ts = p->ts + pde->window_size;
+ break;
+ }
+ pulse_queue_dequeue(pde);
+ ps->count--;
+ }
+ }
+
+ /* valid radar if enough burst detected and delta with first burst
+ is at least duration/2 */
+ if (ps->count > pde->rs->ppb_thresh &&
+ (ts - ps->first_ts) > (pde->window_size / 2)) {
+ return ps;
+ } else {
+ pulse_queue_enqueue(pde, ts);
+ ps->dur = delta_ts;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * pri detector ops to detect long radar waveform
+ */
+static struct pri_detector_ops pri_detector_long = {
+ .init = pde_long_init,
+ .add_pulse = pde_long_add_pulse,
+ .reset_on_pri_overflow = 0,
+};
+
+
+/***************************************************************************
+ * PRI detector init/reset/exit/get
+ **************************************************************************/
+/**
+ * pri_detector_init- Create a new pri_detector
+ *
+ * @dpd: dfs_pattern_detector instance pointer
+ * @radar_type: index of radar pattern
+ * @freq: Frequency of the pri detector
+ */
+struct pri_detector *pri_detector_init(struct dfs_pattern_detector *dpd,
+ u16 radar_type, u16 freq)
+{
+ struct pri_detector *pde;
+
+ pde = kzalloc(sizeof(*pde), GFP_ATOMIC);
+ if (pde == NULL)
+ return NULL;
+
+ INIT_LIST_HEAD(&pde->sequences);
+ INIT_LIST_HEAD(&pde->pulses);
+ INIT_LIST_HEAD(&pde->head);
+ list_add(&pde->head, &dpd->detectors[radar_type]);
+
+ pde->rs = &dpd->radar_spec[radar_type];
+ pde->freq = freq;
+
+ if (pde->rs->type == RADAR_WAVEFORM_LONG) {
+ /* for LONG WAVEFORM */
+ pde->ops = &pri_detector_long;
+ } else {
+ /* for SHORT, WEATHER and INTERLEAVED */
+ pde->ops = &pri_detector_short;
+ }
+
+ /* Init dependent of specs */
+ pde->ops->init(pde);
+
+ pool_register_ref();
+ return pde;
+}
+
+/**
+ * pri_detector_reset - Reset pri_detector
+ *
+ * @pde: pointer on pri_detector
+ * @ts: New ts reference for the pri_detector
+ *
+ * free pulse queue and sequences list and give objects back to pools
+ */
+static
+void pri_detector_reset(struct pri_detector *pde, u64 ts)
+{
+ struct pri_sequence *ps, *ps0;
+ struct pulse_elem *p, *p0;
+ list_for_each_entry_safe(ps, ps0, &pde->sequences, head) {
+ list_del_init(&ps->head);
+ pool_put_pseq_elem(ps);
+ }
+ list_for_each_entry_safe(p, p0, &pde->pulses, head) {
+ list_del_init(&p->head);
+ pool_put_pulse_elem(p);
+ }
+ pde->count = 0;
+ pde->last_ts = ts;
+}
+
+/**
+ * pri_detector_exit - Delete pri_detector
+ *
+ * @pde: pointer on pri_detector
+ */
+static
+void pri_detector_exit(struct pri_detector *pde)
+{
+ pri_detector_reset(pde, 0);
+ pool_deregister_ref();
+ list_del(&pde->head);
+ kfree(pde);
+}
+
+/**
+ * pri_detector_get() - get pri detector for a given frequency and type
+ * @dpd: dfs_pattern_detector instance pointer
+ * @freq: frequency in MHz
+ * @radar_type: index of radar pattern
+ * @return pointer to pri detector on success, NULL otherwise
+ *
+ * Return existing pri detector for the given frequency or return a
+ * newly create one.
+ * Pri detector are "merged" by frequency so that if a pri detector for a freq
+ * of +/- 2Mhz already exists don't create a new one.
+ *
+ * Maybe will need to adapt frequency merge for pattern with chirp.
+ */
+static struct pri_detector *
+pri_detector_get(struct dfs_pattern_detector *dpd, u16 freq, u16 radar_type)
+{
+ struct pri_detector *pde, *cur = NULL;
+ list_for_each_entry(pde, &dpd->detectors[radar_type], head) {
+ if (pde->freq == freq) {
+ if (pde->count)
+ return pde;
+ else
+ cur = pde;
+ } else if (pde->freq - 2 == freq && pde->count) {
+ return pde;
+ } else if (pde->freq + 2 == freq && pde->count) {
+ return pde;
+ }
+ }
+
+ if (cur)
+ return cur;
+ else
+ return pri_detector_init(dpd, radar_type, freq);
+}
+
+
+/******************************************************************************
+ * DFS Pattern Detector
+ *****************************************************************************/
+/**
+ * dfs_pattern_detector_reset() - reset all channel detectors
+ *
+ * @dpd: dfs_pattern_detector
+ */
+static void dfs_pattern_detector_reset(struct dfs_pattern_detector *dpd)
+{
+ struct pri_detector *pde;
+ int i;
+
+ for (i = 0; i < dpd->num_radar_types; i++) {
+ if (!list_empty(&dpd->detectors[i]))
+ list_for_each_entry(pde, &dpd->detectors[i], head)
+ pri_detector_reset(pde, dpd->last_pulse_ts);
+ }
+
+ dpd->last_pulse_ts = 0;
+ dpd->prev_jiffies = jiffies;
+}
+
+/**
+ * dfs_pattern_detector_reset() - delete all channel detectors
+ *
+ * @dpd: dfs_pattern_detector
+ */
+static void dfs_pattern_detector_exit(struct dfs_pattern_detector *dpd)
+{
+ struct pri_detector *pde, *pde0;
+ int i;
+
+ for (i = 0; i < dpd->num_radar_types; i++) {
+ if (!list_empty(&dpd->detectors[i]))
+ list_for_each_entry_safe(pde, pde0, &dpd->detectors[i], head)
+ pri_detector_exit(pde);
+ }
+
+ kfree(dpd);
+}
+
+/**
+ * dfs_pattern_detector_pri_overflow - reset all channel detectors on pri
+ * overflow
+ * @dpd: dfs_pattern_detector
+ */
+static void dfs_pattern_detector_pri_overflow(struct dfs_pattern_detector *dpd)
+{
+ struct pri_detector *pde;
+ int i;
+
+ for (i = 0; i < dpd->num_radar_types; i++) {
+ if (!list_empty(&dpd->detectors[i]))
+ list_for_each_entry(pde, &dpd->detectors[i], head)
+ if (pde->ops->reset_on_pri_overflow)
+ pri_detector_reset(pde, dpd->last_pulse_ts);
+ }
+}
+
+/**
+ * dfs_pattern_detector_add_pulse - Process one pulse
+ *
+ * @dpd: dfs_pattern_detector
+ * @chain: Chain that correspond to this pattern_detector (only for debug)
+ * @freq: frequency of the pulse
+ * @pri: Delta with previous pulse. (0 if delta is too big for u16)
+ * @len: width of the pulse
+ * @now: jiffies value when pulse was received
+ *
+ * Get (or create) the channel_detector for this frequency. Then add the pulse
+ * in each pri_detector created in this channel_detector.
+ *
+ *
+ * @return True is the pulse complete a radar pattern, false otherwise
+ */
+static bool dfs_pattern_detector_add_pulse(struct dfs_pattern_detector *dpd,
+ enum ecrnx_radar_chain chain,
+ u16 freq, u16 pri, u16 len, u32 now)
+{
+ u32 i;
+
+ /*
+ * pulses received for a non-supported or un-initialized
+ * domain are treated as detected radars for fail-safety
+ */
+ if (dpd->region == NL80211_DFS_UNSET)
+ return true;
+
+ /* Compute pulse time stamp */
+ if (pri == 0) {
+ u32 delta_jiffie;
+ if (unlikely(now < dpd->prev_jiffies)) {
+ delta_jiffie = 0xffffffff - dpd->prev_jiffies + now;
+ } else {
+ delta_jiffie = now - dpd->prev_jiffies;
+ }
+ dpd->last_pulse_ts += jiffies_to_usecs(delta_jiffie);
+ dpd->prev_jiffies = now;
+ dfs_pattern_detector_pri_overflow(dpd);
+ } else {
+ dpd->last_pulse_ts += pri;
+ }
+
+ for (i = 0; i < dpd->num_radar_types; i++) {
+ struct pri_sequence *ps;
+ struct pri_detector *pde;
+ const struct radar_detector_specs *rs = &dpd->radar_spec[i];
+
+ /* no need to look up for pde if len is not within range */
+ if ((rs->width_min > len) ||
+ (rs->width_max < len)) {
+ continue;
+ }
+
+ pde = pri_detector_get(dpd, freq, i);
+ ps = pde->ops->add_pulse(pde, len, dpd->last_pulse_ts, pri);
+
+ if (ps != NULL) {
+ trace_radar_detected(chain, dpd->region, pde->freq, i, ps->pri);
+ // reset everything instead of just the channel detector
+ dfs_pattern_detector_reset(dpd);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/**
+ * get_dfs_domain_radar_types() - get radar types for a given DFS domain
+ * @param domain DFS domain
+ * @return radar_types ptr on success, NULL if DFS domain is not supported
+ */
+static const struct radar_types *
+get_dfs_domain_radar_types(enum nl80211_dfs_regions region)
+{
+ u32 i;
+ for (i = 0; i < ARRAY_SIZE(dfs_domains); i++) {
+ if (dfs_domains[i]->region == region)
+ return dfs_domains[i];
+ }
+ return NULL;
+}
+
+/**
+ * get_dfs_max_radar_types() - get maximum radar types for all supported domain
+ * @return the maximum number of radar pattern supported by on region
+ */
+static u16 get_dfs_max_radar_types(void)
+{
+ u32 i;
+ u16 max = 0;
+ for (i = 0; i < ARRAY_SIZE(dfs_domains); i++) {
+ if (dfs_domains[i]->num_radar_types > max)
+ max = dfs_domains[i]->num_radar_types;
+ }
+ return max;
+}
+
+/**
+ * dfs_pattern_detector_set_domain - set DFS domain
+ *
+ * @dpd: dfs_pattern_detector
+ * @region: DFS region
+ *
+ * set DFS domain, resets detector lines upon domain changes
+ */
+static
+bool dfs_pattern_detector_set_domain(struct dfs_pattern_detector *dpd,
+ enum nl80211_dfs_regions region, u8 chain)
+{
+ const struct radar_types *rt;
+ struct pri_detector *pde, *pde0;
+ int i;
+
+ if (dpd->region == region)
+ return true;
+
+ dpd->region = NL80211_DFS_UNSET;
+
+ rt = get_dfs_domain_radar_types(region);
+ if (rt == NULL)
+ return false;
+
+ /* delete all pri detectors for previous DFS domain */
+ for (i = 0; i < dpd->num_radar_types; i++) {
+ if (!list_empty(&dpd->detectors[i]))
+ list_for_each_entry_safe(pde, pde0, &dpd->detectors[i], head)
+ pri_detector_exit(pde);
+ }
+
+ if (chain == ECRNX_RADAR_RIU)
+ dpd->radar_spec = rt->spec_riu;
+ else
+ dpd->radar_spec = rt->spec_fcu;
+ dpd->num_radar_types = rt->num_radar_types;
+
+ dpd->region = region;
+ return true;
+}
+
+/**
+ * dfs_pattern_detector_init - Initialize dfs_pattern_detector
+ *
+ * @region: DFS region
+ * @return: pointer on dfs_pattern_detector
+ *
+ */
+static struct dfs_pattern_detector *
+dfs_pattern_detector_init(enum nl80211_dfs_regions region, u8 chain)
+{
+ struct dfs_pattern_detector *dpd;
+ u16 i, max_radar_type = get_dfs_max_radar_types();
+
+ dpd = kmalloc(sizeof(*dpd) + max_radar_type * sizeof(dpd->detectors[0]),
+ GFP_KERNEL);
+ if (dpd == NULL)
+ return NULL;
+
+ dpd->region = NL80211_DFS_UNSET;
+ dpd->enabled = ECRNX_RADAR_DETECT_DISABLE;
+ dpd->last_pulse_ts = 0;
+ dpd->prev_jiffies = jiffies;
+ dpd->num_radar_types = 0;
+ for (i = 0; i < max_radar_type; i++)
+ INIT_LIST_HEAD(&dpd->detectors[i]);
+
+ if (dfs_pattern_detector_set_domain(dpd, region, chain))
+ return dpd;
+
+ kfree(dpd);
+ return NULL;
+}
+
+
+/******************************************************************************
+ * driver interface
+ *****************************************************************************/
+static u16 ecrnx_radar_get_center_freq(struct ecrnx_hw *ecrnx_hw, u8 chain)
+{
+ if (chain == ECRNX_RADAR_FCU)
+ return ecrnx_hw->phy.sec_chan.center1_freq;
+
+ if (chain == ECRNX_RADAR_RIU) {
+#ifdef CONFIG_ECRNX_SOFTMAC
+ return ecrnx_hw->cur_freq;
+#else
+ if (!ecrnx_chanctx_valid(ecrnx_hw, ecrnx_hw->cur_chanctx)) {
+ WARN(1, "Radar pulse without channel information");
+ } else
+ return ecrnx_hw->chanctx_table[ecrnx_hw->cur_chanctx].chan_def.center_freq1;
+#endif /* CONFIG_ECRNX_SOFTMAC */
+ }
+
+ return 0;
+}
+
+static void ecrnx_radar_detected(struct ecrnx_hw *ecrnx_hw)
+{
+#ifdef CONFIG_ECRNX_SOFTMAC
+ ieee80211_radar_detected(ecrnx_hw->hw);
+#else
+ struct cfg80211_chan_def chan_def;
+
+ if (!ecrnx_chanctx_valid(ecrnx_hw, ecrnx_hw->cur_chanctx)) {
+ WARN(1, "Radar detected without channel information");
+ return;
+ }
+
+ /*
+ recopy chan_def in local variable because ecrnx_radar_cancel_cac may
+ clean the variable (if in CAC and it's the only vif using this context)
+ and CAC should be aborted before reporting the radar.
+ */
+ chan_def = ecrnx_hw->chanctx_table[ecrnx_hw->cur_chanctx].chan_def;
+
+ ecrnx_radar_cancel_cac(&ecrnx_hw->radar);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+ cfg80211_radar_event(ecrnx_hw->wiphy, &chan_def, GFP_KERNEL);
+#endif
+#endif /* CONFIG_ECRNX_SOFTMAC */
+}
+
+static void ecrnx_radar_process_pulse(struct work_struct *ws)
+{
+ struct ecrnx_radar *radar = container_of(ws, struct ecrnx_radar,
+ detection_work);
+ struct ecrnx_hw *ecrnx_hw = container_of(radar, struct ecrnx_hw, radar);
+ int chain;
+ u32 pulses[ECRNX_RADAR_LAST][ECRNX_RADAR_PULSE_MAX];
+ u16 pulses_count[ECRNX_RADAR_LAST];
+ u32 now = jiffies; /* would be better to store jiffies value in IT handler */
+
+ /* recopy pulses locally to avoid too long spin_lock */
+ spin_lock_bh(&radar->lock);
+ for (chain = ECRNX_RADAR_RIU; chain < ECRNX_RADAR_LAST; chain++) {
+ int start, count;
+
+ count = radar->pulses[chain].count;
+ start = radar->pulses[chain].index - count;
+ if (start < 0)
+ start += ECRNX_RADAR_PULSE_MAX;
+
+ pulses_count[chain] = count;
+ if (count == 0)
+ continue;
+
+ if ((start + count) > ECRNX_RADAR_PULSE_MAX) {
+ u16 count1 = (ECRNX_RADAR_PULSE_MAX - start);
+ memcpy(&(pulses[chain][0]),
+ &(radar->pulses[chain].buffer[start]),
+ count1 * sizeof(struct radar_pulse));
+ memcpy(&(pulses[chain][count1]),
+ &(radar->pulses[chain].buffer[0]),
+ (count - count1) * sizeof(struct radar_pulse));
+ } else {
+ memcpy(&(pulses[chain][0]),
+ &(radar->pulses[chain].buffer[start]),
+ count * sizeof(struct radar_pulse));
+ }
+ radar->pulses[chain].count = 0;
+ }
+ spin_unlock_bh(&radar->lock);
+
+
+ /* now process pulses */
+ for (chain = ECRNX_RADAR_RIU; chain < ECRNX_RADAR_LAST; chain++) {
+ int i;
+ u16 freq;
+
+ if (pulses_count[chain] == 0)
+ continue;
+
+ freq = ecrnx_radar_get_center_freq(ecrnx_hw, chain);
+
+ for (i = 0; i < pulses_count[chain] ; i++) {
+ struct radar_pulse *p = (struct radar_pulse *)&pulses[chain][i];
+ trace_radar_pulse(chain, p);
+ if (dfs_pattern_detector_add_pulse(radar->dpd[chain], chain,
+ (s16)freq + (2 * p->freq),
+ p->rep, (p->len * 2), now)) {
+ u16 idx = radar->detected[chain].index;
+
+ if (chain == ECRNX_RADAR_RIU) {
+ /* operating chain, inform upper layer to change channel */
+ if (radar->dpd[chain]->enabled == ECRNX_RADAR_DETECT_REPORT) {
+ ecrnx_radar_detected(ecrnx_hw);
+ /* no need to report new radar until upper layer set a
+ new channel. This prevent warning if a new radar is
+ detected while mac80211 is changing channel */
+ ecrnx_radar_detection_enable(radar,
+ ECRNX_RADAR_DETECT_DISABLE,
+ chain);
+ /* purge any event received since the beginning of the
+ function (we are sure not to interfer with tasklet
+ as we disable detection just before) */
+ radar->pulses[chain].count = 0;
+ }
+ } else {
+ /* secondary radar detection chain, simply report info in
+ debugfs for now */
+ }
+
+ radar->detected[chain].freq[idx] = (s16)freq + (2 * p->freq);
+ radar->detected[chain].time[idx] = ktime_get_real_seconds();
+ radar->detected[chain].index = ((idx + 1 ) %
+ NX_NB_RADAR_DETECTED);
+ radar->detected[chain].count++;
+ /* no need to process next pulses for this chain */
+ break;
+ }
+ }
+ }
+}
+
+#ifdef CONFIG_ECRNX_FULLMAC
+static void ecrnx_radar_cac_work(struct work_struct *ws)
+{
+ struct delayed_work *dw = container_of(ws, struct delayed_work, work);
+ struct ecrnx_radar *radar = container_of(dw, struct ecrnx_radar, cac_work);
+ struct ecrnx_hw *ecrnx_hw = container_of(radar, struct ecrnx_hw, radar);
+ struct ecrnx_chanctx *ctxt;
+
+ if (radar->cac_vif == NULL) {
+ WARN(1, "CAC finished but no vif set");
+ return;
+ }
+
+ ctxt = &ecrnx_hw->chanctx_table[radar->cac_vif->ch_index];
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+ cfg80211_cac_event(radar->cac_vif->ndev,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
+ &ctxt->chan_def,
+#endif
+ NL80211_RADAR_CAC_FINISHED, GFP_KERNEL);
+#endif
+ ecrnx_send_apm_stop_cac_req(ecrnx_hw, radar->cac_vif);
+ ecrnx_chanctx_unlink(radar->cac_vif);
+
+ radar->cac_vif = NULL;
+}
+#endif /* CONFIG_ECRNX_FULLMAC */
+
+bool ecrnx_radar_detection_init(struct ecrnx_radar *radar)
+{
+ spin_lock_init(&radar->lock);
+
+ radar->dpd[ECRNX_RADAR_RIU] = dfs_pattern_detector_init(NL80211_DFS_UNSET,
+ ECRNX_RADAR_RIU);
+ if (radar->dpd[ECRNX_RADAR_RIU] == NULL)
+ return false;
+
+ radar->dpd[ECRNX_RADAR_FCU] = dfs_pattern_detector_init(NL80211_DFS_UNSET,
+ ECRNX_RADAR_FCU);
+ if (radar->dpd[ECRNX_RADAR_FCU] == NULL) {
+ ecrnx_radar_detection_deinit(radar);
+ return false;
+ }
+
+ INIT_WORK(&radar->detection_work, ecrnx_radar_process_pulse);
+#ifdef CONFIG_ECRNX_FULLMAC
+ INIT_DELAYED_WORK(&radar->cac_work, ecrnx_radar_cac_work);
+ radar->cac_vif = NULL;
+#endif /* CONFIG_ECRNX_FULLMAC */
+ return true;
+}
+
+void ecrnx_radar_detection_deinit(struct ecrnx_radar *radar)
+{
+ if (radar->dpd[ECRNX_RADAR_RIU]) {
+ dfs_pattern_detector_exit(radar->dpd[ECRNX_RADAR_RIU]);
+ radar->dpd[ECRNX_RADAR_RIU] = NULL;
+ }
+ if (radar->dpd[ECRNX_RADAR_FCU]) {
+ dfs_pattern_detector_exit(radar->dpd[ECRNX_RADAR_FCU]);
+ radar->dpd[ECRNX_RADAR_FCU] = NULL;
+ }
+}
+
+bool ecrnx_radar_set_domain(struct ecrnx_radar *radar,
+ enum nl80211_dfs_regions region)
+{
+ if (radar->dpd[0] == NULL)
+ return false;
+
+ trace_radar_set_region(region);
+
+ return (dfs_pattern_detector_set_domain(radar->dpd[ECRNX_RADAR_RIU],
+ region, ECRNX_RADAR_RIU) &&
+ dfs_pattern_detector_set_domain(radar->dpd[ECRNX_RADAR_FCU],
+ region, ECRNX_RADAR_FCU));
+}
+
+void ecrnx_radar_detection_enable(struct ecrnx_radar *radar, u8 enable, u8 chain)
+{
+ if (chain < ECRNX_RADAR_LAST ) {
+ trace_radar_enable_detection(radar->dpd[chain]->region, enable, chain);
+ spin_lock_bh(&radar->lock);
+ radar->dpd[chain]->enabled = enable;
+ spin_unlock_bh(&radar->lock);
+ }
+}
+
+bool ecrnx_radar_detection_is_enable(struct ecrnx_radar *radar, u8 chain)
+{
+ return radar->dpd[chain]->enabled != ECRNX_RADAR_DETECT_DISABLE;
+}
+
+#ifdef CONFIG_ECRNX_FULLMAC
+void ecrnx_radar_start_cac(struct ecrnx_radar *radar, u32 cac_time_ms,
+ struct ecrnx_vif *vif)
+{
+ WARN(radar->cac_vif != NULL, "CAC already in progress");
+ radar->cac_vif = vif;
+ schedule_delayed_work(&radar->cac_work, msecs_to_jiffies(cac_time_ms));
+}
+
+void ecrnx_radar_cancel_cac(struct ecrnx_radar *radar)
+{
+ struct ecrnx_hw *ecrnx_hw = container_of(radar, struct ecrnx_hw, radar);
+
+ if (radar->cac_vif == NULL) {
+ return;
+ }
+
+ if (cancel_delayed_work(&radar->cac_work)) {
+ struct ecrnx_chanctx *ctxt;
+ ctxt = &ecrnx_hw->chanctx_table[radar->cac_vif->ch_index];
+ ecrnx_send_apm_stop_cac_req(ecrnx_hw, radar->cac_vif);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+ cfg80211_cac_event(radar->cac_vif->ndev,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
+ &ctxt->chan_def,
+#endif
+ NL80211_RADAR_CAC_ABORTED, GFP_KERNEL);
+#endif
+ ecrnx_chanctx_unlink(radar->cac_vif);
+ }
+
+ radar->cac_vif = NULL;
+}
+
+void ecrnx_radar_detection_enable_on_cur_channel(struct ecrnx_hw *ecrnx_hw)
+{
+ struct ecrnx_chanctx *ctxt;
+
+ /* If no information on current channel do nothing */
+ if (!ecrnx_chanctx_valid(ecrnx_hw, ecrnx_hw->cur_chanctx))
+ return;
+
+ ctxt = &ecrnx_hw->chanctx_table[ecrnx_hw->cur_chanctx];
+ if (ctxt->chan_def.chan->flags & IEEE80211_CHAN_RADAR) {
+ ecrnx_radar_detection_enable(&ecrnx_hw->radar,
+ ECRNX_RADAR_DETECT_REPORT,
+ ECRNX_RADAR_RIU);
+ } else {
+ ecrnx_radar_detection_enable(&ecrnx_hw->radar,
+ ECRNX_RADAR_DETECT_DISABLE,
+ ECRNX_RADAR_RIU);
+ }
+}
+#endif /* CONFIG_ECRNX_FULLMAC */
+
+/*****************************************************************************
+ * Debug functions
+ *****************************************************************************/
+static
+int ecrnx_radar_dump_pri_detector(char *buf, size_t len,
+ struct pri_detector *pde)
+{
+ char freq_info[] = "Freq = %3.dMhz\n";
+ char seq_info[] = " pri | count | false \n";
+ struct pri_sequence *seq;
+ int res, write = 0;
+
+ if (list_empty(&pde->sequences)) {
+ return 0;
+ }
+
+ if (buf == NULL) {
+ int nb_seq = 1;
+ list_for_each_entry(seq, &pde->sequences, head) {
+ nb_seq++;
+ }
+
+ return (sizeof(freq_info) + nb_seq * sizeof(seq_info));
+ }
+
+ res = scnprintf(buf, len, freq_info, pde->freq);
+ write += res;
+ len -= res;
+
+ res = scnprintf(&buf[write], len, "%s", seq_info);
+ write += res;
+ len -= res;
+
+ list_for_each_entry(seq, &pde->sequences, head) {
+ res = scnprintf(&buf[write], len, " %6.d | %2.d | %.2d \n",
+ seq->pri, seq->count, seq->count_falses);
+ write += res;
+ len -= res;
+ }
+
+ return write;
+}
+
+int ecrnx_radar_dump_pattern_detector(char *buf, size_t len,
+ struct ecrnx_radar *radar, u8 chain)
+{
+ struct dfs_pattern_detector *dpd = radar->dpd[chain];
+ char info[] = "Type = %3.d\n";
+ struct pri_detector *pde;
+ int i, res, write = 0;
+
+ /* if buf is NULL return size needed for dump */
+ if (buf == NULL) {
+ int size_needed = 0;
+
+ for (i = 0; i < dpd->num_radar_types; i++) {
+ list_for_each_entry(pde, &dpd->detectors[i], head) {
+ size_needed += ecrnx_radar_dump_pri_detector(NULL, 0, pde);
+ }
+ size_needed += sizeof(info);
+
+ return size_needed;
+ }
+ }
+
+ /* */
+ for (i = 0; i < dpd->num_radar_types; i++) {
+ res = scnprintf(&buf[write], len, info, i);
+
+ write += res;
+ len -= res;
+ list_for_each_entry(pde, &dpd->detectors[i], head) {
+ res = ecrnx_radar_dump_pri_detector(&buf[write], len, pde);
+ write += res;
+ len -= res;
+ }
+ }
+
+ return write;
+}
+
+
+int ecrnx_radar_dump_radar_detected(char *buf, size_t len,
+ struct ecrnx_radar *radar, u8 chain)
+{
+ struct ecrnx_radar_detected *detect = &(radar->detected[chain]);
+ char info[] = "2001/02/02 - 02:20 5126MHz\n";
+ int idx, i, res, write = 0;
+ int count = detect->count;
+
+ if (count > NX_NB_RADAR_DETECTED)
+ count = NX_NB_RADAR_DETECTED;
+
+ if (buf == NULL) {
+ return (count * sizeof(info)) + 1;
+ }
+
+ idx = (detect->index - detect->count) % NX_NB_RADAR_DETECTED;
+
+ for (i = 0; i < count; i++) {
+ struct tm tm;
+ time64_to_tm(detect->time[idx], 0, &tm);
+
+ res = scnprintf(&buf[write], len,
+ "%.4d/%.2d/%.2d - %.2d:%.2d %4.4dMHz\n",
+ (int)tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
+ tm.tm_hour, tm.tm_min, detect->freq[idx]);
+ write += res;
+ len -= res;
+
+ idx = (idx + 1 ) % NX_NB_RADAR_DETECTED;
+ }
+
+ return write;
+}
diff --git a/drivers/net/wireless/eswin/ecrnx_radar.h b/drivers/net/wireless/eswin/ecrnx_radar.h
new file mode 100644
index 000000000000..78a9d1d05ecd
--- /dev/null
+++ b/drivers/net/wireless/eswin/ecrnx_radar.h
@@ -0,0 +1,160 @@
+/**
+ ******************************************************************************
+ *
+ * @file ecrnx_radar.h
+ *
+ * @brief Functions to handle radar detection
+ *
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+#ifndef _ECRNX_RADAR_H_
+#define _ECRNX_RADAR_H_
+
+#include <linux/nl80211.h>
+
+struct ecrnx_vif;
+struct ecrnx_hw;
+
+enum ecrnx_radar_chain {
+ ECRNX_RADAR_RIU = 0,
+ ECRNX_RADAR_FCU,
+ ECRNX_RADAR_LAST
+};
+
+enum ecrnx_radar_detector {
+ ECRNX_RADAR_DETECT_DISABLE = 0, /* Ignore radar pulses */
+ ECRNX_RADAR_DETECT_ENABLE = 1, /* Process pattern detection but do not
+ report radar to upper layer (for test) */
+ ECRNX_RADAR_DETECT_REPORT = 2 /* Process pattern detection and report
+ radar to upper layer. */
+};
+
+#ifdef CONFIG_ECRNX_RADAR
+#include <linux/workqueue.h>
+#include <linux/spinlock.h>
+
+#define ECRNX_RADAR_PULSE_MAX 32
+
+/**
+ * struct ecrnx_radar_pulses - List of pulses reported by HW
+ * @index: write index
+ * @count: number of valid pulses
+ * @buffer: buffer of pulses
+ */
+struct ecrnx_radar_pulses {
+ /* Last radar pulses received */
+ int index;
+ int count;
+ u32 buffer[ECRNX_RADAR_PULSE_MAX];
+};
+
+/**
+ * struct dfs_pattern_detector - DFS pattern detector
+ * @region: active DFS region, NL80211_DFS_UNSET until set
+ * @num_radar_types: number of different radar types
+ * @last_pulse_ts: time stamp of last valid pulse in usecs
+ * @prev_jiffies:
+ * @radar_detector_specs: array of radar detection specs
+ * @channel_detectors: list connecting channel_detector elements
+ */
+struct dfs_pattern_detector {
+ u8 enabled;
+ enum nl80211_dfs_regions region;
+ u8 num_radar_types;
+ u64 last_pulse_ts;
+ u32 prev_jiffies;
+ const struct radar_detector_specs *radar_spec;
+ struct list_head detectors[];
+};
+
+#define NX_NB_RADAR_DETECTED 4
+
+/**
+ * struct ecrnx_radar_detected - List of radar detected
+ */
+struct ecrnx_radar_detected {
+ u16 index;
+ u16 count;
+ s64 time[NX_NB_RADAR_DETECTED];
+ s16 freq[NX_NB_RADAR_DETECTED];
+};
+
+
+struct ecrnx_radar {
+ struct ecrnx_radar_pulses pulses[ECRNX_RADAR_LAST];
+ struct dfs_pattern_detector *dpd[ECRNX_RADAR_LAST];
+ struct ecrnx_radar_detected detected[ECRNX_RADAR_LAST];
+ struct work_struct detection_work; /* Work used to process radar pulses */
+ spinlock_t lock; /* lock for pulses processing */
+
+ /* In softmac cac is handled by mac80211 */
+#ifdef CONFIG_ECRNX_FULLMAC
+ struct delayed_work cac_work; /* Work used to handle CAC */
+ struct ecrnx_vif *cac_vif; /* vif on which we started CAC */
+#endif
+};
+
+bool ecrnx_radar_detection_init(struct ecrnx_radar *radar);
+void ecrnx_radar_detection_deinit(struct ecrnx_radar *radar);
+bool ecrnx_radar_set_domain(struct ecrnx_radar *radar,
+ enum nl80211_dfs_regions region);
+void ecrnx_radar_detection_enable(struct ecrnx_radar *radar, u8 enable, u8 chain);
+bool ecrnx_radar_detection_is_enable(struct ecrnx_radar *radar, u8 chain);
+void ecrnx_radar_start_cac(struct ecrnx_radar *radar, u32 cac_time_ms,
+ struct ecrnx_vif *vif);
+void ecrnx_radar_cancel_cac(struct ecrnx_radar *radar);
+void ecrnx_radar_detection_enable_on_cur_channel(struct ecrnx_hw *ecrnx_hw);
+int ecrnx_radar_dump_pattern_detector(char *buf, size_t len,
+ struct ecrnx_radar *radar, u8 chain);
+int ecrnx_radar_dump_radar_detected(char *buf, size_t len,
+ struct ecrnx_radar *radar, u8 chain);
+
+#else
+
+struct ecrnx_radar {
+};
+
+static inline bool ecrnx_radar_detection_init(struct ecrnx_radar *radar)
+{return true;}
+
+static inline void ecrnx_radar_detection_deinit(struct ecrnx_radar *radar)
+{}
+
+static inline bool ecrnx_radar_set_domain(struct ecrnx_radar *radar,
+ enum nl80211_dfs_regions region)
+{return true;}
+
+static inline void ecrnx_radar_detection_enable(struct ecrnx_radar *radar,
+ u8 enable, u8 chain)
+{}
+
+static inline bool ecrnx_radar_detection_is_enable(struct ecrnx_radar *radar,
+ u8 chain)
+{return false;}
+
+static inline void ecrnx_radar_start_cac(struct ecrnx_radar *radar,
+ u32 cac_time_ms, struct ecrnx_vif *vif)
+{}
+
+static inline void ecrnx_radar_cancel_cac(struct ecrnx_radar *radar)
+{}
+
+static inline void ecrnx_radar_detection_enable_on_cur_channel(struct ecrnx_hw *ecrnx_hw)
+{}
+
+static inline int ecrnx_radar_dump_pattern_detector(char *buf, size_t len,
+ struct ecrnx_radar *radar,
+ u8 chain)
+{return 0;}
+
+static inline int ecrnx_radar_dump_radar_detected(char *buf, size_t len,
+ struct ecrnx_radar *radar,
+ u8 chain)
+{return 0;}
+
+#endif /* CONFIG_ECRNX_RADAR */
+
+#endif // _ECRNX_RADAR_H_
diff --git a/drivers/net/wireless/eswin/ecrnx_strs.c b/drivers/net/wireless/eswin/ecrnx_strs.c
new file mode 100644
index 000000000000..4352b9dc1dba
--- /dev/null
+++ b/drivers/net/wireless/eswin/ecrnx_strs.c
@@ -0,0 +1,252 @@
+/**
+ ******************************************************************************
+ *
+ * @file ecrnx_strs.c
+ *
+ * @brief Miscellaneous debug strings
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+
+#include "lmac_msg.h"
+
+static const char *const ecrnx_mmid2str[MSG_I(MM_MAX)] = {
+ [MSG_I(MM_RESET_REQ)] = "MM_RESET_REQ",
+ [MSG_I(MM_RESET_CFM)] = "MM_RESET_CFM",
+ [MSG_I(MM_START_REQ)] = "MM_START_REQ",
+ [MSG_I(MM_START_CFM)] = "MM_START_CFM",
+ [MSG_I(MM_VERSION_REQ)] = "MM_VERSION_REQ",
+ [MSG_I(MM_VERSION_CFM)] = "MM_VERSION_CFM",
+ [MSG_I(MM_ADD_IF_REQ)] = "MM_ADD_IF_REQ",
+ [MSG_I(MM_ADD_IF_CFM)] = "MM_ADD_IF_CFM",
+ [MSG_I(MM_REMOVE_IF_REQ)] = "MM_REMOVE_IF_REQ",
+ [MSG_I(MM_REMOVE_IF_CFM)] = "MM_REMOVE_IF_CFM",
+ [MSG_I(MM_STA_ADD_REQ)] = "MM_STA_ADD_REQ",
+ [MSG_I(MM_STA_ADD_CFM)] = "MM_STA_ADD_CFM",
+ [MSG_I(MM_STA_DEL_REQ)] = "MM_STA_DEL_REQ",
+ [MSG_I(MM_STA_DEL_CFM)] = "MM_STA_DEL_CFM",
+ [MSG_I(MM_SET_FILTER_REQ)] = "MM_SET_FILTER_REQ",
+ [MSG_I(MM_SET_FILTER_CFM)] = "MM_SET_FILTER_CFM",
+ [MSG_I(MM_SET_CHANNEL_REQ)] = "MM_SET_CHANNEL_REQ",
+ [MSG_I(MM_SET_CHANNEL_CFM)] = "MM_SET_CHANNEL_CFM",
+ [MSG_I(MM_SET_DTIM_REQ)] = "MM_SET_DTIM_REQ",
+ [MSG_I(MM_SET_DTIM_CFM)] = "MM_SET_DTIM_CFM",
+ [MSG_I(MM_SET_BEACON_INT_REQ)] = "MM_SET_BEACON_INT_REQ",
+ [MSG_I(MM_SET_BEACON_INT_CFM)] = "MM_SET_BEACON_INT_CFM",
+ [MSG_I(MM_SET_BASIC_RATES_REQ)] = "MM_SET_BASIC_RATES_REQ",
+ [MSG_I(MM_SET_BASIC_RATES_CFM)] = "MM_SET_BASIC_RATES_CFM",
+ [MSG_I(MM_SET_BSSID_REQ)] = "MM_SET_BSSID_REQ",
+ [MSG_I(MM_SET_BSSID_CFM)] = "MM_SET_BSSID_CFM",
+ [MSG_I(MM_SET_EDCA_REQ)] = "MM_SET_EDCA_REQ",
+ [MSG_I(MM_SET_EDCA_CFM)] = "MM_SET_EDCA_CFM",
+ [MSG_I(MM_SET_MODE_REQ)] = "MM_SET_MODE_REQ",
+ [MSG_I(MM_SET_MODE_CFM)] = "MM_SET_MODE_CFM",
+ [MSG_I(MM_SET_VIF_STATE_REQ)] = "MM_SET_VIF_STATE_REQ",
+ [MSG_I(MM_SET_VIF_STATE_CFM)] = "MM_SET_VIF_STATE_CFM",
+ [MSG_I(MM_SET_SLOTTIME_REQ)] = "MM_SET_SLOTTIME_REQ",
+ [MSG_I(MM_SET_SLOTTIME_CFM)] = "MM_SET_SLOTTIME_CFM",
+ [MSG_I(MM_SET_IDLE_REQ)] = "MM_SET_IDLE_REQ",
+ [MSG_I(MM_SET_IDLE_CFM)] = "MM_SET_IDLE_CFM",
+ [MSG_I(MM_KEY_ADD_REQ)] = "MM_KEY_ADD_REQ",
+ [MSG_I(MM_KEY_ADD_CFM)] = "MM_KEY_ADD_CFM",
+ [MSG_I(MM_KEY_DEL_REQ)] = "MM_KEY_DEL_REQ",
+ [MSG_I(MM_KEY_DEL_CFM)] = "MM_KEY_DEL_CFM",
+ [MSG_I(MM_BA_ADD_REQ)] = "MM_BA_ADD_REQ",
+ [MSG_I(MM_BA_ADD_CFM)] = "MM_BA_ADD_CFM",
+ [MSG_I(MM_BA_DEL_REQ)] = "MM_BA_DEL_REQ",
+ [MSG_I(MM_BA_DEL_CFM)] = "MM_BA_DEL_CFM",
+ [MSG_I(MM_PRIMARY_TBTT_IND)] = "MM_PRIMARY_TBTT_IND",
+ [MSG_I(MM_SECONDARY_TBTT_IND)] = "MM_SECONDARY_TBTT_IND",
+ [MSG_I(MM_SET_POWER_REQ)] = "MM_SET_POWER_REQ",
+ [MSG_I(MM_SET_POWER_CFM)] = "MM_SET_POWER_CFM",
+ [MSG_I(MM_DBG_TRIGGER_REQ)] = "MM_DBG_TRIGGER_REQ",
+ [MSG_I(MM_SET_PS_MODE_REQ)] = "MM_SET_PS_MODE_REQ",
+ [MSG_I(MM_SET_PS_MODE_CFM)] = "MM_SET_PS_MODE_CFM",
+ [MSG_I(MM_CHAN_CTXT_ADD_REQ)] = "MM_CHAN_CTXT_ADD_REQ",
+ [MSG_I(MM_CHAN_CTXT_ADD_CFM)] = "MM_CHAN_CTXT_ADD_CFM",
+ [MSG_I(MM_CHAN_CTXT_DEL_REQ)] = "MM_CHAN_CTXT_DEL_REQ",
+ [MSG_I(MM_CHAN_CTXT_DEL_CFM)] = "MM_CHAN_CTXT_DEL_CFM",
+ [MSG_I(MM_CHAN_CTXT_LINK_REQ)] = "MM_CHAN_CTXT_LINK_REQ",
+ [MSG_I(MM_CHAN_CTXT_LINK_CFM)] = "MM_CHAN_CTXT_LINK_CFM",
+ [MSG_I(MM_CHAN_CTXT_UNLINK_REQ)] = "MM_CHAN_CTXT_UNLINK_REQ",
+ [MSG_I(MM_CHAN_CTXT_UNLINK_CFM)] = "MM_CHAN_CTXT_UNLINK_CFM",
+ [MSG_I(MM_CHAN_CTXT_UPDATE_REQ)] = "MM_CHAN_CTXT_UPDATE_REQ",
+ [MSG_I(MM_CHAN_CTXT_UPDATE_CFM)] = "MM_CHAN_CTXT_UPDATE_CFM",
+ [MSG_I(MM_CHAN_CTXT_SCHED_REQ)] = "MM_CHAN_CTXT_SCHED_REQ",
+ [MSG_I(MM_CHAN_CTXT_SCHED_CFM)] = "MM_CHAN_CTXT_SCHED_CFM",
+ [MSG_I(MM_BCN_CHANGE_REQ)] = "MM_BCN_CHANGE_REQ",
+ [MSG_I(MM_BCN_CHANGE_CFM)] = "MM_BCN_CHANGE_CFM",
+ [MSG_I(MM_TIM_UPDATE_REQ)] = "MM_TIM_UPDATE_REQ",
+ [MSG_I(MM_TIM_UPDATE_CFM)] = "MM_TIM_UPDATE_CFM",
+ [MSG_I(MM_CONNECTION_LOSS_IND)] = "MM_CONNECTION_LOSS_IND",
+ [MSG_I(MM_CHANNEL_SWITCH_IND)] = "MM_CHANNEL_SWITCH_IND",
+ [MSG_I(MM_CHANNEL_PRE_SWITCH_IND)] = "MM_CHANNEL_PRE_SWITCH_IND",
+ [MSG_I(MM_REMAIN_ON_CHANNEL_REQ)] = "MM_REMAIN_ON_CHANNEL_REQ",
+ [MSG_I(MM_REMAIN_ON_CHANNEL_CFM)] = "MM_REMAIN_ON_CHANNEL_CFM",
+ [MSG_I(MM_REMAIN_ON_CHANNEL_EXP_IND)] = "MM_REMAIN_ON_CHANNEL_EXP_IND",
+ [MSG_I(MM_PS_CHANGE_IND)] = "MM_PS_CHANGE_IND",
+ [MSG_I(MM_TRAFFIC_REQ_IND)] = "MM_TRAFFIC_REQ_IND",
+ [MSG_I(MM_SET_PS_OPTIONS_REQ)] = "MM_SET_PS_OPTIONS_REQ",
+ [MSG_I(MM_SET_PS_OPTIONS_CFM)] = "MM_SET_PS_OPTIONS_CFM",
+ [MSG_I(MM_P2P_VIF_PS_CHANGE_IND)] = "MM_P2P_VIF_PS_CHANGE_IND",
+ [MSG_I(MM_CSA_COUNTER_IND)] = "MM_CSA_COUNTER_IND",
+ [MSG_I(MM_CHANNEL_SURVEY_IND)] = "MM_CHANNEL_SURVEY_IND",
+ [MSG_I(MM_SET_P2P_NOA_REQ)] = "MM_SET_P2P_NOA_REQ",
+ [MSG_I(MM_SET_P2P_OPPPS_REQ)] = "MM_SET_P2P_OPPPS_REQ",
+ [MSG_I(MM_SET_P2P_NOA_CFM)] = "MM_SET_P2P_NOA_CFM",
+ [MSG_I(MM_SET_P2P_OPPPS_CFM)] = "MM_SET_P2P_OPPPS_CFM",
+ [MSG_I(MM_CFG_RSSI_REQ)] = "MM_CFG_RSSI_REQ",
+ [MSG_I(MM_RSSI_STATUS_IND)] = "MM_RSSI_STATUS_IND",
+ [MSG_I(MM_CSA_FINISH_IND)] = "MM_CSA_FINISH_IND",
+ [MSG_I(MM_CSA_TRAFFIC_IND)] = "MM_CSA_TRAFFIC_IND",
+ [MSG_I(MM_MU_GROUP_UPDATE_REQ)] = "MM_MU_GROUP_UPDATE_REQ",
+ [MSG_I(MM_MU_GROUP_UPDATE_CFM)] = "MM_MU_GROUP_UPDATE_CFM",
+ [MSG_I(MM_GET_CAL_RESULT_REQ)] = "MM_GET_CAL_RESULT_REQ",
+ [MSG_I(MM_GET_CAL_RESULT_CFM)] = "MM_GET_CAL_RESULT_CFM",
+};
+
+static const char *const ecrnx_dbgid2str[MSG_I(DBG_MAX)] = {
+ [MSG_I(DBG_MEM_READ_REQ)] = "DBG_MEM_READ_REQ",
+ [MSG_I(DBG_MEM_READ_CFM)] = "DBG_MEM_READ_CFM",
+ [MSG_I(DBG_MEM_WRITE_REQ)] = "DBG_MEM_WRITE_REQ",
+ [MSG_I(DBG_MEM_WRITE_CFM)] = "DBG_MEM_WRITE_CFM",
+ [MSG_I(DBG_SET_MOD_FILTER_REQ)] = "DBG_SET_MOD_FILTER_REQ",
+ [MSG_I(DBG_SET_MOD_FILTER_CFM)] = "DBG_SET_MOD_FILTER_CFM",
+ [MSG_I(DBG_SET_SEV_FILTER_REQ)] = "DBG_SET_SEV_FILTER_REQ",
+ [MSG_I(DBG_SET_SEV_FILTER_CFM)] = "DBG_SET_SEV_FILTER_CFM",
+ [MSG_I(DBG_ERROR_IND)] = "DBG_ERROR_IND",
+ [MSG_I(DBG_GET_SYS_STAT_REQ)] = "DBG_GET_SYS_STAT_REQ",
+ [MSG_I(DBG_GET_SYS_STAT_CFM)] = "DBG_GET_SYS_STAT_CFM",
+};
+
+static const char *const ecrnx_scanid2str[MSG_I(SCAN_MAX)] = {
+ [MSG_I(SCAN_START_REQ)] = "SCAN_START_REQ",
+ [MSG_I(SCAN_START_CFM)] = "SCAN_START_CFM",
+ [MSG_I(SCAN_DONE_IND)] = "SCAN_DONE_IND",
+};
+
+static const char *const ecrnx_tdlsid2str[MSG_I(TDLS_MAX)] = {
+ [MSG_I(TDLS_CHAN_SWITCH_CFM)] = "TDLS_CHAN_SWITCH_CFM",
+ [MSG_I(TDLS_CHAN_SWITCH_REQ)] = "TDLS_CHAN_SWITCH_REQ",
+ [MSG_I(TDLS_CHAN_SWITCH_IND)] = "TDLS_CHAN_SWITCH_IND",
+ [MSG_I(TDLS_CHAN_SWITCH_BASE_IND)] = "TDLS_CHAN_SWITCH_BASE_IND",
+ [MSG_I(TDLS_CANCEL_CHAN_SWITCH_REQ)] = "TDLS_CANCEL_CHAN_SWITCH_REQ",
+ [MSG_I(TDLS_CANCEL_CHAN_SWITCH_CFM)] = "TDLS_CANCEL_CHAN_SWITCH_CFM",
+ [MSG_I(TDLS_PEER_PS_IND)] = "TDLS_PEER_PS_IND",
+ [MSG_I(TDLS_PEER_TRAFFIC_IND_REQ)] = "TDLS_PEER_TRAFFIC_IND_REQ",
+ [MSG_I(TDLS_PEER_TRAFFIC_IND_CFM)] = "TDLS_PEER_TRAFFIC_IND_CFM",
+};
+
+#ifdef CONFIG_ECRNX_FULLMAC
+
+static const char *const ecrnx_scanuid2str[MSG_I(SCANU_MAX)] = {
+ [MSG_I(SCANU_START_REQ)] = "SCANU_START_REQ",
+ [MSG_I(SCANU_START_CFM)] = "SCANU_START_CFM",
+ [MSG_I(SCANU_JOIN_REQ)] = "SCANU_JOIN_REQ",
+ [MSG_I(SCANU_JOIN_CFM)] = "SCANU_JOIN_CFM",
+ [MSG_I(SCANU_RESULT_IND)] = "SCANU_RESULT_IND",
+ [MSG_I(SCANU_FAST_REQ)] = "SCANU_FAST_REQ",
+ [MSG_I(SCANU_FAST_CFM)] = "SCANU_FAST_CFM",
+ [MSG_I(SCANU_CANCEL_REQ)] = "SCANU_CANCEL_REQ",
+ [MSG_I(SCANU_CANCEL_CFM)] = "SCANU_CANCEL_CFM",
+};
+
+static const char *const ecrnx_meid2str[MSG_I(ME_MAX)] = {
+ [MSG_I(ME_CONFIG_REQ)] = "ME_CONFIG_REQ",
+ [MSG_I(ME_CONFIG_CFM)] = "ME_CONFIG_CFM",
+ [MSG_I(ME_CHAN_CONFIG_REQ)] = "ME_CHAN_CONFIG_REQ",
+ [MSG_I(ME_CHAN_CONFIG_CFM)] = "ME_CHAN_CONFIG_CFM",
+ [MSG_I(ME_SET_CONTROL_PORT_REQ)] = "ME_SET_CONTROL_PORT_REQ",
+ [MSG_I(ME_SET_CONTROL_PORT_CFM)] = "ME_SET_CONTROL_PORT_CFM",
+ [MSG_I(ME_TKIP_MIC_FAILURE_IND)] = "ME_TKIP_MIC_FAILURE_IND",
+ [MSG_I(ME_STA_ADD_REQ)] = "ME_STA_ADD_REQ",
+ [MSG_I(ME_STA_ADD_CFM)] = "ME_STA_ADD_CFM",
+ [MSG_I(ME_STA_DEL_REQ)] = "ME_STA_DEL_REQ",
+ [MSG_I(ME_STA_DEL_CFM)] = "ME_STA_DEL_CFM",
+ [MSG_I(ME_TX_CREDITS_UPDATE_IND)]= "ME_TX_CREDITS_UPDATE_IND",
+ [MSG_I(ME_RC_STATS_REQ)] = "ME_RC_STATS_REQ",
+ [MSG_I(ME_RC_STATS_CFM)] = "ME_RC_STATS_CFM",
+ [MSG_I(ME_RC_SET_RATE_REQ)] = "ME_RC_SET_RATE_REQ",
+ [MSG_I(ME_TRAFFIC_IND_REQ)] = "ME_TRAFFIC_IND_REQ",
+ [MSG_I(ME_TRAFFIC_IND_CFM)] = "ME_TRAFFIC_IND_CFM",
+ [MSG_I(ME_SET_PS_MODE_REQ)] = "ME_SET_PS_MODE_REQ",
+ [MSG_I(ME_SET_PS_MODE_CFM)] = "ME_SET_PS_MODE_CFM",
+};
+
+static const char *const ecrnx_smid2str[MSG_I(SM_MAX)] = {
+ [MSG_I(SM_CONNECT_REQ)] = "SM_CONNECT_REQ",
+ [MSG_I(SM_CONNECT_CFM)] = "SM_CONNECT_CFM",
+ [MSG_I(SM_CONNECT_IND)] = "SM_CONNECT_IND",
+ [MSG_I(SM_DISCONNECT_REQ)] = "SM_DISCONNECT_REQ",
+ [MSG_I(SM_DISCONNECT_CFM)] = "SM_DISCONNECT_CFM",
+ [MSG_I(SM_DISCONNECT_IND)] = "SM_DISCONNECT_IND",
+ [MSG_I(SM_EXTERNAL_AUTH_REQUIRED_IND)] = "SM_EXTERNAL_AUTH_REQUIRED_IND",
+ [MSG_I(SM_EXTERNAL_AUTH_REQUIRED_RSP)] = "SM_EXTERNAL_AUTH_REQUIRED_RSP",
+};
+
+static const char *const ecrnx_apmid2str[MSG_I(APM_MAX)] = {
+ [MSG_I(APM_START_REQ)] = "APM_START_REQ",
+ [MSG_I(APM_START_CFM)] = "APM_START_CFM",
+ [MSG_I(APM_STOP_REQ)] = "APM_STOP_REQ",
+ [MSG_I(APM_STOP_CFM)] = "APM_STOP_CFM",
+ [MSG_I(APM_START_CAC_REQ)] = "APM_START_CAC_REQ",
+ [MSG_I(APM_START_CAC_CFM)] = "APM_START_CAC_CFM",
+ [MSG_I(APM_STOP_CAC_REQ)] = "APM_STOP_CAC_REQ",
+ [MSG_I(APM_STOP_CAC_CFM)] = "APM_STOP_CAC_CFM",
+};
+
+static const char *const ecrnx_meshid2str[MSG_I(MESH_MAX)] = {
+ [MSG_I(MESH_START_REQ)] = "MESH_START_REQ",
+ [MSG_I(MESH_START_CFM)] = "MESH_START_CFM",
+ [MSG_I(MESH_STOP_REQ)] = "MESH_STOP_REQ",
+ [MSG_I(MESH_STOP_CFM)] = "MESH_STOP_CFM",
+ [MSG_I(MESH_UPDATE_REQ)] = "MESH_UPDATE_REQ",
+ [MSG_I(MESH_UPDATE_CFM)] = "MESH_UPDATE_CFM",
+ [MSG_I(MESH_PATH_CREATE_REQ)] = "MESH_PATH_CREATE_REQ",
+ [MSG_I(MESH_PATH_CREATE_CFM)] = "MESH_PATH_CREATE_CFM",
+ [MSG_I(MESH_PATH_UPDATE_REQ)] = "MESH_PATH_UPDATE_REQ",
+ [MSG_I(MESH_PATH_UPDATE_CFM)] = "MESH_PATH_UPDATE_CFM",
+ [MSG_I(MESH_PROXY_ADD_REQ)] = "MESH_PROXY_ADD_REQ",
+ [MSG_I(MESH_PEER_UPDATE_IND)] = "MESH_PEER_UPDATE_IND",
+ [MSG_I(MESH_PATH_UPDATE_IND)] = "MESH_PATH_UPDATE_IND",
+ [MSG_I(MESH_PROXY_UPDATE_IND)] = "MESH_PROXY_UPDATE_IND",
+};
+
+static const char *const ecrnx_twtid2str[MSG_I(TWT_MAX)] = {
+ [MSG_I(TWT_SETUP_REQ)] = "TWT_SETUP_REQ",
+ [MSG_I(TWT_SETUP_CFM)] = "TWT_SETUP_CFM",
+ [MSG_I(TWT_SETUP_IND)] = "TWT_SETUP_IND",
+ [MSG_I(TWT_TEARDOWN_REQ)] = "TWT_TEARDOWN_REQ",
+ [MSG_I(TWT_TEARDOWN_CFM)] = "TWT_TEARDOWN_CFM",
+};
+
+#if defined(CONFIG_ECRNX_P2P)
+static const char *const rwnx_p2plistenid2str[MSG_I(P2P_LISTEN_MAX)] = {
+ [MSG_I(P2P_LISTEN_START_REQ)] = "P2P_LISTEN_START_REQ",
+ [MSG_I(P2P_LISTEN_START_CFM)] = "P2P_LISTEN_START_CFM",
+ [MSG_I(P2P_CANCEL_LISTEN_REQ)] = "P2P_CANCEL_LISTEN_REQ",
+ [MSG_I(P2P_CANCEL_LISTEN_CFM)] = "P2P_CANCEL_LISTEN_CFM",
+};
+#endif
+
+#endif /* CONFIG_ECRNX_FULLMAC */
+
+const char *const *ecrnx_id2str[TASK_LAST_EMB + 1] = {
+ [TASK_MM] = ecrnx_mmid2str,
+ [TASK_DBG] = ecrnx_dbgid2str,
+ [TASK_SCAN] = ecrnx_scanid2str,
+ [TASK_TDLS] = ecrnx_tdlsid2str,
+#ifdef CONFIG_ECRNX_FULLMAC
+ [TASK_SCANU] = ecrnx_scanuid2str,
+ [TASK_ME] = ecrnx_meid2str,
+ [TASK_SM] = ecrnx_smid2str,
+ [TASK_APM] = ecrnx_apmid2str,
+ [TASK_MESH] = ecrnx_meshid2str,
+#if defined(CONFIG_ECRNX_P2P)
+ [TASK_P2P_LISTEN] = rwnx_p2plistenid2str,
+#endif
+ [TASK_TWT] = ecrnx_twtid2str,
+#endif
+};
diff --git a/drivers/net/wireless/eswin/ecrnx_strs.h b/drivers/net/wireless/eswin/ecrnx_strs.h
new file mode 100644
index 000000000000..aa0d12915bd1
--- /dev/null
+++ b/drivers/net/wireless/eswin/ecrnx_strs.h
@@ -0,0 +1,31 @@
+/**
+ ****************************************************************************************
+ *
+ * @file ecrnx_strs.h
+ *
+ * @brief Miscellaneous debug strings
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ****************************************************************************************
+ */
+
+#ifndef _ECRNX_STRS_H_
+#define _ECRNX_STRS_H_
+
+#ifdef CONFIG_ECRNX_FHOST
+
+#define ECRNX_ID2STR(tag) "Cmd"
+
+#else
+#include "lmac_msg.h"
+
+#define ECRNX_ID2STR(tag) (((MSG_T(tag) < ARRAY_SIZE(ecrnx_id2str)) && \
+ (ecrnx_id2str[MSG_T(tag)]) && \
+ ((ecrnx_id2str[MSG_T(tag)])[MSG_I(tag)])) ? \
+ (ecrnx_id2str[MSG_T(tag)])[MSG_I(tag)] : "unknown")
+
+extern const char *const *ecrnx_id2str[TASK_LAST_EMB + 1];
+#endif /* CONFIG_ECRNX_FHOST */
+
+#endif /* _ECRNX_STRS_H_ */
diff --git a/drivers/net/wireless/eswin/ecrnx_testmode.c b/drivers/net/wireless/eswin/ecrnx_testmode.c
new file mode 100644
index 000000000000..7ce995e9040e
--- /dev/null
+++ b/drivers/net/wireless/eswin/ecrnx_testmode.c
@@ -0,0 +1,226 @@
+/**
+ ****************************************************************************************
+ *
+ * @file ecrnx_testmode.c
+ *
+ * @brief Test mode function definitions
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ****************************************************************************************
+ */
+
+#include <net/mac80211.h>
+#include <net/netlink.h>
+
+#include "ecrnx_testmode.h"
+#include "ecrnx_msg_tx.h"
+#include "ecrnx_dini.h"
+#include "reg_access.h"
+
+/*
+ * This function handles the user application commands for register access.
+ *
+ * It retrieves command ID carried with ECRNX_TM_ATTR_COMMAND and calls to the
+ * handlers respectively.
+ *
+ * If it's an unknown commdn ID, -ENOSYS is returned; or -ENOMSG if the
+ * mandatory fields(ECRNX_TM_ATTR_REG_OFFSET,ECRNX_TM_ATTR_REG_VALUE32)
+ * are missing; Otherwise 0 is replied indicating the success of the command execution.
+ *
+ * If ECRNX_TM_ATTR_COMMAND is ECRNX_TM_CMD_APP2DEV_REG_READ, the register read
+ * value is returned with ECRNX_TM_ATTR_REG_VALUE32.
+ *
+ * @hw: ieee80211_hw object that represents the device
+ * @tb: general message fields from the user space
+ */
+int ecrnx_testmode_reg(struct ieee80211_hw *hw, struct nlattr **tb)
+{
+ struct ecrnx_hw *ecrnx_hw = hw->priv;
+ u32 mem_addr, val32;
+ struct sk_buff *skb;
+ int status = 0;
+
+ /* First check if register address is there */
+ if (!tb[ECRNX_TM_ATTR_REG_OFFSET]) {
+ ECRNX_ERR("Error finding register offset\n");
+ return -ENOMSG;
+ }
+
+ mem_addr = nla_get_u32(tb[ECRNX_TM_ATTR_REG_OFFSET]);
+
+ switch (nla_get_u32(tb[ECRNX_TM_ATTR_COMMAND])) {
+ case ECRNX_TM_CMD_APP2DEV_REG_READ:
+ {
+ struct dbg_mem_read_cfm mem_read_cfm;
+
+ /*** Send the command to the LMAC ***/
+ if ((status = ecrnx_send_dbg_mem_read_req(ecrnx_hw, mem_addr, &mem_read_cfm)))
+ return status;
+
+ /* Allocate the answer message */
+ skb = cfg80211_testmode_alloc_reply_skb(hw->wiphy, 20);
+ if (!skb) {
+ ECRNX_ERR("Error allocating memory\n");
+ return -ENOMEM;
+ }
+
+ val32 = mem_read_cfm.memdata;
+ if (nla_put_u32(skb, ECRNX_TM_ATTR_REG_VALUE32, val32))
+ goto nla_put_failure;
+
+ /* Send the answer to upper layer */
+ status = cfg80211_testmode_reply(skb);
+ if (status < 0)
+ ECRNX_ERR("Error sending msg : %d\n", status);
+ }
+ break;
+
+ case ECRNX_TM_CMD_APP2DEV_REG_WRITE:
+ {
+ if (!tb[ECRNX_TM_ATTR_REG_VALUE32]) {
+ ECRNX_ERR("Error finding value to write\n");
+ return -ENOMSG;
+ } else {
+ val32 = nla_get_u32(tb[ECRNX_TM_ATTR_REG_VALUE32]);
+ /* Send the command to the LMAC */
+ if ((status = ecrnx_send_dbg_mem_write_req(ecrnx_hw, mem_addr, val32)))
+ return status;
+ }
+ }
+ break;
+
+ default:
+ ECRNX_ERR("Unknown testmode register command ID\n");
+ return -ENOSYS;
+ }
+
+ return status;
+
+nla_put_failure:
+ kfree_skb(skb);
+ return -EMSGSIZE;
+}
+
+/*
+ * This function handles the user application commands for Debug filter settings.
+ *
+ * @hw: ieee80211_hw object that represents the device
+ * @tb: general message fields from the user space
+ */
+int ecrnx_testmode_dbg_filter(struct ieee80211_hw *hw, struct nlattr **tb)
+{
+ struct ecrnx_hw *ecrnx_hw = hw->priv;
+ u32 filter;
+ int status = 0;
+
+ /* First check if the filter is there */
+ if (!tb[ECRNX_TM_ATTR_REG_FILTER]) {
+ ECRNX_ERR("Error finding filter value\n");
+ return -ENOMSG;
+ }
+
+ filter = nla_get_u32(tb[ECRNX_TM_ATTR_REG_FILTER]);
+ ECRNX_DBG("testmode debug filter, setting: 0x%x\n", filter);
+
+ switch (nla_get_u32(tb[ECRNX_TM_ATTR_COMMAND])) {
+ case ECRNX_TM_CMD_APP2DEV_SET_DBGMODFILTER:
+ {
+ /* Send the command to the LMAC */
+ if ((status = ecrnx_send_dbg_set_mod_filter_req(ecrnx_hw, filter)))
+ return status;
+ }
+ break;
+ case ECRNX_TM_CMD_APP2DEV_SET_DBGSEVFILTER:
+ {
+ /* Send the command to the LMAC */
+ if ((status = ecrnx_send_dbg_set_sev_filter_req(ecrnx_hw, filter)))
+ return status;
+ }
+ break;
+
+ default:
+ ECRNX_ERR("Unknown testmode register command ID\n");
+ return -ENOSYS;
+ }
+
+ return status;
+}
+
+/*
+ * This function handles the user application commands for register access without using
+ * the normal LMAC messaging way.
+ * This time register access will be done through direct PCI BAR windows. This can be used
+ * to access registers even when the :AMC FW is stuck.
+ *
+ * @hw: ieee80211_hw object that represents the device
+ * @tb: general message fields from the user space
+ */
+int ecrnx_testmode_reg_dbg(struct ieee80211_hw *hw, struct nlattr **tb)
+{
+ struct ecrnx_hw *ecrnx_hw = hw->priv;
+ struct ecrnx_plat *ecrnx_plat = ecrnx_hw->plat;
+ u32 mem_addr;
+ struct sk_buff *skb;
+ int status = 0;
+ volatile unsigned int reg_value = 0;
+ unsigned int offset;
+
+ /* First check if register address is there */
+ if (!tb[ECRNX_TM_ATTR_REG_OFFSET]) {
+ ECRNX_ERR("Error finding register offset\n");
+ return -ENOMSG;
+ }
+
+ mem_addr = nla_get_u32(tb[ECRNX_TM_ATTR_REG_OFFSET]);
+ offset = mem_addr & 0x00FFFFFF;
+
+ switch (nla_get_u32(tb[ECRNX_TM_ATTR_COMMAND])) {
+ case ECRNX_TM_CMD_APP2DEV_REG_READ_DBG:
+ {
+ /*** Send the command to the LMAC ***/
+ reg_value = ECRNX_REG_READ(ecrnx_plat, ECRNX_ADDR_SYSTEM, offset);
+
+ /* Allocate the answer message */
+ skb = cfg80211_testmode_alloc_reply_skb(hw->wiphy, 20);
+ if (!skb) {
+ ECRNX_ERR("Error allocating memory\n");
+ return -ENOMEM;
+ }
+
+ if (nla_put_u32(skb, ECRNX_TM_ATTR_REG_VALUE32, reg_value))
+ goto nla_put_failure;
+
+ /* Send the answer to upper layer */
+ status = cfg80211_testmode_reply(skb);
+ if (status < 0)
+ ECRNX_ERR("Error sending msg : %d\n", status);
+ }
+ break;
+
+ case ECRNX_TM_CMD_APP2DEV_REG_WRITE_DBG:
+ {
+ if (!tb[ECRNX_TM_ATTR_REG_VALUE32]) {
+ ECRNX_ERR("Error finding value to write\n");
+ return -ENOMSG;
+ } else {
+ reg_value = nla_get_u32(tb[ECRNX_TM_ATTR_REG_VALUE32]);
+
+ /* Send the command to the LMAC */
+ ECRNX_REG_WRITE(reg_value, ecrnx_plat, ECRNX_ADDR_SYSTEM,
+ offset);
+ }
+ }
+ break;
+
+ default:
+ ECRNX_ERR("Unknown testmode register command ID\n");
+ return -ENOSYS;
+ }
+
+ return status;
+
+nla_put_failure:
+ kfree_skb(skb);
+ return -EMSGSIZE;
+}
diff --git a/drivers/net/wireless/eswin/ecrnx_testmode.h b/drivers/net/wireless/eswin/ecrnx_testmode.h
new file mode 100644
index 000000000000..36d85620cea1
--- /dev/null
+++ b/drivers/net/wireless/eswin/ecrnx_testmode.h
@@ -0,0 +1,64 @@
+/**
+ ****************************************************************************************
+ *
+ * @file ecrnx_testmode.h
+ *
+ * @brief Test mode function declarations
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ****************************************************************************************
+ */
+
+#ifndef ECRNX_TESTMODE_H_
+#define ECRNX_TESTMODE_H_
+
+#include <net/mac80211.h>
+#include <net/netlink.h>
+
+/* Commands from user space to kernel space(ECRNX_TM_CMD_APP2DEV_XX) and
+ * from and kernel space to user space(ECRNX_TM_CMD_DEV2APP_XX).
+ * The command ID is carried with ECRNX_TM_ATTR_COMMAND.
+ */
+enum ecrnx_tm_cmd_t {
+ /* commands from user application to access register */
+ ECRNX_TM_CMD_APP2DEV_REG_READ = 1,
+ ECRNX_TM_CMD_APP2DEV_REG_WRITE,
+
+ /* commands from user application to select the Debug levels */
+ ECRNX_TM_CMD_APP2DEV_SET_DBGMODFILTER,
+ ECRNX_TM_CMD_APP2DEV_SET_DBGSEVFILTER,
+
+ /* commands to access registers without sending messages to LMAC layer,
+ * this must be used when LMAC FW is stuck. */
+ ECRNX_TM_CMD_APP2DEV_REG_READ_DBG,
+ ECRNX_TM_CMD_APP2DEV_REG_WRITE_DBG,
+
+ ECRNX_TM_CMD_MAX,
+};
+
+enum ecrnx_tm_attr_t {
+ ECRNX_TM_ATTR_NOT_APPLICABLE = 0,
+
+ ECRNX_TM_ATTR_COMMAND,
+
+ /* When ECRNX_TM_ATTR_COMMAND is ECRNX_TM_CMD_APP2DEV_REG_XXX,
+ * The mandatory fields are:
+ * ECRNX_TM_ATTR_REG_OFFSET for the offset of the target register;
+ * ECRNX_TM_ATTR_REG_VALUE32 for value */
+ ECRNX_TM_ATTR_REG_OFFSET,
+ ECRNX_TM_ATTR_REG_VALUE32,
+
+ /* When ECRNX_TM_ATTR_COMMAND is ECRNX_TM_CMD_APP2DEV_SET_DBGXXXFILTER,
+ * The mandatory field is ECRNX_TM_ATTR_REG_FILTER. */
+ ECRNX_TM_ATTR_REG_FILTER,
+
+ ECRNX_TM_ATTR_MAX,
+};
+
+/***********************************************************************/
+int ecrnx_testmode_reg(struct ieee80211_hw *hw, struct nlattr **tb);
+int ecrnx_testmode_dbg_filter(struct ieee80211_hw *hw, struct nlattr **tb);
+int ecrnx_testmode_reg_dbg(struct ieee80211_hw *hw, struct nlattr **tb);
+
+#endif /* ECRNX_TESTMODE_H_ */
diff --git a/drivers/net/wireless/eswin/ecrnx_txq.c b/drivers/net/wireless/eswin/ecrnx_txq.c
new file mode 100644
index 000000000000..80536b907e7c
--- /dev/null
+++ b/drivers/net/wireless/eswin/ecrnx_txq.c
@@ -0,0 +1,1772 @@
+/**
+ ******************************************************************************
+ *
+ * @file ecrnx_txq.c
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+
+#include "ecrnx_defs.h"
+#include "ecrnx_tx.h"
+#include "ipc_host.h"
+#include "ecrnx_events.h"
+
+/******************************************************************************
+ * Utils functions
+ *****************************************************************************/
+#ifdef CONFIG_ECRNX_SOFTMAC
+const int nx_tid_prio[NX_NB_TID_PER_STA] = {15, 7, 14, 6, 13, 5, 12, 4,
+ 11, 3, 8, 0, 10, 2, 9, 1};
+
+static inline int ecrnx_txq_sta_idx(struct ecrnx_sta *sta, u8 tid)
+{
+ return sta->sta_idx * NX_NB_TXQ_PER_STA;
+}
+
+static inline int ecrnx_txq_vif_idx(struct ecrnx_vif *vif, u8 ac)
+{
+ return vif->vif_index * NX_NB_TXQ_PER_VIF + ac + NX_FIRST_VIF_TXQ_IDX;
+}
+
+#ifdef CONFIG_MAC80211_TXQ
+struct ecrnx_txq *ecrnx_txq_sta_get(struct ecrnx_sta *ecrnx_sta, u8 tid)
+{
+ struct ieee80211_sta *sta = ecrnx_to_ieee80211_sta(ecrnx_sta);
+ struct ieee80211_txq *mac_txq = sta->txq[tid];
+
+ return (struct ecrnx_txq *)mac_txq->drv_priv;
+}
+
+struct ecrnx_txq *ecrnx_txq_vif_get(struct ecrnx_vif *ecrnx_vif, u8 ac)
+{
+ struct ieee80211_vif *vif = ecrnx_to_ieee80211_vif(ecrnx_vif);
+
+ /* mac80211 only allocate one txq per vif for Best Effort */
+ if (ac == ECRNX_HWQ_BE) {
+ struct ieee80211_txq *mac_txq = vif->txq;
+ if (!mac_txq)
+ return NULL;
+ return (struct ecrnx_txq *)mac_txq->drv_priv;
+ }
+
+ if (ac > NX_TXQ_CNT)
+ ac = ECRNX_HWQ_BK;
+
+ return &ecrnx_vif->txqs[ac];
+}
+
+#else /* ! CONFIG_MAC80211_TXQ */
+struct ecrnx_txq *ecrnx_txq_sta_get(struct ecrnx_sta *sta, u8 tid)
+{
+ if (tid >= NX_NB_TXQ_PER_STA)
+ tid = 0;
+
+ return &sta->txqs[tid];
+}
+
+struct ecrnx_txq *ecrnx_txq_vif_get(struct ecrnx_vif *vif, u8 ac)
+{
+ if (ac > NX_TXQ_CNT)
+ ac = ECRNX_HWQ_BK;
+
+ return &vif->txqs[ac];
+}
+
+#endif /* CONFIG_MAC80211_TXQ */
+
+static inline struct ecrnx_sta *ecrnx_txq_2_sta(struct ecrnx_txq *txq)
+{
+ if (txq->sta)
+ return (struct ecrnx_sta *)txq->sta->drv_priv;
+ return NULL;
+}
+
+#else /* CONFIG_ECRNX_FULLMAC */
+const int nx_tid_prio[NX_NB_TID_PER_STA] = {7, 6, 5, 4, 3, 0, 2, 1};
+
+static inline int ecrnx_txq_sta_idx(struct ecrnx_sta *sta, u8 tid)
+{
+ if (is_multicast_sta(sta->sta_idx))
+ return NX_FIRST_VIF_TXQ_IDX + sta->vif_idx;
+ else
+ return (sta->sta_idx * NX_NB_TXQ_PER_STA) + tid;
+}
+
+static inline int ecrnx_txq_vif_idx(struct ecrnx_vif *vif, u8 type)
+{
+ return NX_FIRST_VIF_TXQ_IDX + master_vif_idx(vif) + (type * NX_VIRT_DEV_MAX);
+}
+
+struct ecrnx_txq *ecrnx_txq_sta_get(struct ecrnx_sta *sta, u8 tid,
+ struct ecrnx_hw * ecrnx_hw)
+{
+ if (tid >= NX_NB_TXQ_PER_STA)
+ tid = 0;
+
+ return &ecrnx_hw->txq[ecrnx_txq_sta_idx(sta, tid)];
+}
+
+struct ecrnx_txq *ecrnx_txq_vif_get(struct ecrnx_vif *vif, u8 type)
+{
+ if (type > NX_UNK_TXQ_TYPE)
+ type = NX_BCMC_TXQ_TYPE;
+
+ return &vif->ecrnx_hw->txq[ecrnx_txq_vif_idx(vif, type)];
+}
+
+static inline struct ecrnx_sta *ecrnx_txq_2_sta(struct ecrnx_txq *txq)
+{
+ return txq->sta;
+}
+
+#endif /* CONFIG_ECRNX_SOFTMAC */
+
+
+/******************************************************************************
+ * Init/Deinit functions
+ *****************************************************************************/
+/**
+ * ecrnx_txq_init - Initialize a TX queue
+ *
+ * @txq: TX queue to be initialized
+ * @idx: TX queue index
+ * @status: TX queue initial status
+ * @hwq: Associated HW queue
+ * @ndev: Net device this queue belongs to
+ * (may be null for non netdev txq)
+ *
+ * Each queue is initialized with the credit of @NX_TXQ_INITIAL_CREDITS.
+ */
+static void ecrnx_txq_init(struct ecrnx_txq *txq, int idx, u8 status,
+ struct ecrnx_hwq *hwq, int tid,
+#ifdef CONFIG_ECRNX_SOFTMAC
+ struct ieee80211_sta *sta
+#else
+ struct ecrnx_sta *sta, struct net_device *ndev
+#endif
+ )
+{
+ int i;
+
+ txq->idx = idx;
+ txq->status = status;
+ txq->credits = NX_TXQ_INITIAL_CREDITS;
+ txq->pkt_sent = 0;
+ skb_queue_head_init(&txq->sk_list);
+ txq->last_retry_skb = NULL;
+ txq->nb_retry = 0;
+ txq->hwq = hwq;
+ txq->sta = sta;
+ for (i = 0; i < CONFIG_USER_MAX ; i++)
+ txq->pkt_pushed[i] = 0;
+ txq->push_limit = 0;
+ txq->tid = tid;
+#ifdef CONFIG_MAC80211_TXQ
+ txq->nb_ready_mac80211 = 0;
+#endif
+#ifdef CONFIG_ECRNX_SOFTMAC
+ txq->baw.agg_on = false;
+#ifdef CONFIG_ECRNX_AMSDUS_TX
+ txq->amsdu_anchor = NULL;
+ txq->amsdu_ht_len_cap = 0;
+ txq->amsdu_vht_len_cap = 0;
+#endif /* CONFIG_ECRNX_AMSDUS_TX */
+
+#else /* ! CONFIG_ECRNX_SOFTMAC */
+ txq->ps_id = LEGACY_PS_ID;
+ if (idx < NX_FIRST_VIF_TXQ_IDX) {
+ int sta_idx = sta->sta_idx;
+ int tid = idx - (sta_idx * NX_NB_TXQ_PER_STA);
+ if (tid < NX_NB_TID_PER_STA)
+ txq->ndev_idx = NX_STA_NDEV_IDX(tid, sta_idx);
+ else
+ txq->ndev_idx = NDEV_NO_TXQ;
+ } else if (idx < NX_FIRST_UNK_TXQ_IDX) {
+ txq->ndev_idx = NX_BCMC_TXQ_NDEV_IDX;
+ } else {
+ txq->ndev_idx = NDEV_NO_TXQ;
+ }
+ txq->ndev = ndev;
+#ifdef CONFIG_ECRNX_AMSDUS_TX
+ txq->amsdu = NULL;
+ txq->amsdu_len = 0;
+#endif /* CONFIG_ECRNX_AMSDUS_TX */
+#endif /* CONFIG_ECRNX_SOFTMAC */
+}
+
+/**
+ * ecrnx_txq_flush - Flush all buffers queued for a TXQ
+ *
+ * @ecrnx_hw: main driver data
+ * @txq: txq to flush
+ */
+void ecrnx_txq_drop_skb(struct ecrnx_txq *txq, struct sk_buff *skb, struct ecrnx_hw *ecrnx_hw, bool retry_packet)
+{
+ struct ecrnx_sw_txhdr *sw_txhdr;
+ unsigned long queued_time = 0;
+
+ skb_unlink(skb, &txq->sk_list);
+
+ sw_txhdr = ((struct ecrnx_txhdr *)skb->data)->sw_hdr;
+ /* hwq->len doesn't count skb to retry */
+#ifdef CONFIG_ECRNX_FULLMAC
+ queued_time = jiffies - sw_txhdr->jiffies;
+#endif /* CONFIG_ECRNX_SOFTMAC*/
+ trace_txq_drop_skb(skb, txq, queued_time);
+
+#ifdef CONFIG_ECRNX_AMSDUS_TX
+ if (sw_txhdr->desc.host.packet_cnt > 1) {
+ struct ecrnx_amsdu_txhdr *amsdu_txhdr;
+ list_for_each_entry(amsdu_txhdr, &sw_txhdr->amsdu.hdrs, list) {
+#ifndef CONFIG_ECRNX_ESWIN
+ dma_unmap_single(ecrnx_hw->dev, amsdu_txhdr->dma_addr,
+ amsdu_txhdr->map_len, DMA_TO_DEVICE);
+#endif
+ dev_kfree_skb_any(amsdu_txhdr->skb);
+ }
+#ifdef CONFIG_ECRNX_FULLMAC
+ if (txq->amsdu == sw_txhdr)
+ txq->amsdu = NULL;
+#endif
+ }
+#endif
+ kmem_cache_free(ecrnx_hw->sw_txhdr_cache, sw_txhdr);
+#ifndef CONFIG_ECRNX_ESWIN
+ dma_unmap_single(ecrnx_hw->dev, sw_txhdr->dma_addr, sw_txhdr->map_len,
+ DMA_TO_DEVICE);
+#endif
+ if (retry_packet) {
+ txq->nb_retry--;
+ if (txq->nb_retry == 0) {
+ WARN(skb != txq->last_retry_skb,
+ "last dropped retry buffer is not the expected one");
+ txq->last_retry_skb = NULL;
+ }
+ }
+#ifdef CONFIG_ECRNX_SOFTMAC
+ else
+ txq->hwq->len --; // hwq->len doesn't count skb to retry
+ ieee80211_free_txskb(ecrnx_hw->hw, skb);
+#else
+ dev_kfree_skb_any(skb);
+#endif /* CONFIG_ECRNX_SOFTMAC */
+}
+/**
+ * ecrnx_txq_flush - Flush all buffers queued for a TXQ
+ *
+ * @ecrnx_hw: main driver data
+ * @txq: txq to flush
+ */
+void ecrnx_txq_flush(struct ecrnx_hw *ecrnx_hw, struct ecrnx_txq *txq)
+{
+ int i, pushed = 0;
+
+ while(!skb_queue_empty(&txq->sk_list)) {
+ ecrnx_txq_drop_skb(txq, skb_peek(&txq->sk_list), ecrnx_hw, txq->nb_retry);
+ }
+
+ for (i = 0; i < CONFIG_USER_MAX; i++) {
+ pushed += txq->pkt_pushed[i];
+ }
+
+ if (pushed)
+ dev_warn(ecrnx_hw->dev, "TXQ[%d]: %d skb still pushed to the FW",
+ txq->idx, pushed);
+}
+
+/**
+ * ecrnx_txq_deinit - De-initialize a TX queue
+ *
+ * @ecrnx_hw: Driver main data
+ * @txq: TX queue to be de-initialized
+ * Any buffer stuck in a queue will be freed.
+ */
+static void ecrnx_txq_deinit(struct ecrnx_hw *ecrnx_hw, struct ecrnx_txq *txq)
+{
+ if (txq->idx == TXQ_INACTIVE)
+ return;
+
+ spin_lock_bh(&ecrnx_hw->tx_lock);
+ ecrnx_txq_del_from_hw_list(txq);
+ txq->idx = TXQ_INACTIVE;
+ spin_unlock_bh(&ecrnx_hw->tx_lock);
+
+ ecrnx_txq_flush(ecrnx_hw, txq);
+}
+
+/**
+ * ecrnx_txq_vif_init - Initialize all TXQ linked to a vif
+ *
+ * @ecrnx_hw: main driver data
+ * @ecrnx_vif: Pointer on VIF
+ * @status: Intial txq status
+ *
+ * Softmac : 1 VIF TXQ per HWQ
+ *
+ * Fullmac : 1 VIF TXQ for BC/MC
+ * 1 VIF TXQ for MGMT to unknown STA
+ */
+void ecrnx_txq_vif_init(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *ecrnx_vif,
+ u8 status)
+{
+ struct ecrnx_txq *txq;
+ int idx;
+
+#ifdef CONFIG_ECRNX_SOFTMAC
+ int ac;
+
+ idx = ecrnx_txq_vif_idx(ecrnx_vif, 0);
+ foreach_vif_txq(ecrnx_vif, txq, ac) {
+ if (txq) {
+ ecrnx_txq_init(txq, idx, status, &ecrnx_hw->hwq[ac], 0, NULL);
+#ifdef CONFIG_MAC80211_TXQ
+ if (ac != ECRNX_HWQ_BE)
+ txq->nb_ready_mac80211 = NOT_MAC80211_TXQ;
+#endif
+ }
+ idx++;
+ }
+
+#else
+ txq = ecrnx_txq_vif_get(ecrnx_vif, NX_BCMC_TXQ_TYPE);
+ idx = ecrnx_txq_vif_idx(ecrnx_vif, NX_BCMC_TXQ_TYPE);
+ ecrnx_txq_init(txq, idx, status, &ecrnx_hw->hwq[ECRNX_HWQ_BE], 0,
+ &ecrnx_hw->sta_table[ecrnx_vif->ap.bcmc_index], ecrnx_vif->ndev);
+
+ txq = ecrnx_txq_vif_get(ecrnx_vif, NX_UNK_TXQ_TYPE);
+ idx = ecrnx_txq_vif_idx(ecrnx_vif, NX_UNK_TXQ_TYPE);
+ ecrnx_txq_init(txq, idx, status, &ecrnx_hw->hwq[ECRNX_HWQ_VO], TID_MGT,
+ NULL, ecrnx_vif->ndev);
+
+#endif /* CONFIG_ECRNX_SOFTMAC */
+}
+
+/**
+ * ecrnx_txq_vif_deinit - Deinitialize all TXQ linked to a vif
+ *
+ * @ecrnx_hw: main driver data
+ * @ecrnx_vif: Pointer on VIF
+ */
+void ecrnx_txq_vif_deinit(struct ecrnx_hw * ecrnx_hw, struct ecrnx_vif *ecrnx_vif)
+{
+ struct ecrnx_txq *txq;
+
+#ifdef CONFIG_ECRNX_SOFTMAC
+ int ac;
+
+ foreach_vif_txq(ecrnx_vif, txq, ac) {
+ if (txq)
+ ecrnx_txq_deinit(ecrnx_hw, txq);
+ }
+
+#else
+ txq = ecrnx_txq_vif_get(ecrnx_vif, NX_BCMC_TXQ_TYPE);
+ ecrnx_txq_deinit(ecrnx_hw, txq);
+
+ txq = ecrnx_txq_vif_get(ecrnx_vif, NX_UNK_TXQ_TYPE);
+ ecrnx_txq_deinit(ecrnx_hw, txq);
+
+#endif /* CONFIG_ECRNX_SOFTMAC */
+}
+
+
+/**
+ * ecrnx_txq_sta_init - Initialize TX queues for a STA
+ *
+ * @ecrnx_hw: Main driver data
+ * @ecrnx_sta: STA for which tx queues need to be initialized
+ * @status: Intial txq status
+ *
+ * This function initialize all the TXQ associated to a STA.
+ * Softmac : 1 TXQ per TID
+ *
+ * Fullmac : 1 TXQ per TID (limited to 8)
+ * 1 TXQ for MGMT
+ */
+void ecrnx_txq_sta_init(struct ecrnx_hw *ecrnx_hw, struct ecrnx_sta *ecrnx_sta,
+ u8 status)
+{
+ struct ecrnx_txq *txq;
+ int tid, idx;
+
+#ifdef CONFIG_ECRNX_SOFTMAC
+ struct ieee80211_sta *sta = ecrnx_to_ieee80211_sta(ecrnx_sta);
+ idx = ecrnx_txq_sta_idx(ecrnx_sta, 0);
+
+ foreach_sta_txq(ecrnx_sta, txq, tid, ecrnx_hw) {
+ ecrnx_txq_init(txq, idx, status, &ecrnx_hw->hwq[ecrnx_tid2hwq[tid]], tid, sta);
+ idx++;
+ }
+
+#else
+ struct ecrnx_vif *ecrnx_vif = ecrnx_hw->vif_table[ecrnx_sta->vif_idx];
+ idx = ecrnx_txq_sta_idx(ecrnx_sta, 0);
+
+ foreach_sta_txq(ecrnx_sta, txq, tid, ecrnx_hw) {
+ ecrnx_txq_init(txq, idx, status, &ecrnx_hw->hwq[ecrnx_tid2hwq[tid]], tid,
+ ecrnx_sta, ecrnx_vif->ndev);
+ txq->ps_id = ecrnx_sta->uapsd_tids & (1 << tid) ? UAPSD_ID : LEGACY_PS_ID;
+ idx++;
+ }
+
+#endif /* CONFIG_ECRNX_SOFTMAC*/
+#ifndef CONFIG_ECRNX_ESWIN
+ ecrnx_ipc_sta_buffer_init(ecrnx_hw, ecrnx_sta->sta_idx);
+#endif
+}
+
+/**
+ * ecrnx_txq_sta_deinit - Deinitialize TX queues for a STA
+ *
+ * @ecrnx_hw: Main driver data
+ * @ecrnx_sta: STA for which tx queues need to be deinitialized
+ */
+void ecrnx_txq_sta_deinit(struct ecrnx_hw *ecrnx_hw, struct ecrnx_sta *ecrnx_sta)
+{
+ struct ecrnx_txq *txq;
+ int tid;
+
+ foreach_sta_txq(ecrnx_sta, txq, tid, ecrnx_hw) {
+ ecrnx_txq_deinit(ecrnx_hw, txq);
+ }
+}
+
+#ifdef CONFIG_ECRNX_FULLMAC
+/**
+ * ecrnx_txq_unk_vif_init - Initialize TXQ for unknown STA linked to a vif
+ *
+ * @ecrnx_vif: Pointer on VIF
+ */
+void ecrnx_txq_unk_vif_init(struct ecrnx_vif *ecrnx_vif)
+{
+ struct ecrnx_hw *ecrnx_hw = ecrnx_vif->ecrnx_hw;
+ struct ecrnx_txq *txq;
+ int idx;
+
+ txq = ecrnx_txq_vif_get(ecrnx_vif, NX_UNK_TXQ_TYPE);
+ idx = ecrnx_txq_vif_idx(ecrnx_vif, NX_UNK_TXQ_TYPE);
+ ecrnx_txq_init(txq, idx, 0, &ecrnx_hw->hwq[ECRNX_HWQ_VO], TID_MGT, NULL, ecrnx_vif->ndev);
+}
+
+/**
+ * ecrnx_txq_tdls_vif_deinit - Deinitialize TXQ for unknown STA linked to a vif
+ *
+ * @ecrnx_vif: Pointer on VIF
+ */
+void ecrnx_txq_unk_vif_deinit(struct ecrnx_vif *ecrnx_vif)
+{
+ struct ecrnx_txq *txq;
+
+ txq = ecrnx_txq_vif_get(ecrnx_vif, NX_UNK_TXQ_TYPE);
+ ecrnx_txq_deinit(ecrnx_vif->ecrnx_hw, txq);
+}
+
+/**
+ * ecrnx_init_unk_txq - Initialize TX queue for the transmission on a offchannel
+ *
+ * @vif: Interface for which the queue has to be initialized
+ *
+ * NOTE: Offchannel txq is only active for the duration of the ROC
+ */
+void ecrnx_txq_offchan_init(struct ecrnx_vif *ecrnx_vif)
+{
+ struct ecrnx_hw *ecrnx_hw = ecrnx_vif->ecrnx_hw;
+ struct ecrnx_txq *txq;
+
+ txq = &ecrnx_hw->txq[NX_OFF_CHAN_TXQ_IDX];
+ ecrnx_txq_init(txq, NX_OFF_CHAN_TXQ_IDX, ECRNX_TXQ_STOP_CHAN,
+ &ecrnx_hw->hwq[ECRNX_HWQ_VO], TID_MGT, NULL, ecrnx_vif->ndev);
+}
+
+/**
+ * ecrnx_deinit_offchan_txq - Deinitialize TX queue for offchannel
+ *
+ * @vif: Interface that manages the STA
+ *
+ * This function deintialize txq for one STA.
+ * Any buffer stuck in a queue will be freed.
+ */
+void ecrnx_txq_offchan_deinit(struct ecrnx_vif *ecrnx_vif)
+{
+ struct ecrnx_txq *txq;
+
+ txq = &ecrnx_vif->ecrnx_hw->txq[NX_OFF_CHAN_TXQ_IDX];
+ ecrnx_txq_deinit(ecrnx_vif->ecrnx_hw, txq);
+}
+
+
+/**
+ * ecrnx_txq_tdls_vif_init - Initialize TXQ vif for TDLS
+ *
+ * @ecrnx_vif: Pointer on VIF
+ */
+void ecrnx_txq_tdls_vif_init(struct ecrnx_vif *ecrnx_vif)
+{
+ struct ecrnx_hw *ecrnx_hw = ecrnx_vif->ecrnx_hw;
+
+ if (!(ecrnx_hw->wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS))
+ return;
+
+ ecrnx_txq_unk_vif_init(ecrnx_vif);
+}
+
+/**
+ * ecrnx_txq_tdls_vif_deinit - Deinitialize TXQ vif for TDLS
+ *
+ * @ecrnx_vif: Pointer on VIF
+ */
+void ecrnx_txq_tdls_vif_deinit(struct ecrnx_vif *ecrnx_vif)
+{
+ struct ecrnx_hw *ecrnx_hw = ecrnx_vif->ecrnx_hw;
+
+ if (!(ecrnx_hw->wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS))
+ return;
+
+ ecrnx_txq_unk_vif_deinit(ecrnx_vif);
+}
+/**
+ * ecrnx_txq_drop_old_traffic - Drop pkt queued for too long in a TXQ
+ *
+ * @txq: TXQ to process
+ * @ecrnx_hw: Driver main data
+ * @skb_timeout: Max queue duration, in jiffies, for this queue
+ * @dropped: Updated to inidicate if at least one skb was dropped
+ *
+ * @return Whether there is still pkt queued in this queue.
+ */
+static bool ecrnx_txq_drop_old_traffic(struct ecrnx_txq *txq, struct ecrnx_hw *ecrnx_hw,
+ unsigned long skb_timeout, bool *dropped)
+{
+ struct sk_buff *skb, *skb_next;
+ bool pkt_queued = false;
+ int retry_packet = txq->nb_retry;
+
+ if (txq->idx == TXQ_INACTIVE)
+ return false;
+
+ spin_lock(&ecrnx_hw->tx_lock);
+
+ skb_queue_walk_safe(&txq->sk_list, skb, skb_next) {
+
+ struct ecrnx_sw_txhdr *sw_txhdr;
+
+ if (retry_packet) {
+ // Don't drop retry packets
+ retry_packet--;
+ continue;
+ }
+
+ sw_txhdr = ((struct ecrnx_txhdr *)skb->data)->sw_hdr;
+
+ if (!time_after(jiffies, sw_txhdr->jiffies + skb_timeout)) {
+ pkt_queued = true;
+ break;
+ }
+
+ *dropped = true;
+ ECRNX_WARN("%s:skb:0x%08x,txq:0x%p, txq_idx:%d, ps_active:%d, ps_id:%d \n", __func__, skb, txq, txq->idx, txq->sta->ps.active, txq->ps_id);
+ ecrnx_txq_drop_skb(txq, skb, ecrnx_hw, false);
+ if (txq->sta && txq->sta->ps.active) {
+ txq->sta->ps.pkt_ready[txq->ps_id]--;
+ if (txq->sta->ps.pkt_ready[txq->ps_id] == 0)
+ ecrnx_set_traffic_status(ecrnx_hw, txq->sta, false, txq->ps_id);
+
+ // drop packet during PS service period
+ if (txq->sta->ps.sp_cnt[txq->ps_id]) {
+ txq->sta->ps.sp_cnt[txq->ps_id] --;
+ if (txq->push_limit)
+ txq->push_limit--;
+ if (WARN(((txq->ps_id == UAPSD_ID) &&
+ (txq->sta->ps.sp_cnt[txq->ps_id] == 0)),
+ "Drop last packet of UAPSD service period")) {
+ // TODO: inform FW to end SP
+ }
+ }
+ trace_ps_drop(txq->sta);
+ }
+ }
+
+ if (skb_queue_empty(&txq->sk_list)) {
+ ecrnx_txq_del_from_hw_list(txq);
+ txq->pkt_sent = 0;
+ }
+
+ spin_unlock(&ecrnx_hw->tx_lock);
+
+#ifdef CONFIG_ECRNX_FULLMAC
+ /* restart netdev queue if number no more queued buffer */
+ if (unlikely(txq->status & ECRNX_TXQ_NDEV_FLOW_CTRL) &&
+ skb_queue_empty(&txq->sk_list)) {
+ txq->status &= ~ECRNX_TXQ_NDEV_FLOW_CTRL;
+ netif_wake_subqueue(txq->ndev, txq->ndev_idx);
+ trace_txq_flowctrl_restart(txq);
+ }
+#endif /* CONFIG_ECRNX_FULLMAC */
+
+ return pkt_queued;
+}
+
+/**
+ * ecrnx_txq_drop_ap_vif_old_traffic - Drop pkt queued for too long in TXQs
+ * linked to an "AP" vif (AP, MESH, P2P_GO)
+ *
+ * @vif: Vif to process
+ * @return Whether there is still pkt queued in any TXQ.
+ */
+static bool ecrnx_txq_drop_ap_vif_old_traffic(struct ecrnx_vif *vif)
+{
+ struct ecrnx_sta *sta;
+ unsigned long timeout = (vif->ap.bcn_interval * HZ * 3) >> 10;
+ bool pkt_queued = false;
+ bool pkt_dropped = false;
+
+ // Should never be needed but still check VIF queues
+ ecrnx_txq_drop_old_traffic(ecrnx_txq_vif_get(vif, NX_BCMC_TXQ_TYPE),
+ vif->ecrnx_hw, ECRNX_TXQ_MAX_QUEUE_JIFFIES, &pkt_dropped);
+ ecrnx_txq_drop_old_traffic(ecrnx_txq_vif_get(vif, NX_UNK_TXQ_TYPE),
+ vif->ecrnx_hw, ECRNX_TXQ_MAX_QUEUE_JIFFIES, &pkt_dropped);
+ ECRNX_WARN("Dropped packet in BCMC/UNK queue. \n");
+
+ list_for_each_entry(sta, &vif->ap.sta_list, list) {
+ struct ecrnx_txq *txq;
+ int tid;
+ foreach_sta_txq(sta, txq, tid, vif->ecrnx_hw) {
+ pkt_queued |= ecrnx_txq_drop_old_traffic(txq, vif->ecrnx_hw,
+ timeout * sta->listen_interval,
+ &pkt_dropped);
+ }
+ }
+
+ return pkt_queued;
+}
+
+/**
+ * ecrnx_txq_drop_sta_vif_old_traffic - Drop pkt queued for too long in TXQs
+ * linked to a "STA" vif. In theory this should not be required as there is no
+ * case where traffic can accumulate in a STA interface.
+ *
+ * @vif: Vif to process
+ * @return Whether there is still pkt queued in any TXQ.
+ */
+static bool ecrnx_txq_drop_sta_vif_old_traffic(struct ecrnx_vif *vif)
+{
+ struct ecrnx_txq *txq;
+ bool pkt_queued = false, pkt_dropped = false;
+ int tid;
+
+ if (vif->tdls_status == TDLS_LINK_ACTIVE) {
+ txq = ecrnx_txq_vif_get(vif, NX_UNK_TXQ_TYPE);
+ pkt_queued |= ecrnx_txq_drop_old_traffic(txq, vif->ecrnx_hw,
+ ECRNX_TXQ_MAX_QUEUE_JIFFIES,
+ &pkt_dropped);
+ foreach_sta_txq(vif->sta.tdls_sta, txq, tid, vif->ecrnx_hw) {
+ pkt_queued |= ecrnx_txq_drop_old_traffic(txq, vif->ecrnx_hw,
+ ECRNX_TXQ_MAX_QUEUE_JIFFIES,
+ &pkt_dropped);
+ }
+ }
+
+ if (vif->sta.ap) {
+ foreach_sta_txq(vif->sta.ap, txq, tid, vif->ecrnx_hw) {
+ pkt_queued |= ecrnx_txq_drop_old_traffic(txq, vif->ecrnx_hw,
+ ECRNX_TXQ_MAX_QUEUE_JIFFIES,
+ &pkt_dropped);
+ }
+ }
+
+ if (pkt_dropped) {
+ ECRNX_WARN("Dropped packet in STA interface TXQs. \n");
+ }
+ return pkt_queued;
+}
+
+/**
+ * ecrnx_txq_cleanup_timer_cb - callack for TXQ cleaup timer
+ * Used to prevent pkt to accumulate in TXQ. The main use case is for AP
+ * interface with client in Power Save mode but just in case all TXQs are
+ * checked.
+ *
+ * @t: timer structure
+ */
+static void ecrnx_txq_cleanup_timer_cb(struct timer_list *t)
+{
+ struct ecrnx_hw *ecrnx_hw = from_timer(ecrnx_hw, t, txq_cleanup);
+ struct ecrnx_vif *vif;
+ bool pkt_queue = false;
+
+ list_for_each_entry(vif, &ecrnx_hw->vifs, list) {
+ switch (ECRNX_VIF_TYPE(vif)) {
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_P2P_GO:
+ case NL80211_IFTYPE_MESH_POINT:
+ pkt_queue |= ecrnx_txq_drop_ap_vif_old_traffic(vif);
+ break;
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_P2P_CLIENT:
+ pkt_queue |= ecrnx_txq_drop_sta_vif_old_traffic(vif);
+ break;
+ case NL80211_IFTYPE_AP_VLAN:
+ case NL80211_IFTYPE_MONITOR:
+ default:
+ continue;
+ }
+ }
+
+ if (pkt_queue)
+ mod_timer(t, jiffies + ECRNX_TXQ_CLEANUP_INTERVAL);
+}
+
+/**
+ * ecrnx_txq_start_cleanup_timer - Start 'cleanup' timer if not started
+ *
+ * @ecrnx_hw: Driver main data
+ */
+void ecrnx_txq_start_cleanup_timer(struct ecrnx_hw *ecrnx_hw, struct ecrnx_sta *sta)
+{
+ if (sta && !is_multicast_sta(sta->sta_idx) &&
+ !timer_pending(&ecrnx_hw->txq_cleanup))
+ mod_timer(&ecrnx_hw->txq_cleanup, jiffies + ECRNX_TXQ_CLEANUP_INTERVAL);
+}
+
+/**
+ * ecrnx_txq_prepare - Global initialization of txq
+ *
+ * @ecrnx_hw: Driver main data
+ */
+void ecrnx_txq_prepare(struct ecrnx_hw *ecrnx_hw)
+{
+ int i;
+
+ for (i = 0; i < NX_NB_TXQ; i++) {
+ ecrnx_hw->txq[i].idx = TXQ_INACTIVE;
+ }
+
+ timer_setup(&ecrnx_hw->txq_cleanup, ecrnx_txq_cleanup_timer_cb, 0);
+}
+#endif
+
+/******************************************************************************
+ * Start/Stop functions
+ *****************************************************************************/
+/**
+ * ecrnx_txq_add_to_hw_list - Add TX queue to a HW queue schedule list.
+ *
+ * @txq: TX queue to add
+ *
+ * Add the TX queue if not already present in the HW queue list.
+ * To be called with tx_lock hold
+ */
+void ecrnx_txq_add_to_hw_list(struct ecrnx_txq *txq)
+{
+ if (!(txq->status & ECRNX_TXQ_IN_HWQ_LIST)) {
+ trace_txq_add_to_hw(txq);
+ txq->status |= ECRNX_TXQ_IN_HWQ_LIST;
+ list_add_tail(&txq->sched_list, &txq->hwq->list);
+ txq->hwq->need_processing = true;
+ }
+}
+
+/**
+ * ecrnx_txq_del_from_hw_list - Delete TX queue from a HW queue schedule list.
+ *
+ * @txq: TX queue to delete
+ *
+ * Remove the TX queue from the HW queue list if present.
+ * To be called with tx_lock hold
+ */
+void ecrnx_txq_del_from_hw_list(struct ecrnx_txq *txq)
+{
+ if (txq->status & ECRNX_TXQ_IN_HWQ_LIST) {
+ trace_txq_del_from_hw(txq);
+ txq->status &= ~ECRNX_TXQ_IN_HWQ_LIST;
+ list_del(&txq->sched_list);
+ }
+}
+
+/**
+ * ecrnx_txq_skb_ready - Check if skb are available for the txq
+ *
+ * @txq: Pointer on txq
+ * @return True if there are buffer ready to be pushed on this txq,
+ * false otherwise
+ */
+static inline bool ecrnx_txq_skb_ready(struct ecrnx_txq *txq)
+{
+#ifdef CONFIG_MAC80211_TXQ
+ if (txq->nb_ready_mac80211 != NOT_MAC80211_TXQ)
+ return ((txq->nb_ready_mac80211 > 0) || !skb_queue_empty(&txq->sk_list));
+ else
+#endif
+ return !skb_queue_empty(&txq->sk_list);
+}
+
+/**
+ * ecrnx_txq_start - Try to Start one TX queue
+ *
+ * @txq: TX queue to start
+ * @reason: reason why the TX queue is started (among ECRNX_TXQ_STOP_xxx)
+ *
+ * Re-start the TX queue for one reason.
+ * If after this the txq is no longer stopped and some buffers are ready,
+ * the TX queue is also added to HW queue list.
+ * To be called with tx_lock hold
+ */
+void ecrnx_txq_start(struct ecrnx_txq *txq, u16 reason)
+{
+ BUG_ON(txq==NULL);
+ if (txq->idx != TXQ_INACTIVE && (txq->status & reason))
+ {
+ trace_txq_start(txq, reason);
+ txq->status &= ~reason;
+ if (!ecrnx_txq_is_stopped(txq) && ecrnx_txq_skb_ready(txq))
+ ecrnx_txq_add_to_hw_list(txq);
+ }
+}
+
+/**
+ * ecrnx_txq_stop - Stop one TX queue
+ *
+ * @txq: TX queue to stop
+ * @reason: reason why the TX queue is stopped (among ECRNX_TXQ_STOP_xxx)
+ *
+ * Stop the TX queue. It will remove the TX queue from HW queue list
+ * To be called with tx_lock hold
+ */
+void ecrnx_txq_stop(struct ecrnx_txq *txq, u16 reason)
+{
+ BUG_ON(txq==NULL);
+ if (txq->idx != TXQ_INACTIVE)
+ {
+ trace_txq_stop(txq, reason);
+ txq->status |= reason;
+ ecrnx_txq_del_from_hw_list(txq);
+ }
+}
+
+
+/**
+ * ecrnx_txq_sta_start - Start all the TX queue linked to a STA
+ *
+ * @sta: STA whose TX queues must be re-started
+ * @reason: Reason why the TX queue are restarted (among ECRNX_TXQ_STOP_xxx)
+ * @ecrnx_hw: Driver main data
+ *
+ * This function will re-start all the TX queues of the STA for the reason
+ * specified. It can be :
+ * - ECRNX_TXQ_STOP_STA_PS: the STA is no longer in power save mode
+ * - ECRNX_TXQ_STOP_VIF_PS: the VIF is in power save mode (p2p absence)
+ * - ECRNX_TXQ_STOP_CHAN: the STA's VIF is now on the current active channel
+ *
+ * Any TX queue with buffer ready and not Stopped for other reasons, will be
+ * added to the HW queue list
+ * To be called with tx_lock hold
+ */
+void ecrnx_txq_sta_start(struct ecrnx_sta *ecrnx_sta, u16 reason
+#ifdef CONFIG_ECRNX_FULLMAC
+ , struct ecrnx_hw *ecrnx_hw
+#endif
+ )
+{
+ struct ecrnx_txq *txq;
+ int tid;
+
+ trace_txq_sta_start(ecrnx_sta->sta_idx);
+ ECRNX_DBG("%s-%d:ecrnx_txq_stop,reaosn:0x%x,sta:0x%08x,sta_index:0x%08x \n", __func__, __LINE__, reason, ecrnx_sta, ecrnx_sta->sta_idx);
+ foreach_sta_txq(ecrnx_sta, txq, tid, ecrnx_hw) {
+ ecrnx_txq_start(txq, reason);
+
+ if (txq->idx != TXQ_INACTIVE && !skb_queue_empty(&txq->sk_list))
+ {
+ ecrnx_hwq_process(ecrnx_hw, txq->hwq);
+ }
+ }
+}
+
+
+/**
+ * ecrnx_stop_sta_txq - Stop all the TX queue linked to a STA
+ *
+ * @sta: STA whose TX queues must be stopped
+ * @reason: Reason why the TX queue are stopped (among ECRNX_TX_STOP_xxx)
+ * @ecrnx_hw: Driver main data
+ *
+ * This function will stop all the TX queues of the STA for the reason
+ * specified. It can be :
+ * - ECRNX_TXQ_STOP_STA_PS: the STA is in power save mode
+ * - ECRNX_TXQ_STOP_VIF_PS: the VIF is in power save mode (p2p absence)
+ * - ECRNX_TXQ_STOP_CHAN: the STA's VIF is not on the current active channel
+ *
+ * Any TX queue present in a HW queue list will be removed from this list.
+ * To be called with tx_lock hold
+ */
+void ecrnx_txq_sta_stop(struct ecrnx_sta *ecrnx_sta, u16 reason
+#ifdef CONFIG_ECRNX_FULLMAC
+ , struct ecrnx_hw *ecrnx_hw
+#endif
+ )
+{
+ struct ecrnx_txq *txq;
+ int tid;
+
+ if (!ecrnx_sta)
+ return;
+
+ trace_txq_sta_stop(ecrnx_sta->sta_idx);
+ foreach_sta_txq(ecrnx_sta, txq, tid, ecrnx_hw) {
+ ECRNX_DBG("%s-%d:stop_reaosn:0x%x,sta:0x%08x,sta_index:0x%08x ,txq:0x%p,tid:%d \n", __func__, __LINE__, reason, ecrnx_sta, ecrnx_sta->sta_idx, txq, tid);
+ ecrnx_txq_stop(txq, reason);
+ }
+}
+
+#ifdef CONFIG_ECRNX_SOFTMAC
+void ecrnx_txq_tdls_sta_start(struct ecrnx_sta *ecrnx_sta, u16 reason,
+ struct ecrnx_hw *ecrnx_hw)
+#else
+void ecrnx_txq_tdls_sta_start(struct ecrnx_vif *ecrnx_vif, u16 reason,
+ struct ecrnx_hw *ecrnx_hw)
+#endif
+{
+#ifdef CONFIG_ECRNX_SOFTMAC
+ trace_txq_vif_start(ecrnx_sta->vif_idx);
+ spin_lock_bh(&ecrnx_hw->tx_lock);
+
+ if (ecrnx_sta->tdls.active) {
+ ecrnx_txq_sta_start(ecrnx_sta, reason);
+ }
+
+ spin_unlock_bh(&ecrnx_hw->tx_lock);
+#else
+ trace_txq_vif_start(ecrnx_vif->vif_index);
+ spin_lock_bh(&ecrnx_hw->tx_lock);
+
+ if (ecrnx_vif->sta.tdls_sta)
+ ecrnx_txq_sta_start(ecrnx_vif->sta.tdls_sta, reason, ecrnx_hw);
+
+ spin_unlock_bh(&ecrnx_hw->tx_lock);
+#endif
+}
+
+#ifdef CONFIG_ECRNX_SOFTMAC
+void ecrnx_txq_tdls_sta_stop(struct ecrnx_sta *ecrnx_sta, u16 reason,
+ struct ecrnx_hw *ecrnx_hw)
+#else
+void ecrnx_txq_tdls_sta_stop(struct ecrnx_vif *ecrnx_vif, u16 reason,
+ struct ecrnx_hw *ecrnx_hw)
+#endif
+{
+#ifdef CONFIG_ECRNX_SOFTMAC
+ trace_txq_vif_stop(ecrnx_sta->vif_idx);
+
+ spin_lock_bh(&ecrnx_hw->tx_lock);
+
+ if (ecrnx_sta->tdls.active) {
+ ecrnx_txq_sta_stop(ecrnx_sta, reason);
+ }
+
+ spin_unlock_bh(&ecrnx_hw->tx_lock);
+#else
+ trace_txq_vif_stop(ecrnx_vif->vif_index);
+
+ spin_lock_bh(&ecrnx_hw->tx_lock);
+
+ if (ecrnx_vif->sta.tdls_sta)
+ ecrnx_txq_sta_stop(ecrnx_vif->sta.tdls_sta, reason, ecrnx_hw);
+
+ spin_unlock_bh(&ecrnx_hw->tx_lock);
+#endif
+}
+
+#ifdef CONFIG_ECRNX_FULLMAC
+static inline
+void ecrnx_txq_vif_for_each_sta(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *ecrnx_vif,
+ void (*f)(struct ecrnx_sta *, u16, struct ecrnx_hw *),
+ u16 reason)
+{
+ switch (ECRNX_VIF_TYPE(ecrnx_vif)) {
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_P2P_CLIENT:
+ {
+ if (ecrnx_vif->tdls_status == TDLS_LINK_ACTIVE)
+ f(ecrnx_vif->sta.tdls_sta, reason, ecrnx_hw);
+#ifdef CONFIG_ECRNX_P2P
+ if (!(ecrnx_vif->sta.ap == NULL))
+#else
+ if (!WARN_ON(ecrnx_vif->sta.ap == NULL))
+#endif
+ f(ecrnx_vif->sta.ap, reason, ecrnx_hw);
+ break;
+ }
+ case NL80211_IFTYPE_AP_VLAN:
+ {
+ ecrnx_vif = ecrnx_vif->ap_vlan.master;
+ struct ecrnx_sta *sta;
+ list_for_each_entry(sta, &ecrnx_vif->ap.sta_list, list) {
+ f(sta, reason, ecrnx_hw);
+ }
+ break;
+ }
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_MESH_POINT:
+ case NL80211_IFTYPE_P2P_GO:
+ {
+ struct ecrnx_sta *sta;
+ list_for_each_entry(sta, &ecrnx_vif->ap.sta_list, list) {
+ f(sta, reason, ecrnx_hw);
+ }
+ break;
+ }
+ default:
+ BUG();
+ break;
+ }
+}
+
+#endif
+
+/**
+ * ecrnx_txq_vif_start - START TX queues of all STA associated to the vif
+ * and vif's TXQ
+ *
+ * @vif: Interface to start
+ * @reason: Start reason (ECRNX_TXQ_STOP_CHAN or ECRNX_TXQ_STOP_VIF_PS)
+ * @ecrnx_hw: Driver main data
+ *
+ * Iterate over all the STA associated to the vif and re-start them for the
+ * reason @reason
+ * Take tx_lock
+ */
+void ecrnx_txq_vif_start(struct ecrnx_vif *ecrnx_vif, u16 reason,
+ struct ecrnx_hw *ecrnx_hw)
+{
+ struct ecrnx_txq *txq;
+#ifdef CONFIG_ECRNX_SOFTMAC
+ struct ecrnx_sta *ecrnx_sta;
+ int ac;
+#endif
+
+ trace_txq_vif_start(ecrnx_vif->vif_index);
+
+ spin_lock_bh(&ecrnx_hw->tx_lock);
+
+#ifdef CONFIG_ECRNX_SOFTMAC
+ list_for_each_entry(ecrnx_sta, &ecrnx_vif->stations, list) {
+ if ((!ecrnx_vif->roc_tdls) ||
+ (ecrnx_sta->tdls.active && ecrnx_vif->roc_tdls && ecrnx_sta->tdls.chsw_en))
+ ecrnx_txq_sta_start(ecrnx_sta, reason);
+ }
+
+ foreach_vif_txq(ecrnx_vif, txq, ac) {
+ if (txq)
+ ecrnx_txq_start(txq, reason);
+ }
+#else
+ //Reject if monitor interface
+ if (ecrnx_vif->wdev.iftype == NL80211_IFTYPE_MONITOR)
+ goto end;
+
+ if (ecrnx_vif->roc_tdls && ecrnx_vif->sta.tdls_sta && ecrnx_vif->sta.tdls_sta->tdls.chsw_en) {
+ ecrnx_txq_sta_start(ecrnx_vif->sta.tdls_sta, reason, ecrnx_hw);
+ }
+ if (!ecrnx_vif->roc_tdls) {
+ ecrnx_txq_vif_for_each_sta(ecrnx_hw, ecrnx_vif, ecrnx_txq_sta_start, reason);
+ }
+
+ txq = ecrnx_txq_vif_get(ecrnx_vif, NX_BCMC_TXQ_TYPE);
+ ecrnx_txq_start(txq, reason);
+ txq = ecrnx_txq_vif_get(ecrnx_vif, NX_UNK_TXQ_TYPE);
+ ecrnx_txq_start(txq, reason);
+
+end:
+#endif /* CONFIG_ECRNX_SOFTMAC */
+
+ spin_unlock_bh(&ecrnx_hw->tx_lock);
+}
+
+
+/**
+ * ecrnx_txq_vif_stop - STOP TX queues of all STA associated to the vif
+ *
+ * @vif: Interface to stop
+ * @arg: Stop reason (ECRNX_TXQ_STOP_CHAN or ECRNX_TXQ_STOP_VIF_PS)
+ * @ecrnx_hw: Driver main data
+ *
+ * Iterate over all the STA associated to the vif and stop them for the
+ * reason ECRNX_TXQ_STOP_CHAN or ECRNX_TXQ_STOP_VIF_PS
+ * Take tx_lock
+ */
+void ecrnx_txq_vif_stop(struct ecrnx_vif *ecrnx_vif, u16 reason,
+ struct ecrnx_hw *ecrnx_hw)
+{
+ struct ecrnx_txq *txq;
+#ifdef CONFIG_ECRNX_SOFTMAC
+ struct ecrnx_sta *sta;
+ int ac;
+#endif
+
+ trace_txq_vif_stop(ecrnx_vif->vif_index);
+ spin_lock_bh(&ecrnx_hw->tx_lock);
+
+#ifdef CONFIG_ECRNX_SOFTMAC
+ list_for_each_entry(sta, &ecrnx_vif->stations, list) {
+ ecrnx_txq_sta_stop(sta, reason);
+ }
+
+ foreach_vif_txq(ecrnx_vif, txq, ac) {
+ if (txq)
+ {
+ ecrnx_txq_stop(txq, reason);
+ }
+ }
+
+#else
+ //Reject if monitor interface
+ if (ecrnx_vif->wdev.iftype == NL80211_IFTYPE_MONITOR)
+ goto end;
+
+ ecrnx_txq_vif_for_each_sta(ecrnx_hw, ecrnx_vif, ecrnx_txq_sta_stop, reason);
+
+ txq = ecrnx_txq_vif_get(ecrnx_vif, NX_BCMC_TXQ_TYPE);
+ ecrnx_txq_stop(txq, reason);
+ txq = ecrnx_txq_vif_get(ecrnx_vif, NX_UNK_TXQ_TYPE);
+ ecrnx_txq_stop(txq, reason);
+
+end:
+#endif /* CONFIG_ECRNX_SOFTMAC*/
+
+ spin_unlock_bh(&ecrnx_hw->tx_lock);
+}
+
+#ifdef CONFIG_ECRNX_FULLMAC
+
+/**
+ * ecrnx_start_offchan_txq - START TX queue for offchannel frame
+ *
+ * @ecrnx_hw: Driver main data
+ */
+void ecrnx_txq_offchan_start(struct ecrnx_hw *ecrnx_hw)
+{
+ struct ecrnx_txq *txq;
+
+ txq = &ecrnx_hw->txq[NX_OFF_CHAN_TXQ_IDX];
+ spin_lock_bh(&ecrnx_hw->tx_lock);
+ ecrnx_txq_start(txq, ECRNX_TXQ_STOP_CHAN);
+ spin_unlock_bh(&ecrnx_hw->tx_lock);
+ ECRNX_DBG("%s-%d:ecrnx_txq_start,reaosn:0x%x \n", __func__, __LINE__, ECRNX_TXQ_STOP_CHAN);
+}
+
+/**
+ * ecrnx_switch_vif_sta_txq - Associate TXQ linked to a STA to a new vif
+ *
+ * @sta: STA whose txq must be switched
+ * @old_vif: Vif currently associated to the STA (may no longer be active)
+ * @new_vif: vif which should be associated to the STA for now on
+ *
+ * This function will switch the vif (i.e. the netdev) associated to all STA's
+ * TXQ. This is used when AP_VLAN interface are created.
+ * If one STA is associated to an AP_vlan vif, it will be moved from the master
+ * AP vif to the AP_vlan vif.
+ * If an AP_vlan vif is removed, then STA will be moved back to mastert AP vif.
+ *
+ */
+void ecrnx_txq_sta_switch_vif(struct ecrnx_sta *sta, struct ecrnx_vif *old_vif,
+ struct ecrnx_vif *new_vif)
+{
+ struct ecrnx_hw *ecrnx_hw = new_vif->ecrnx_hw;
+ struct ecrnx_txq *txq;
+ int i;
+
+ /* start TXQ on the new interface, and update ndev field in txq */
+ if (!netif_carrier_ok(new_vif->ndev))
+ netif_carrier_on(new_vif->ndev);
+ txq = ecrnx_txq_sta_get(sta, 0, ecrnx_hw);
+ for (i = 0; i < NX_NB_TID_PER_STA; i++, txq++) {
+ txq->ndev = new_vif->ndev;
+ netif_wake_subqueue(txq->ndev, txq->ndev_idx);
+ }
+}
+#endif /* CONFIG_ECRNX_FULLMAC */
+
+/******************************************************************************
+ * TXQ queue/schedule functions
+ *****************************************************************************/
+/**
+ * ecrnx_txq_queue_skb - Queue a buffer in a TX queue
+ *
+ * @skb: Buffer to queue
+ * @txq: TX Queue in which the buffer must be added
+ * @ecrnx_hw: Driver main data
+ * @retry: Should it be queued in the retry list
+ *
+ * @return: Retrun 1 if txq has been added to hwq list, 0 otherwise
+ *
+ * Add a buffer in the buffer list of the TX queue
+ * and add this TX queue in the HW queue list if the txq is not stopped.
+ * If this is a retry packet it is added after the last retry packet or at the
+ * beginning if there is no retry packet queued.
+ *
+ * If the STA is in PS mode and this is the first packet queued for this txq
+ * update TIM.
+ *
+ * To be called with tx_lock hold
+ */
+int ecrnx_txq_queue_skb(struct sk_buff *skb, struct ecrnx_txq *txq,
+ struct ecrnx_hw *ecrnx_hw, bool retry,
+ struct sk_buff *skb_prev)
+{
+
+#ifdef CONFIG_ECRNX_FULLMAC
+ if (unlikely(txq->sta && txq->sta->ps.active)) {
+ txq->sta->ps.pkt_ready[txq->ps_id]++;
+ trace_ps_queue(txq->sta);
+
+ if (txq->sta->ps.pkt_ready[txq->ps_id] == 1) {
+ ecrnx_set_traffic_status(ecrnx_hw, txq->sta, true, txq->ps_id);
+ }
+ }
+#endif
+
+ if (!retry) {
+ /* add buffer in the sk_list */
+ if (skb_prev)
+ skb_append(skb_prev, skb, &txq->sk_list);
+ else
+ skb_queue_tail(&txq->sk_list, skb);
+#ifdef CONFIG_ECRNX_FULLMAC
+ // to update for SOFTMAC
+ ecrnx_ipc_sta_buffer(ecrnx_hw, txq->sta, txq->tid,
+ ((struct ecrnx_txhdr *)skb->data)->sw_hdr->frame_len);
+ ecrnx_txq_start_cleanup_timer(ecrnx_hw, txq->sta);
+#endif
+ } else {
+ if (txq->last_retry_skb)
+ skb_append(txq->last_retry_skb, skb, &txq->sk_list);
+ else
+ skb_queue_head(&txq->sk_list, skb);
+
+ txq->last_retry_skb = skb;
+ txq->nb_retry++;
+ }
+
+ trace_txq_queue_skb(skb, txq, retry);
+
+ /* Flowctrl corresponding netdev queue if needed */
+#ifdef CONFIG_ECRNX_FULLMAC
+ /* If too many buffer are queued for this TXQ stop netdev queue */
+ if ((txq->ndev_idx != NDEV_NO_TXQ) &&
+ (skb_queue_len(&txq->sk_list) > ECRNX_NDEV_FLOW_CTRL_STOP)) {
+ txq->status |= ECRNX_TXQ_NDEV_FLOW_CTRL;
+ netif_stop_subqueue(txq->ndev, txq->ndev_idx);
+ trace_txq_flowctrl_stop(txq);
+ }
+#else /* ! CONFIG_ECRNX_FULLMAC */
+
+ if (!retry && ++txq->hwq->len == txq->hwq->len_stop) {
+ trace_hwq_flowctrl_stop(txq->hwq->id);
+ ieee80211_stop_queue(ecrnx_hw->hw, txq->hwq->id);
+ ecrnx_hw->stats.queues_stops++;
+ }
+#endif /* CONFIG_ECRNX_FULLMAC */
+
+ //ECRNX_DBG("txq status: 0x%x \n", txq->status);
+ /* add it in the hwq list if not stopped and not yet present */
+ if (!ecrnx_txq_is_stopped(txq)) {
+ ecrnx_txq_add_to_hw_list(txq);
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * ecrnx_txq_confirm_any - Process buffer confirmed by fw
+ *
+ * @ecrnx_hw: Driver main data
+ * @txq: TX Queue
+ * @hwq: HW Queue
+ * @sw_txhdr: software descriptor of the confirmed packet
+ *
+ * Process a buffer returned by the fw. It doesn't check buffer status
+ * and only does systematic counter update:
+ * - hw credit
+ * - buffer pushed to fw
+ *
+ * To be called with tx_lock hold
+ */
+void ecrnx_txq_confirm_any(struct ecrnx_hw *ecrnx_hw, struct ecrnx_txq *txq,
+ struct ecrnx_hwq *hwq, struct ecrnx_sw_txhdr *sw_txhdr)
+{
+ int user = 0;
+
+#ifdef CONFIG_ECRNX_MUMIMO_TX
+ int group_id;
+
+ user = ECRNX_MUMIMO_INFO_POS_ID(sw_txhdr->desc.host.mumimo_info);
+ group_id = ECRNX_MUMIMO_INFO_GROUP_ID(sw_txhdr->desc.host.mumimo_info);
+
+ if ((txq->idx != TXQ_INACTIVE) &&
+ (txq->pkt_pushed[user] == 1) &&
+ (txq->status & ECRNX_TXQ_STOP_MU_POS)){
+ ecrnx_txq_start(txq, ECRNX_TXQ_STOP_MU_POS);
+ ECRNX_DBG("%s-%d:ecrnx_txq_start,reaosn:0x%x \n", __func__, __LINE__, ECRNX_TXQ_STOP_MU_POS);
+ }
+
+#endif /* CONFIG_ECRNX_MUMIMO_TX */
+
+ if (txq->pkt_pushed[user])
+ txq->pkt_pushed[user]--;
+
+ hwq->credits[user]++;
+ hwq->need_processing = true;
+ ecrnx_hw->stats.cfm_balance[hwq->id]--;
+}
+
+/******************************************************************************
+ * HWQ processing
+ *****************************************************************************/
+static inline
+bool ecrnx_txq_take_mu_lock(struct ecrnx_hw *ecrnx_hw)
+{
+ bool res = false;
+#ifdef CONFIG_ECRNX_MUMIMO_TX
+ if (ecrnx_hw->mod_params->mutx)
+ res = (down_trylock(&ecrnx_hw->mu.lock) == 0);
+#endif /* CONFIG_ECRNX_MUMIMO_TX */
+ return res;
+}
+
+static inline
+void ecrnx_txq_release_mu_lock(struct ecrnx_hw *ecrnx_hw)
+{
+#ifdef CONFIG_ECRNX_MUMIMO_TX
+ up(&ecrnx_hw->mu.lock);
+#endif /* CONFIG_ECRNX_MUMIMO_TX */
+}
+
+static inline
+void ecrnx_txq_set_mu_info(struct ecrnx_hw *ecrnx_hw, struct ecrnx_txq *txq,
+ int group_id, int pos)
+{
+#ifdef CONFIG_ECRNX_MUMIMO_TX
+ trace_txq_select_mu_group(txq, group_id, pos);
+ if (group_id) {
+ txq->mumimo_info = group_id | (pos << 6);
+ ecrnx_mu_set_active_group(ecrnx_hw, group_id);
+ } else
+ txq->mumimo_info = 0;
+#endif /* CONFIG_ECRNX_MUMIMO_TX */
+}
+
+static inline
+s8 ecrnx_txq_get_credits(struct ecrnx_txq *txq)
+{
+ s8 cred = txq->credits;
+ /* if destination is in PS mode, push_limit indicates the maximum
+ number of packet that can be pushed on this txq. */
+ if (txq->push_limit && (cred > txq->push_limit)) {
+ cred = txq->push_limit;
+ }
+ return cred;
+}
+
+/**
+ * skb_queue_extract - Extract buffer from skb list
+ *
+ * @list: List of skb to extract from
+ * @head: List of skb to append to
+ * @nb_elt: Number of skb to extract
+ *
+ * extract the first @nb_elt of @list and append them to @head
+ * It is assume that:
+ * - @list contains more that @nb_elt
+ * - There is no need to take @list nor @head lock to modify them
+ */
+static inline void skb_queue_extract(struct sk_buff_head *list,
+ struct sk_buff_head *head, int nb_elt)
+{
+ int i;
+ struct sk_buff *first, *last, *ptr;
+
+ first = ptr = list->next;
+ for (i = 0; i < nb_elt; i++) {
+ ptr = ptr->next;
+ }
+ last = ptr->prev;
+
+ /* unlink nb_elt in list */
+ list->qlen -= nb_elt;
+ list->next = ptr;
+ ptr->prev = (struct sk_buff *)list;
+
+ /* append nb_elt at end of head */
+ head->qlen += nb_elt;
+ last->next = (struct sk_buff *)head;
+ head->prev->next = first;
+ first->prev = head->prev;
+ head->prev = last;
+}
+
+
+#ifdef CONFIG_MAC80211_TXQ
+/**
+ * ecrnx_txq_mac80211_dequeue - Dequeue buffer from mac80211 txq and
+ * add them to push list
+ *
+ * @ecrnx_hw: Main driver data
+ * @sk_list: List of buffer to push (initialized without lock)
+ * @txq: TXQ to dequeue buffers from
+ * @max: Max number of buffer to dequeue
+ *
+ * Dequeue buffer from mac80211 txq, prepare them for transmission and chain them
+ * to the list of buffer to push.
+ *
+ * @return true if no more buffer are queued in mac80211 txq and false otherwise.
+ */
+static bool ecrnx_txq_mac80211_dequeue(struct ecrnx_hw *ecrnx_hw,
+ struct sk_buff_head *sk_list,
+ struct ecrnx_txq *txq, int max)
+{
+ struct ieee80211_txq *mac_txq;
+ struct sk_buff *skb;
+ unsigned long mac_txq_len;
+
+ if (txq->nb_ready_mac80211 == NOT_MAC80211_TXQ)
+ return true;
+
+ mac_txq = container_of((void *)txq, struct ieee80211_txq, drv_priv);
+
+ for (; max > 0; max--) {
+ skb = ecrnx_tx_dequeue_prep(ecrnx_hw, mac_txq);
+ if (skb == NULL)
+ return true;
+
+ __skb_queue_tail(sk_list, skb);
+ }
+
+ /* re-read mac80211 txq current length.
+ It is mainly for debug purpose to trace dropped packet. There is no
+ problems to have nb_ready_mac80211 != actual mac80211 txq length */
+ ieee80211_txq_get_depth(mac_txq, &mac_txq_len, NULL);
+ if (txq->nb_ready_mac80211 > mac_txq_len)
+ trace_txq_drop(txq, txq->nb_ready_mac80211 - mac_txq_len);
+ txq->nb_ready_mac80211 = mac_txq_len;
+
+ return (txq->nb_ready_mac80211 == 0);
+}
+#endif
+
+/**
+ * ecrnx_txq_get_skb_to_push - Get list of buffer to push for one txq
+ *
+ * @ecrnx_hw: main driver data
+ * @hwq: HWQ on wich buffers will be pushed
+ * @txq: TXQ to get buffers from
+ * @user: user postion to use
+ * @sk_list_push: list to update
+ *
+ *
+ * This function will returned a list of buffer to push for one txq.
+ * It will take into account the number of credit of the HWQ for this user
+ * position and TXQ (and push_limit).
+ * This allow to get a list that can be pushed without having to test for
+ * hwq/txq status after each push
+ *
+ * If a MU group has been selected for this txq, it will also update the
+ * counter for the group
+ *
+ * @return true if txq no longer have buffer ready after the ones returned.
+ * false otherwise
+ */
+static
+bool ecrnx_txq_get_skb_to_push(struct ecrnx_hw *ecrnx_hw, struct ecrnx_hwq *hwq,
+ struct ecrnx_txq *txq, int user,
+ struct sk_buff_head *sk_list_push)
+{
+ int nb_ready = skb_queue_len(&txq->sk_list);
+ int credits = min_t(int, ecrnx_txq_get_credits(txq), hwq->credits[user]);
+ bool res = false;
+
+ __skb_queue_head_init(sk_list_push);
+
+ if (credits >= nb_ready) {
+ skb_queue_splice_init(&txq->sk_list, sk_list_push);
+#ifdef CONFIG_MAC80211_TXQ
+ res = ecrnx_txq_mac80211_dequeue(ecrnx_hw, sk_list_push, txq, credits - nb_ready);
+ credits = skb_queue_len(sk_list_push);
+#else
+ res = true;
+ credits = nb_ready;
+#endif
+ } else {
+ skb_queue_extract(&txq->sk_list, sk_list_push, credits);
+
+ /* When processing PS service period (i.e. push_limit != 0), no longer
+ process this txq if the buffers extracted will complete the SP for
+ this txq */
+ if (txq->push_limit && (credits == txq->push_limit))
+ res = true;
+ }
+
+ ecrnx_mu_set_active_sta(ecrnx_hw, ecrnx_txq_2_sta(txq), credits);
+
+ return res;
+}
+
+/**
+ * ecrnx_txq_select_user - Select User queue for a txq
+ *
+ * @ecrnx_hw: main driver data
+ * @mu_lock: true is MU lock is taken
+ * @txq: TXQ to select MU group for
+ * @hwq: HWQ for the TXQ
+ * @user: Updated with user position selected
+ *
+ * @return false if it is no possible to process this txq.
+ * true otherwise
+ *
+ * This function selects the MU group to use for a TXQ.
+ * The selection is done as follow:
+ *
+ * - return immediately for STA that don't belongs to any group and select
+ * group 0 / user 0
+ *
+ * - If MU tx is disabled (by user mutx_on, or because mu group are being
+ * updated !mu_lock), select group 0 / user 0
+ *
+ * - Use the best group selected by @ecrnx_mu_group_sta_select.
+ *
+ * Each time a group is selected (except for the first case where sta
+ * doesn't belongs to a MU group), the function checks that no buffer is
+ * pending for this txq on another user position. If this is the case stop
+ * the txq (ECRNX_TXQ_STOP_MU_POS) and return false.
+ *
+ */
+static
+bool ecrnx_txq_select_user(struct ecrnx_hw *ecrnx_hw, bool mu_lock,
+ struct ecrnx_txq *txq, struct ecrnx_hwq *hwq, int *user)
+{
+ int pos = 0;
+#ifdef CONFIG_ECRNX_MUMIMO_TX
+ int id, group_id = 0;
+ struct ecrnx_sta *sta = ecrnx_txq_2_sta(txq);
+
+ /* for sta that belong to no group return immediately */
+ if (!sta || !sta->group_info.cnt)
+ goto end;
+
+ /* If MU is disabled, need to check user */
+ if (!ecrnx_hw->mod_params->mutx_on || !mu_lock)
+ goto check_user;
+
+ /* Use the "best" group selected */
+ group_id = sta->group_info.group;
+
+ if (group_id > 0)
+ pos = ecrnx_mu_group_sta_get_pos(ecrnx_hw, sta, group_id);
+
+ check_user:
+ /* check that we can push on this user position */
+#if CONFIG_USER_MAX == 2
+ id = (pos + 1) & 0x1;
+ if (txq->pkt_pushed[id]) {
+ ecrnx_txq_stop(txq, ECRNX_TXQ_STOP_MU_POS);
+ ECRNX_DBG("%s-%d:ecrnx_txq_stop,reaosn:0x%x \n", __func__, __LINE__, ECRNX_TXQ_STOP_MU_POS);
+ return false;
+ }
+
+#else
+ for (id = 0 ; id < CONFIG_USER_MAX ; id++) {
+ if (id != pos && txq->pkt_pushed[id]) {
+ ecrnx_txq_stop(txq, ECRNX_TXQ_STOP_MU_POS);
+ ECRNX_DBG("%s-%d:ecrnx_txq_stop,reaosn:0x%x \n", __func__, __LINE__, ECRNX_TXQ_STOP_MU_POS);
+ return false;
+ }
+ }
+#endif
+
+ end:
+ ecrnx_txq_set_mu_info(ecrnx_hw, txq, group_id, pos);
+#endif /* CONFIG_ECRNX_MUMIMO_TX */
+
+ *user = pos;
+ return true;
+}
+
+
+/**
+ * ecrnx_hwq_process - Process one HW queue list
+ *
+ * @ecrnx_hw: Driver main data
+ * @hw_queue: HW queue index to process
+ *
+ * The function will iterate over all the TX queues linked in this HW queue
+ * list. For each TX queue, push as many buffers as possible in the HW queue.
+ * (NB: TX queue have at least 1 buffer, otherwise it wouldn't be in the list)
+ * - If TX queue no longer have buffer, remove it from the list and check next
+ * TX queue
+ * - If TX queue no longer have credits or has a push_limit (PS mode) and it
+ * is reached , remove it from the list and check next TX queue
+ * - If HW queue is full, update list head to start with the next TX queue on
+ * next call if current TX queue already pushed "too many" pkt in a row, and
+ * return
+ *
+ * To be called when HW queue list is modified:
+ * - when a buffer is pushed on a TX queue
+ * - when new credits are received
+ * - when a STA returns from Power Save mode or receives traffic request.
+ * - when Channel context change
+ *
+ * To be called with tx_lock hold
+ */
+#define ALL_HWQ_MASK ((1 << CONFIG_USER_MAX) - 1)
+
+void ecrnx_hwq_process(struct ecrnx_hw *ecrnx_hw, struct ecrnx_hwq *hwq)
+{
+ struct ecrnx_txq *txq, *next;
+ int user, credit_map = 0;
+ bool mu_enable;
+
+ trace_process_hw_queue(hwq);
+
+ hwq->need_processing = false;
+
+ mu_enable = ecrnx_txq_take_mu_lock(ecrnx_hw);
+ if (!mu_enable)
+ credit_map = ALL_HWQ_MASK - 1;
+
+ list_for_each_entry_safe(txq, next, &hwq->list, sched_list) {
+ struct ecrnx_txhdr *txhdr = NULL;
+ struct sk_buff_head sk_list_push;
+ struct sk_buff *skb;
+ bool txq_empty;
+
+ trace_process_txq(txq);
+
+ /* sanity check for debug */
+ BUG_ON(!(txq->status & ECRNX_TXQ_IN_HWQ_LIST));
+ BUG_ON(txq->idx == TXQ_INACTIVE);
+ BUG_ON(txq->credits <= 0);
+ BUG_ON(!ecrnx_txq_skb_ready(txq));
+
+ if (!ecrnx_txq_select_user(ecrnx_hw, mu_enable, txq, hwq, &user))
+ continue;
+
+ if (!hwq->credits[user]) {
+ credit_map |= BIT(user);
+ if (credit_map == ALL_HWQ_MASK)
+ break;
+ continue;
+ }
+
+ txq_empty = ecrnx_txq_get_skb_to_push(ecrnx_hw, hwq, txq, user,
+ &sk_list_push);
+
+ while ((skb = __skb_dequeue(&sk_list_push)) != NULL) {
+ txhdr = (struct ecrnx_txhdr *)skb->data;
+ ecrnx_tx_push(ecrnx_hw, txhdr, 0);
+ }
+
+ if (txq_empty) {
+ ecrnx_txq_del_from_hw_list(txq);
+ txq->pkt_sent = 0;
+#if defined CONFIG_ECRNX_SOFTMAC && defined CONFIG_ECRNX_AMSDUS_TX
+ if (txq->amsdu_ht_len_cap)
+ ieee80211_amsdu_ctl(ecrnx_hw->hw, txq->sta, txq->tid, NULL,
+ 0, 0, false);
+#endif
+ } else if ((hwq->credits[user] == 0) &&
+ ecrnx_txq_is_scheduled(txq)) {
+ /* txq not empty,
+ - To avoid starving need to process other txq in the list
+ - For better aggregation, need to send "as many consecutive
+ pkt as possible" for he same txq
+ ==> Add counter to trigger txq switch
+ */
+ if (txq->pkt_sent > hwq->size) {
+ txq->pkt_sent = 0;
+ list_rotate_left(&hwq->list);
+ }
+ }
+
+#ifdef CONFIG_ECRNX_FULLMAC
+ /* Unable to complete PS traffic request because of hwq credit */
+ if (txq->push_limit && txq->sta) {
+ if (txq->ps_id == LEGACY_PS_ID) {
+ /* for legacy PS abort SP and wait next ps-poll */
+ txq->sta->ps.sp_cnt[txq->ps_id] -= txq->push_limit;
+ txq->push_limit = 0;
+ }
+ /* for u-apsd need to complete the SP to send EOSP frame */
+ }
+
+ /* restart netdev queue if number of queued buffer is below threshold */
+ if (unlikely(txq->status & ECRNX_TXQ_NDEV_FLOW_CTRL) &&
+ skb_queue_len(&txq->sk_list) < ECRNX_NDEV_FLOW_CTRL_RESTART) {
+ txq->status &= ~ECRNX_TXQ_NDEV_FLOW_CTRL;
+ netif_wake_subqueue(txq->ndev, txq->ndev_idx);
+ trace_txq_flowctrl_restart(txq);
+ }
+#endif /* CONFIG_ECRNX_FULLMAC */
+
+ }
+
+#ifdef CONFIG_ECRNX_SOFTMAC
+ if (hwq->len < hwq->len_start &&
+ ieee80211_queue_stopped(ecrnx_hw->hw, hwq->id)) {
+ trace_hwq_flowctrl_start(hwq->id);
+ ieee80211_wake_queue(ecrnx_hw->hw, hwq->id);
+ }
+#endif /* CONFIG_ECRNX_SOFTMAC */
+
+ if (mu_enable)
+ ecrnx_txq_release_mu_lock(ecrnx_hw);
+}
+
+/**
+ * ecrnx_hwq_process_all - Process all HW queue list
+ *
+ * @ecrnx_hw: Driver main data
+ *
+ * Loop over all HWQ, and process them if needed
+ * To be called with tx_lock hold
+ */
+void ecrnx_hwq_process_all(struct ecrnx_hw *ecrnx_hw)
+{
+ int id;
+
+ ecrnx_mu_group_sta_select(ecrnx_hw);
+
+ for (id = ARRAY_SIZE(ecrnx_hw->hwq) - 1; id >= 0 ; id--) {
+ if (ecrnx_hw->hwq[id].need_processing) {
+ ecrnx_hwq_process(ecrnx_hw, &ecrnx_hw->hwq[id]);
+ }
+ }
+}
+
+/**
+ * ecrnx_hwq_init - Initialize all hwq structures
+ *
+ * @ecrnx_hw: Driver main data
+ *
+ */
+void ecrnx_hwq_init(struct ecrnx_hw *ecrnx_hw)
+{
+ int i, j;
+
+ for (i = 0; i < ARRAY_SIZE(ecrnx_hw->hwq); i++) {
+ struct ecrnx_hwq *hwq = &ecrnx_hw->hwq[i];
+
+ for (j = 0 ; j < CONFIG_USER_MAX; j++)
+ hwq->credits[j] = nx_txdesc_cnt[i];
+ hwq->id = i;
+ hwq->size = nx_txdesc_cnt[i];
+ INIT_LIST_HEAD(&hwq->list);
+
+#ifdef CONFIG_ECRNX_SOFTMAC
+ hwq->len = 0;
+ hwq->len_stop = nx_txdesc_cnt[i] * 2;
+ hwq->len_start = hwq->len_stop / 4;
+#endif
+ }
+}
diff --git a/drivers/net/wireless/eswin/ecrnx_txq.h b/drivers/net/wireless/eswin/ecrnx_txq.h
new file mode 100644
index 000000000000..250e1d38c3d6
--- /dev/null
+++ b/drivers/net/wireless/eswin/ecrnx_txq.h
@@ -0,0 +1,506 @@
+/**
+ ****************************************************************************************
+ *
+ * @file ecrnx_txq.h
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ****************************************************************************************
+ */
+#ifndef _ECRNX_TXQ_H_
+#define _ECRNX_TXQ_H_
+
+#include <linux/types.h>
+#include <linux/bitops.h>
+#include <linux/ieee80211.h>
+
+#ifdef CONFIG_ECRNX_SOFTMAC
+#include <net/mac80211.h>
+#include "ecrnx_baws.h"
+
+/**
+ * Softmac TXQ configuration
+ * - STA have one TXQ per TID
+ * - VIF have one TXQ per HW queue
+ *
+ * Txq mapping looks like
+ * for NX_REMOTE_STA_MAX=10 and NX_VIRT_DEV_MAX=4
+ *
+ * | TXQ | VIF | STA | TID | HWQ |
+ * |-----+-----+-------+------+-----|-
+ * | 0 | | 0 | 0 | 1 | 16 TXQ per STA
+ * | 1 | | 0 | 1 | 0 |
+ * | 2 | | 0 | 2 | 0 |
+ * | 3 | | 0 | 3 | 1 |
+ * | 4 | | 0 | 4 | 2 |
+ * | 5 | | 0 | 5 | 2 |
+ * | 6 | | 0 | 6 | 3 |
+ * | 7 | | 0 | 7 | 3 |
+ * | 8 | | 0 | 8 | 1 |
+ * | ...| | | | |
+ * | 16 | | 0 | 16 | 1 |
+ * |-----+-----+-------+------+-----|-
+ * | ... | | | | | same for all STAs
+ * |-----+-----+-------+------+-----|-
+ * | 160 | 0 | | | 0 | 5 TXQ per VIF
+ * | ... | | | | |
+ * | 164 | 0 | | | 4 |
+ * |-----+-----+-------+------+-----|-
+ * | ... | | | | | same for all VIFs
+ * |-----+-----+-------+------+-----|-
+ *
+ * NOTE: When using CONFIG_MAC80211_TXQ only one TXQ is allocated by mac80211
+ * for the VIF (associated to BE ac). To avoid too much differences with case
+ * where TXQ are allocated by the driver the "missing" VIF TXQs are allocated
+ * by the driver. Actually driver also allocates txq for BE (to avoid having
+ * modify ac parameter to access the TXQ) but this one is never used.
+ * Driver check if nb_ready_mac80211 field is equal to NOT_MAC80211_TXQ in
+ * order to distinguish non mac80211 txq.
+ * When the txq interface (.wake_tx_queue) is used only the TXQ
+ * allocated by mac80211 will be used and thus BE access category will always
+ * be used. When "VIF" frames needs to be pushed on different access category
+ * mac80211 will use the tx interface (.tx) and in this case driver will select
+ * the txq associated to the requested access category.
+ */
+#define NX_NB_TID_PER_STA IEEE80211_NUM_TIDS
+#define NX_NB_TXQ_PER_STA NX_NB_TID_PER_STA
+#define NX_NB_TXQ_PER_VIF NX_TXQ_CNT
+#define NX_NB_TXQ ((NX_NB_TXQ_PER_STA * NX_REMOTE_STA_MAX) + \
+ (NX_NB_TXQ_PER_VIF * NX_VIRT_DEV_MAX))
+
+#define NX_FIRST_VIF_TXQ_IDX (NX_REMOTE_STA_MAX * NX_NB_TXQ_PER_STA)
+
+#define NOT_MAC80211_TXQ ULONG_MAX
+
+#else /* i.e. #ifdef CONFIG_ECRNX_FULLMAC */
+/**
+ * Fullmac TXQ configuration:
+ * - STA: 1 TXQ per TID (limited to 8)
+ * 1 TXQ for bufferable MGT frames
+ * - VIF: 1 TXQ for Multi/Broadcast +
+ * 1 TXQ for MGT for unknown STAs or non-bufferable MGT frames
+ * - 1 TXQ for offchannel transmissions
+ *
+ *
+ * Txq mapping looks like
+ * for NX_REMOTE_STA_MAX=10 and NX_VIRT_DEV_MAX=4
+ *
+ * | TXQ | NDEV_ID | VIF | STA | TID | HWQ |
+ * |-----+---------+-----+-------+------+-----|-
+ * | 0 | 0 | | 0 | 0 | 1 | 9 TXQ per STA
+ * | 1 | 1 | | 0 | 1 | 0 | (8 data + 1 mgmt)
+ * | 2 | 2 | | 0 | 2 | 0 |
+ * | 3 | 3 | | 0 | 3 | 1 |
+ * | 4 | 4 | | 0 | 4 | 2 |
+ * | 5 | 5 | | 0 | 5 | 2 |
+ * | 6 | 6 | | 0 | 6 | 3 |
+ * | 7 | 7 | | 0 | 7 | 3 |
+ * | 8 | N/A | | 0 | MGMT | 3 |
+ * |-----+---------+-----+-------+------+-----|-
+ * | ... | | | | | | Same for all STAs
+ * |-----+---------+-----+-------+------+-----|-
+ * | 90 | 80 | 0 | BC/MC | 0 | 1/4 | 1 TXQ for BC/MC per VIF
+ * | ... | | | | | |
+ * | 93 | 80 | 3 | BC/MC | 0 | 1/4 |
+ * |-----+---------+-----+-------+------+-----|-
+ * | 94 | N/A | 0 | N/A | MGMT | 3 | 1 TXQ for unknown STA per VIF
+ * | ... | | | | | |
+ * | 97 | N/A | 3 | N/A | MGMT | 3 |
+ * |-----+---------+-----+-------+------+-----|-
+ * | 98 | N/A | | N/A | MGMT | 3 | 1 TXQ for offchannel frame
+ */
+#define NX_NB_TID_PER_STA 8
+#define NX_NB_TXQ_PER_STA (NX_NB_TID_PER_STA + 1)
+#define NX_NB_TXQ_PER_VIF 2
+#define NX_NB_TXQ ((NX_NB_TXQ_PER_STA * NX_REMOTE_STA_MAX) + \
+ (NX_NB_TXQ_PER_VIF * NX_VIRT_DEV_MAX) + 1)
+
+#define NX_FIRST_VIF_TXQ_IDX (NX_REMOTE_STA_MAX * NX_NB_TXQ_PER_STA)
+#define NX_FIRST_BCMC_TXQ_IDX NX_FIRST_VIF_TXQ_IDX
+#define NX_FIRST_UNK_TXQ_IDX (NX_FIRST_BCMC_TXQ_IDX + NX_VIRT_DEV_MAX)
+
+#define NX_OFF_CHAN_TXQ_IDX (NX_FIRST_VIF_TXQ_IDX + \
+ (NX_VIRT_DEV_MAX * NX_NB_TXQ_PER_VIF))
+#define NX_BCMC_TXQ_TYPE 0
+#define NX_UNK_TXQ_TYPE 1
+
+/**
+ * Each data TXQ is a netdev queue. TXQ to send MGT are not data TXQ as
+ * they did not recieved buffer from netdev interface.
+ * Need to allocate the maximum case.
+ * AP : all STAs + 1 BC/MC
+ */
+#define NX_NB_NDEV_TXQ ((NX_NB_TID_PER_STA * NX_REMOTE_STA_MAX) + 1 )
+#define NX_BCMC_TXQ_NDEV_IDX (NX_NB_TID_PER_STA * NX_REMOTE_STA_MAX)
+#define NX_STA_NDEV_IDX(tid, sta_idx) ((tid) + (sta_idx) * NX_NB_TID_PER_STA)
+#define NDEV_NO_TXQ 0xffff
+#if (NX_NB_NDEV_TXQ >= NDEV_NO_TXQ)
+#error("Need to increase struct ecrnx_txq->ndev_idx size")
+#endif
+
+/* stop netdev queue when number of queued buffers if greater than this */
+#define ECRNX_NDEV_FLOW_CTRL_STOP 200
+/* restart netdev queue when number of queued buffers is lower than this */
+#define ECRNX_NDEV_FLOW_CTRL_RESTART 100
+
+#endif /* CONFIG_ECRNX_SOFTMAC */
+
+#define TXQ_INACTIVE 0xffff
+#if (NX_NB_TXQ >= TXQ_INACTIVE)
+#error("Need to increase struct ecrnx_txq->idx size")
+#endif
+
+#define NX_TXQ_INITIAL_CREDITS 20 //4
+
+#define ECRNX_TXQ_CLEANUP_INTERVAL (10 * HZ) //10s in jiffies
+#define ECRNX_TXQ_MAX_QUEUE_JIFFIES (20 * HZ)
+/**
+ * TXQ tid sorted by decreasing priority
+ */
+extern const int nx_tid_prio[NX_NB_TID_PER_STA];
+
+/**
+ * struct ecrnx_hwq - Structure used to save information relative to
+ * an AC TX queue (aka HW queue)
+ * @list: List of TXQ, that have buffers ready for this HWQ
+ * @credits: available credit for the queue (i.e. nb of buffers that
+ * can be pushed to FW )
+ * @id Id of the HWQ among ECRNX_HWQ_....
+ * @size size of the queue
+ * @need_processing Indicate if hwq should be processed
+ * @len number of packet ready to be pushed to fw for this HW queue
+ * @len_stop threshold to stop mac80211(i.e. netdev) queues. Stop queue when
+ * driver has more than @len_stop packets ready.
+ * @len_start threshold to wake mac8011 queues. Wake queue when driver has
+ * less than @len_start packets ready.
+ */
+struct ecrnx_hwq {
+ struct list_head list;
+ u8 credits[CONFIG_USER_MAX];
+ u8 size;
+ u8 id;
+ bool need_processing;
+#ifdef CONFIG_ECRNX_SOFTMAC
+ u8 len;
+ u8 len_stop;
+ u8 len_start;
+#endif /* CONFIG_ECRNX_SOFTMAC */
+};
+
+/**
+ * enum ecrnx_push_flags - Flags of pushed buffer
+ *
+ * @ECRNX_PUSH_RETRY Pushing a buffer for retry
+ * @ECRNX_PUSH_IMMEDIATE Pushing a buffer without queuing it first
+ */
+enum ecrnx_push_flags {
+ ECRNX_PUSH_RETRY = BIT(0),
+ ECRNX_PUSH_IMMEDIATE = BIT(1),
+};
+
+/**
+ * enum ecrnx_txq_flags - TXQ status flag
+ *
+ * @ECRNX_TXQ_IN_HWQ_LIST: The queue is scheduled for transmission
+ * @ECRNX_TXQ_STOP_FULL: No more credits for the queue
+ * @ECRNX_TXQ_STOP_CSA: CSA is in progress
+ * @ECRNX_TXQ_STOP_STA_PS: Destiniation sta is currently in power save mode
+ * @ECRNX_TXQ_STOP_VIF_PS: Vif owning this queue is currently in power save mode
+ * @ECRNX_TXQ_STOP_CHAN: Channel of this queue is not the current active channel
+ * @ECRNX_TXQ_STOP_MU_POS: TXQ is stopped waiting for all the buffers pushed to
+ * fw to be confirmed
+ * @ECRNX_TXQ_STOP: All possible reason to have a txq stopped
+ * @ECRNX_TXQ_NDEV_FLOW_CTRL: associated netdev queue is currently stopped.
+ * Note: when a TXQ is flowctrl it is NOT stopped
+ */
+enum ecrnx_txq_flags {
+ ECRNX_TXQ_IN_HWQ_LIST = BIT(0),
+ ECRNX_TXQ_STOP_FULL = BIT(1),
+ ECRNX_TXQ_STOP_CSA = BIT(2),
+ ECRNX_TXQ_STOP_STA_PS = BIT(3),
+ ECRNX_TXQ_STOP_VIF_PS = BIT(4),
+ ECRNX_TXQ_STOP_CHAN = BIT(5),
+ ECRNX_TXQ_STOP_MU_POS = BIT(6),
+ ECRNX_TXQ_STOP = (ECRNX_TXQ_STOP_FULL | ECRNX_TXQ_STOP_CSA |
+ ECRNX_TXQ_STOP_STA_PS | ECRNX_TXQ_STOP_VIF_PS |
+ ECRNX_TXQ_STOP_CHAN) ,
+ ECRNX_TXQ_NDEV_FLOW_CTRL = BIT(7),
+};
+
+
+/**
+ * struct ecrnx_txq - Structure used to save information relative to
+ * a RA/TID TX queue
+ *
+ * @idx: Unique txq idx. Set to TXQ_INACTIVE if txq is not used.
+ * @status: bitfield of @ecrnx_txq_flags.
+ * @credits: available credit for the queue (i.e. nb of buffers that
+ * can be pushed to FW).
+ * @pkt_sent: number of consecutive pkt sent without leaving HW queue list
+ * @pkt_pushed: number of pkt currently pending for transmission confirmation
+ * @sched_list: list node for HW queue schedule list (ecrnx_hwq.list)
+ * @sk_list: list of buffers to push to fw
+ * @last_retry_skb: pointer on the last skb in @sk_list that is a retry.
+ * (retry skb are stored at the beginning of the list)
+ * NULL if no retry skb is queued in @sk_list
+ * @nb_retry: Number of retry packet queued.
+ * @hwq: Pointer on the associated HW queue.
+ * @push_limit: number of packet to push before removing the txq from hwq list.
+ * (we always have push_limit < skb_queue_len(sk_list))
+ * @tid: TID
+ *
+ * SOFTMAC specific:
+ * @baw: Block Ack window information
+ * @amsdu_anchor: pointer to ecrnx_sw_txhdr of the first subframe of the A-MSDU.
+ * NULL if no A-MSDU frame is in construction
+ * @amsdu_ht_len_cap:
+ * @amsdu_vht_len_cap:
+ * @nb_ready_mac80211: Number of buffer ready in mac80211 txq
+ *
+ * FULLMAC specific
+ * @ps_id: Index to use for Power save mode (LEGACY or UAPSD)
+ * @ndev_idx: txq idx from netdev point of view (0xFF for non netdev queue)
+ * @ndev: pointer to ndev of the corresponding vif
+ * @amsdu: pointer to ecrnx_sw_txhdr of the first subframe of the A-MSDU.
+ * NULL if no A-MSDU frame is in construction
+ * @amsdu_len: Maximum size allowed for an A-MSDU. 0 means A-MSDU not allowed
+ */
+struct ecrnx_txq {
+ u16 idx;
+ u8 status;
+ s8 credits;
+ u8 pkt_sent;
+ u8 pkt_pushed[CONFIG_USER_MAX];
+ struct list_head sched_list;
+ struct sk_buff_head sk_list;
+ struct sk_buff *last_retry_skb;
+ struct ecrnx_hwq *hwq;
+ int nb_retry;
+ u8 push_limit;
+ u8 tid;
+#ifdef CONFIG_MAC80211_TXQ
+ unsigned long nb_ready_mac80211;
+#endif
+#ifdef CONFIG_ECRNX_SOFTMAC
+ struct ecrnx_baw baw;
+ struct ieee80211_sta *sta;
+#ifdef CONFIG_ECRNX_AMSDUS_TX
+ struct ecrnx_sw_txhdr *amsdu_anchor;
+ u16 amsdu_ht_len_cap;
+ u16 amsdu_vht_len_cap;
+#endif /* CONFIG_ECRNX_AMSDUS_TX */
+#else /* ! CONFIG_ECRNX_SOFTMAC */
+ struct ecrnx_sta *sta;
+ u8 ps_id;
+ u16 ndev_idx;
+ struct net_device *ndev;
+#ifdef CONFIG_ECRNX_AMSDUS_TX
+ struct ecrnx_sw_txhdr *amsdu;
+ u16 amsdu_len;
+#endif /* CONFIG_ECRNX_AMSDUS_TX */
+#endif /* CONFIG_ECRNX_SOFTMAC */
+#ifdef CONFIG_ECRNX_MUMIMO_TX
+ u8 mumimo_info;
+#endif
+};
+
+struct ecrnx_sta;
+struct ecrnx_vif;
+struct ecrnx_hw;
+struct ecrnx_sw_txhdr;
+
+#ifdef CONFIG_ECRNX_MUMIMO_TX
+#define ECRNX_TXQ_GROUP_ID(txq) ((txq)->mumimo_info & 0x3f)
+#define ECRNX_TXQ_POS_ID(txq) (((txq)->mumimo_info >> 6) & 0x3)
+#else
+#define ECRNX_TXQ_GROUP_ID(txq) 0
+#define ECRNX_TXQ_POS_ID(txq) 0
+#endif /* CONFIG_ECRNX_MUMIMO_TX */
+
+static inline bool ecrnx_txq_is_stopped(struct ecrnx_txq *txq)
+{
+ return (txq->status & ECRNX_TXQ_STOP);
+}
+
+static inline bool ecrnx_txq_is_full(struct ecrnx_txq *txq)
+{
+ return (txq->status & ECRNX_TXQ_STOP_FULL);
+}
+
+static inline bool ecrnx_txq_is_scheduled(struct ecrnx_txq *txq)
+{
+ return (txq->status & ECRNX_TXQ_IN_HWQ_LIST);
+}
+
+/**
+ * ecrnx_txq_is_ready_for_push - Check if a TXQ is ready for push
+ *
+ * @txq: txq pointer
+ *
+ * if
+ * - txq is not stopped
+ * - and hwq has credits
+ * - and there is no buffer queued
+ * then a buffer can be immediately pushed without having to queue it first
+ * @return: true if the 3 conditions are met and false otherwise.
+ */
+static inline bool ecrnx_txq_is_ready_for_push(struct ecrnx_txq *txq)
+{
+ return (!ecrnx_txq_is_stopped(txq) &&
+ txq->hwq->credits[ECRNX_TXQ_POS_ID(txq)] > 0 &&
+ skb_queue_empty(&txq->sk_list));
+}
+
+/**
+ * foreach_sta_txq - Macro to iterate over all TXQ of a STA in increasing
+ * TID order
+ *
+ * @sta: pointer to ecrnx_sta
+ * @txq: pointer to ecrnx_txq updated with the next TXQ at each iteration
+ * @tid: int updated with the TXQ tid at each iteration
+ * @ecrnx_hw: main driver data
+ */
+#ifdef CONFIG_MAC80211_TXQ
+#define foreach_sta_txq(sta, txq, tid, ecrnx_hw) \
+ for (tid = 0, txq = ecrnx_txq_sta_get(sta, 0); \
+ tid < NX_NB_TXQ_PER_STA; \
+ tid++, txq = ecrnx_txq_sta_get(sta, tid))
+
+#elif defined(CONFIG_ECRNX_SOFTMAC)
+#define foreach_sta_txq(sta, txq, tid, ecrnx_hw) \
+ for (tid = 0, txq = &sta->txqs[0]; \
+ tid < NX_NB_TXQ_PER_STA; \
+ tid++, txq++)
+
+#else /* CONFIG_ECRNX_FULLMAC */
+#define foreach_sta_txq(sta, txq, tid, ecrnx_hw) \
+ for (tid = 0, txq = ecrnx_txq_sta_get(sta, 0, ecrnx_hw); \
+ tid < (is_multicast_sta(sta->sta_idx) ? 1 : NX_NB_TXQ_PER_STA); \
+ tid++, txq++)
+
+#endif
+
+/**
+ * foreach_sta_txq_prio - Macro to iterate over all TXQ of a STA in
+ * decreasing priority order
+ *
+ * @sta: pointer to ecrnx_sta
+ * @txq: pointer to ecrnx_txq updated with the next TXQ at each iteration
+ * @tid: int updated with the TXQ tid at each iteration
+ * @i: int updated with ieration count
+ * @ecrnx_hw: main driver data
+ *
+ * Note: For fullmac txq for mgmt frame is skipped
+ */
+#ifdef CONFIG_ECRNX_SOFTMAC
+#define foreach_sta_txq_prio(sta, txq, tid, i, ecrnx_hw) \
+ for (i = 0, tid = nx_tid_prio[0], txq = ecrnx_txq_sta_get(sta, tid); \
+ i < NX_NB_TID_PER_STA; \
+ i++, tid = nx_tid_prio[i], txq = ecrnx_txq_sta_get(sta, tid))
+#else /* CONFIG_ECRNX_FULLMAC */
+#define foreach_sta_txq_prio(sta, txq, tid, i, ecrnx_hw) \
+ for (i = 0, tid = nx_tid_prio[0], txq = ecrnx_txq_sta_get(sta, tid, ecrnx_hw); \
+ i < NX_NB_TID_PER_STA; \
+ i++, tid = nx_tid_prio[i], txq = ecrnx_txq_sta_get(sta, tid, ecrnx_hw))
+#endif
+
+/**
+ * foreach_vif_txq - Macro to iterate over all TXQ of a VIF (in AC order)
+ *
+ * @vif: pointer to ecrnx_vif
+ * @txq: pointer to ecrnx_txq updated with the next TXQ at each iteration
+ * @ac: int updated with the TXQ ac at each iteration
+ */
+#ifdef CONFIG_MAC80211_TXQ
+#define foreach_vif_txq(vif, txq, ac) \
+ for (ac = ECRNX_HWQ_BK, txq = ecrnx_txq_vif_get(vif, ac); \
+ ac < NX_NB_TXQ_PER_VIF; \
+ ac++, txq = ecrnx_txq_vif_get(vif, ac))
+
+#else
+#define foreach_vif_txq(vif, txq, ac) \
+ for (ac = ECRNX_HWQ_BK, txq = &vif->txqs[0]; \
+ ac < NX_NB_TXQ_PER_VIF; \
+ ac++, txq++)
+#endif
+
+#ifdef CONFIG_ECRNX_SOFTMAC
+struct ecrnx_txq *ecrnx_txq_sta_get(struct ecrnx_sta *sta, u8 tid);
+struct ecrnx_txq *ecrnx_txq_vif_get(struct ecrnx_vif *vif, u8 ac);
+#else
+struct ecrnx_txq *ecrnx_txq_sta_get(struct ecrnx_sta *sta, u8 tid,
+ struct ecrnx_hw * ecrnx_hw);
+struct ecrnx_txq *ecrnx_txq_vif_get(struct ecrnx_vif *vif, u8 type);
+#endif /* CONFIG_ECRNX_SOFTMAC */
+
+/**
+ * ecrnx_txq_vif_get_status - return status bits related to the vif
+ *
+ * @ecrnx_vif: Pointer to vif structure
+ */
+static inline u8 ecrnx_txq_vif_get_status(struct ecrnx_vif *ecrnx_vif)
+{
+ struct ecrnx_txq *txq = ecrnx_txq_vif_get(ecrnx_vif, 0);
+ return (txq->status & (ECRNX_TXQ_STOP_CHAN | ECRNX_TXQ_STOP_VIF_PS));
+}
+
+void ecrnx_txq_vif_init(struct ecrnx_hw * ecrnx_hw, struct ecrnx_vif *vif,
+ u8 status);
+void ecrnx_txq_vif_deinit(struct ecrnx_hw * ecrnx_hw, struct ecrnx_vif *vif);
+void ecrnx_txq_sta_init(struct ecrnx_hw * ecrnx_hw, struct ecrnx_sta *ecrnx_sta,
+ u8 status);
+void ecrnx_txq_sta_deinit(struct ecrnx_hw * ecrnx_hw, struct ecrnx_sta *ecrnx_sta);
+#ifdef CONFIG_ECRNX_FULLMAC
+void ecrnx_txq_unk_vif_init(struct ecrnx_vif *ecrnx_vif);
+void ecrnx_txq_unk_vif_deinit(struct ecrnx_vif *vif);
+void ecrnx_txq_offchan_init(struct ecrnx_vif *ecrnx_vif);
+void ecrnx_txq_offchan_deinit(struct ecrnx_vif *ecrnx_vif);
+void ecrnx_txq_tdls_vif_init(struct ecrnx_vif *ecrnx_vif);
+void ecrnx_txq_tdls_vif_deinit(struct ecrnx_vif *vif);
+void ecrnx_txq_tdls_sta_start(struct ecrnx_vif *ecrnx_vif, u16 reason,
+ struct ecrnx_hw *ecrnx_hw);
+void ecrnx_txq_tdls_sta_stop(struct ecrnx_vif *ecrnx_vif, u16 reason,
+ struct ecrnx_hw *ecrnx_hw);
+void ecrnx_txq_prepare(struct ecrnx_hw *ecrnx_hw);
+#endif
+
+
+void ecrnx_txq_add_to_hw_list(struct ecrnx_txq *txq);
+void ecrnx_txq_del_from_hw_list(struct ecrnx_txq *txq);
+void ecrnx_txq_stop(struct ecrnx_txq *txq, u16 reason);
+void ecrnx_txq_start(struct ecrnx_txq *txq, u16 reason);
+void ecrnx_txq_vif_start(struct ecrnx_vif *vif, u16 reason,
+ struct ecrnx_hw *ecrnx_hw);
+void ecrnx_txq_vif_stop(struct ecrnx_vif *vif, u16 reason,
+ struct ecrnx_hw *ecrnx_hw);
+
+#ifdef CONFIG_ECRNX_SOFTMAC
+void ecrnx_txq_sta_start(struct ecrnx_sta *sta, u16 reason);
+void ecrnx_txq_sta_stop(struct ecrnx_sta *sta, u16 reason);
+void ecrnx_txq_tdls_sta_start(struct ecrnx_sta *ecrnx_sta, u16 reason,
+ struct ecrnx_hw *ecrnx_hw);
+void ecrnx_txq_tdls_sta_stop(struct ecrnx_sta *ecrnx_sta, u16 reason,
+ struct ecrnx_hw *ecrnx_hw);
+#else
+void ecrnx_txq_sta_start(struct ecrnx_sta *sta, u16 reason,
+ struct ecrnx_hw *ecrnx_hw);
+void ecrnx_txq_sta_stop(struct ecrnx_sta *sta, u16 reason,
+ struct ecrnx_hw *ecrnx_hw);
+void ecrnx_txq_offchan_start(struct ecrnx_hw *ecrnx_hw);
+void ecrnx_txq_sta_switch_vif(struct ecrnx_sta *sta, struct ecrnx_vif *old_vif,
+ struct ecrnx_vif *new_vif);
+
+#endif /* CONFIG_ECRNX_SOFTMAC */
+
+int ecrnx_txq_queue_skb(struct sk_buff *skb, struct ecrnx_txq *txq,
+ struct ecrnx_hw *ecrnx_hw, bool retry,
+ struct sk_buff *skb_prev);
+void ecrnx_txq_confirm_any(struct ecrnx_hw *ecrnx_hw, struct ecrnx_txq *txq,
+ struct ecrnx_hwq *hwq, struct ecrnx_sw_txhdr *sw_txhdr);
+void ecrnx_txq_drop_skb(struct ecrnx_txq *txq, struct sk_buff *skb, struct ecrnx_hw *ecrnx_hw, bool retry_packet);
+
+void ecrnx_hwq_init(struct ecrnx_hw *ecrnx_hw);
+void ecrnx_hwq_process(struct ecrnx_hw *ecrnx_hw, struct ecrnx_hwq *hwq);
+void ecrnx_hwq_process_all(struct ecrnx_hw *ecrnx_hw);
+
+#endif /* _ECRNX_TXQ_H_ */
diff --git a/drivers/net/wireless/eswin/ecrnx_utils.c b/drivers/net/wireless/eswin/ecrnx_utils.c
new file mode 100644
index 000000000000..e60785c66f4d
--- /dev/null
+++ b/drivers/net/wireless/eswin/ecrnx_utils.c
@@ -0,0 +1,1333 @@
+/**
+ * ecrnx_utils.c
+ *
+ * IPC utility function definitions
+ *
+ * Copyright (C) ESWIN 2015-2020
+ */
+#include "ecrnx_utils.h"
+#include "ecrnx_defs.h"
+#include "ecrnx_rx.h"
+#include "ecrnx_tx.h"
+#include "ecrnx_msg_rx.h"
+#include "ecrnx_debugfs.h"
+#include "ecrnx_prof.h"
+#include "ipc_host.h"
+
+#ifdef CONFIG_ECRNX_ESWIN_SDIO
+#include "eswin_utils.h"
+#include "ecrnx_sdio.h"
+#include "sdio.h"
+#elif defined(CONFIG_ECRNX_ESWIN_USB)
+#include "eswin_utils.h"
+#include "ecrnx_usb.h"
+#include "usb.h"
+#endif
+
+
+/**
+ * ecrnx_ipc_elem_pool_allocs() - Allocate and push to fw a pool of buffer.
+ *
+ * @ecrnx_hw: Main driver structure
+ * @pool: Pool to allocate
+ * @nb: Size of the pool to allocate
+ * @elem_size: SIze of one pool element
+ * @pool_name: Name of the pool
+ * @push: Function to push one pool element to fw
+ *
+ * This function will allocate an array to store the list of element addresses,
+ * a dma pool and @nb element in the dma pool.
+ * Each element is set with '0' and then push to fw using the @push function.
+ * It assumes that pointer inside @ipc parameter are set to NULL at start.
+ *
+ * Return: 0 on success and <0 upon error. If error is returned any allocated
+ * memory is NOT freed and ecrnx_ipc_elem_pool_deallocs() must be called.
+ */
+#ifndef CONFIG_ECRNX_ESWIN
+static int ecrnx_ipc_elem_pool_allocs(struct ecrnx_hw *ecrnx_hw,
+ struct ecrnx_ipc_elem_pool *pool,
+ int nb, size_t elem_size, char *pool_name,
+ int (*push)(struct ipc_host_env_tag *,
+ void *, uint32_t))
+{
+ struct ecrnx_ipc_elem *buf;
+ int i;
+
+ pool->nb = 0;
+
+ /* allocate buf array */
+ pool->buf = kmalloc(nb * sizeof(struct ecrnx_ipc_elem), GFP_KERNEL);
+ if (!pool->buf) {
+ dev_err(ecrnx_hw->dev, "Allocation of buffer array for %s failed\n",
+ pool_name);
+ return -ENOMEM;
+ }
+
+ /* allocate dma pool */
+ pool->pool = dma_pool_create(pool_name, ecrnx_hw->dev, elem_size,
+ cache_line_size(), 0);
+ if (!pool->pool) {
+ dev_err(ecrnx_hw->dev, "Allocation of dma pool %s failed\n",
+ pool_name);
+ return -ENOMEM;
+ }
+
+ for (i = 0, buf = pool->buf; i < nb; buf++, i++) {
+
+ /* allocate an elem */
+ buf->addr = dma_pool_alloc(pool->pool, GFP_KERNEL, &buf->dma_addr);
+ if (!buf->addr) {
+ dev_err(ecrnx_hw->dev, "Allocation of block %d/%d in %s failed\n",
+ (i + 1), nb, pool_name);
+ return -ENOMEM;
+ }
+ pool->nb++;
+
+ /* reset the element */
+ memset(buf->addr, 0, elem_size);
+
+ /* push it to FW */
+ push(ecrnx_hw->ipc_env, buf, (uint32_t)buf->dma_addr);
+ }
+
+ return 0;
+}
+#endif
+/**
+ * ecrnx_ipc_elem_pool_deallocs() - Free all memory allocated for a pool
+ *
+ * @pool: Pool to free
+ *
+ * Must be call once after ecrnx_ipc_elem_pool_allocs(), even if it returned
+ * an error
+ */
+#ifndef CONFIG_ECRNX_ESWIN
+static void ecrnx_ipc_elem_pool_deallocs(struct ecrnx_ipc_elem_pool *pool)
+{
+ struct ecrnx_ipc_elem *buf;
+ int i;
+
+ for (i = 0, buf = pool->buf; i < pool->nb ; buf++, i++) {
+ dma_pool_free(pool->pool, buf->addr, buf->dma_addr);
+ }
+ pool->nb = 0;
+
+ if (pool->pool)
+ dma_pool_destroy(pool->pool);
+ pool->pool = NULL;
+
+ if (pool->buf)
+ kfree(pool->buf);
+ pool->buf = NULL;
+}
+#endif
+/**
+ * ecrnx_ipc_elem_var_allocs - Alloc a single ipc buffer and push it to fw
+ *
+ * @ecrnx_hw: Main driver structure
+ * @elem: Element to allocate
+ * @elem_size: Size of the element to allcoate
+ * @dir: DMA direction
+ * @buf: If not NULL, used this buffer instead of allocating a new one. It must
+ * be @elem_size long and be allocated by kmalloc as kfree will be called.
+ * @init: Pointer to initial data to write in buffer before DMA sync. Needed
+ * only if direction is DMA_TO_DEVICE. If set it is assume that its size is
+ * @elem_size.
+ * @push: Function to push the element to fw. May be set to NULL.
+ *
+ * It allocates a buffer (or use the one provided with @buf), initializes it if
+ * @init is set, map buffer for DMA transfer, initializes @elem and push buffer
+ * to FW if @push is seet.
+ *
+ * Return: 0 on success and <0 upon error. If error is returned any allocated
+ * memory has been freed (including @buf if set).
+ */
+int ecrnx_ipc_elem_var_allocs(struct ecrnx_hw *ecrnx_hw,
+ struct ecrnx_ipc_elem_var *elem, size_t elem_size,
+ enum dma_data_direction dir,
+ void *buf, const void *init,
+ void (*push)(struct ipc_host_env_tag *, uint32_t))
+{
+ if (buf) {
+ elem->addr = buf;
+ } else {
+ elem->addr = kmalloc(elem_size, GFP_KERNEL);
+ if (!elem->addr) {
+ dev_err(ecrnx_hw->dev, "Allocation of ipc buffer failed\n");
+ return -ENOMEM;
+ }
+ }
+ elem->size = elem_size;
+
+ if ((dir == DMA_TO_DEVICE) && init) {
+ memcpy(elem->addr, init, elem_size);
+ }
+
+#ifdef CONFIG_ECRNX_ESWIN
+ elem->dma_addr = (ptr_addr)elem->addr;
+#else
+ elem->dma_addr = dma_map_single(ecrnx_hw->dev, elem->addr, elem_size, dir);
+ if (dma_mapping_error(ecrnx_hw->dev, elem->dma_addr)) {
+ dev_err(ecrnx_hw->dev, "DMA mapping failed\n");
+ kfree(elem->addr);
+ elem->addr = NULL;
+ return -EIO;
+ }
+
+ if (push)
+ push(ecrnx_hw->ipc_env, elem->dma_addr);
+#endif
+ return 0;
+}
+
+/**
+ * ecrnx_ipc_elem_var_deallocs() - Free memory allocated for a single ipc buffer
+ *
+ * @ecrnx_hw: Main driver structure
+ * @elem: Element to free
+ */
+void ecrnx_ipc_elem_var_deallocs(struct ecrnx_hw *ecrnx_hw,
+ struct ecrnx_ipc_elem_var *elem)
+{
+ if (!elem->addr)
+ return;
+#ifndef CONFIG_ECRNX_ESWIN
+ dma_unmap_single(ecrnx_hw->dev, elem->dma_addr, elem->size, DMA_TO_DEVICE);
+#endif
+ kfree(elem->addr);
+ elem->addr = NULL;
+}
+
+/**
+ * ecrnx_ipc_skb_elem_allocs() - Allocate and push a skb buffer for the FW
+ *
+ * @ecrnx_hw: Main driver data
+ * @elem: Pointer to the skb elem that will contain the address of the buffer
+ */
+int ecrnx_ipc_skb_elem_allocs(struct ecrnx_hw *ecrnx_hw,
+ struct ecrnx_ipc_skb_elem *elem, size_t skb_size,
+ enum dma_data_direction dir,
+ int (*push)(struct ipc_host_env_tag *,
+ void *, uint32_t))
+{
+ elem->skb = dev_alloc_skb(skb_size);
+ if (unlikely(!elem->skb)) {
+ dev_err(ecrnx_hw->dev, "Allocation of ipc skb failed\n");
+ return -ENOMEM;
+ }
+
+ elem->dma_addr = dma_map_single(ecrnx_hw->dev, elem->skb->data, skb_size, dir);
+ if (unlikely(dma_mapping_error(ecrnx_hw->dev, elem->dma_addr))) {
+ dev_err(ecrnx_hw->dev, "DMA mapping failed\n");
+ dev_kfree_skb(elem->skb);
+ elem->skb = NULL;
+ return -EIO;
+ }
+
+ if (push){
+ push(ecrnx_hw->ipc_env, elem, elem->dma_addr);
+ }
+ return 0;
+}
+
+/**
+ * ecrnx_ipc_skb_elem_deallocs() - Free a skb buffer allocated for the FW
+ *
+ * @ecrnx_hw: Main driver data
+ * @elem: Pointer to the skb elem that contains the address of the buffer
+ * @skb_size: size of the skb buffer data
+ * @dir: DMA direction
+ */
+#ifndef CONFIG_ECRNX_ESWIN
+static void ecrnx_ipc_skb_elem_deallocs(struct ecrnx_hw *ecrnx_hw,
+ struct ecrnx_ipc_skb_elem *elem,
+ size_t skb_size, enum dma_data_direction dir)
+{
+ if (elem->skb) {
+ dma_unmap_single(ecrnx_hw->dev, elem->dma_addr, skb_size, dir);
+ dev_kfree_skb(elem->skb);
+ elem->skb = NULL;
+ }
+}
+#endif
+/**
+ * ecrnx_ipc_unsup_rx_vec_elem_allocs() - Allocate and push an unsupported
+ * RX vector buffer for the FW
+ *
+ * @ecrnx_hw: Main driver data
+ * @elem: Pointer to the skb elem that will contain the address of the buffer
+ */
+#ifndef CONFIG_ECRNX_ESWIN
+int ecrnx_ipc_unsup_rx_vec_elem_allocs(struct ecrnx_hw *ecrnx_hw,
+ struct ecrnx_ipc_skb_elem *elem)
+{
+ struct rx_vector_desc *rxdesc;
+
+ if (ecrnx_ipc_skb_elem_allocs(ecrnx_hw, elem,
+ ecrnx_hw->ipc_env->unsuprxvec_bufsz, DMA_FROM_DEVICE, NULL))
+ return -ENOMEM;
+
+ rxdesc = (struct rx_vector_desc *) elem->skb->data;
+ rxdesc->pattern = 0;
+ dma_sync_single_for_device(ecrnx_hw->dev,
+ elem->dma_addr + offsetof(struct rx_vector_desc, pattern),
+ sizeof(rxdesc->pattern), DMA_BIDIRECTIONAL);
+
+ ipc_host_unsup_rx_vec_buf_push(ecrnx_hw->ipc_env, elem, (u32) elem->dma_addr);
+
+ return 0;
+}
+#endif
+
+/**
+ * ecrnx_ipc_rxbuf_elems_deallocs() - Free all unsupported rx vector buffer
+ * allocated for the FW
+ *
+ * @ecrnx_hw: Main driver data
+ */
+#ifndef CONFIG_ECRNX_ESWIN
+static void ecrnx_ipc_unsup_rx_vec_elems_deallocs(struct ecrnx_hw *ecrnx_hw)
+{
+ struct ecrnx_ipc_skb_elem *elem;
+ int i, nb = ecrnx_hw->ipc_env->unsuprxvec_bufnb;
+
+ if (!ecrnx_hw->e2aunsuprxvec_elems)
+ return;
+
+ for (i = 0, elem = ecrnx_hw->e2aunsuprxvec_elems; i < nb; i++, elem++) {
+ ecrnx_ipc_skb_elem_deallocs(ecrnx_hw, elem, ecrnx_hw->ipc_env->unsuprxvec_bufsz, DMA_FROM_DEVICE);
+ }
+
+ kfree(ecrnx_hw->e2aunsuprxvec_elems);
+ ecrnx_hw->e2aunsuprxvec_elems = NULL;
+}
+#endif
+
+/**
+* ecrnx_ipc_unsup_rx_vec_elems_allocs() - Allocate and push all unsupported RX
+* vector buffer for the FW
+*
+* @ecrnx_hw: Main driver data
+*/
+#ifndef CONFIG_ECRNX_ESWIN
+static int ecrnx_ipc_unsup_rx_vec_elems_allocs(struct ecrnx_hw *ecrnx_hw)
+{
+ struct ecrnx_ipc_skb_elem *elem;
+ int i, nb = ecrnx_hw->ipc_env->unsuprxvec_bufnb;
+
+ ecrnx_hw->e2aunsuprxvec_elems = kzalloc(nb * sizeof(struct ecrnx_ipc_skb_elem),
+ GFP_KERNEL);
+ if (!ecrnx_hw->e2aunsuprxvec_elems) {
+ dev_err(ecrnx_hw->dev, "Failed to allocate unsuprxvec_elems\n");
+ return -ENOMEM;
+ }
+
+ for (i = 0, elem = ecrnx_hw->e2aunsuprxvec_elems; i < nb; i++, elem++)
+ {
+ if (ecrnx_ipc_unsup_rx_vec_elem_allocs(ecrnx_hw, elem)) {
+ dev_err(ecrnx_hw->dev, "Failed to allocate unsuprxvec buf %d/%d\n",
+ i + 1, nb);
+ return -ENOMEM;
+ }
+ }
+ return 0;
+}
+#endif
+
+#ifdef CONFIG_ECRNX_SOFTMAC
+/**
+ * ecrnx_ipc_rxbuf_elem_allocs() - Allocate and push a rx buffer for the FW
+ *
+ * @ecrnx_hw: Main driver data
+ * @elem: Pointer to the skb elem that will contain the address of the buffer
+ */
+#ifndef CONFIG_ECRNX_ESWIN
+int ecrnx_ipc_rxbuf_elem_allocs(struct ecrnx_hw *ecrnx_hw,
+ struct ecrnx_ipc_skb_elem *elem)
+{
+ struct hw_rxhdr *hw_rxhdr;
+
+ if (ecrnx_ipc_skb_elem_allocs(ecrnx_hw, elem,
+ ecrnx_hw->ipc_env->rx_bufsz, DMA_FROM_DEVICE, NULL))
+ return -ENOMEM;
+
+ hw_rxhdr = (struct hw_rxhdr *) elem->skb->data;
+ hw_rxhdr->pattern = 0;
+ dma_sync_single_for_device(ecrnx_hw->dev,
+ elem->dma_addr + offsetof(struct hw_rxhdr, pattern),
+ sizeof(hw_rxhdr->pattern), DMA_BIDIRECTIONAL);
+
+ ipc_host_rxbuf_push(ecrnx_hw->ipc_env, elem, (u32) elem->dma_addr);
+
+ return 0;
+}
+#endif
+
+/**
+ * ecrnx_ipc_rxbuf_elem_repush() - Reset and repush an already allocated RX buffer
+ *
+ * @ecrnx_hw: Main driver data
+ * @elem: Pointer to the skb elem that contains the address of the buffer
+ */
+#ifndef CONFIG_ECRNX_ESWIN
+void ecrnx_ipc_rxbuf_elem_repush(struct ecrnx_hw *ecrnx_hw,
+ struct ecrnx_ipc_skb_elem *elem)
+{
+ struct sk_buff *skb = elem->skb;
+ int pattern_offset = sizeof(struct hw_rxhdr);
+
+ ((struct hw_rxhdr *)skb->data)->pattern = 0;
+ dma_sync_single_for_device(ecrnx_hw->dev, elem->dma_addr,
+ pattern_offset, DMA_BIDIRECTIONAL);
+ ipc_host_rxbuf_push(ecrnx_hw->ipc_env, elem, (u32)elem->dma_addr);
+}
+#endif
+
+/**
+ * ecrnx_ipc_rxbuf_elems_allocs() - Allocate and push all RX buffer for the FW
+ *
+ * @ecrnx_hw: Main driver data
+ */
+#ifndef CONFIG_ECRNX_ESWIN
+static int ecrnx_ipc_rxbuf_elems_allocs(struct ecrnx_hw *ecrnx_hw)
+{
+ struct ecrnx_ipc_skb_elem *elem;
+ int i, nb = ecrnx_hw->ipc_env->rx_bufnb;
+
+ ecrnx_hw->rxbuf_elems = kzalloc(nb * sizeof(struct ecrnx_ipc_skb_elem),
+ GFP_KERNEL);
+ if (!ecrnx_hw->rxbuf_elems) {
+ dev_err(ecrnx_hw->dev, "Failed to allocate rx_elems\n");
+ return -ENOMEM;
+ }
+
+ for (i = 0, elem = ecrnx_hw->rxbuf_elems; i < nb; i++, elem++) {
+ if (ecrnx_ipc_rxbuf_elem_allocs(ecrnx_hw, elem)) {
+ dev_err(ecrnx_hw->dev, "Failed to allocate rx buf %d/%d\n",
+ i + 1, nb);
+ return -ENOMEM;
+ }
+ }
+
+ return 0;
+}
+#endif
+
+/**
+ * ecrnx_ipc_rxbuf_elems_deallocs() - Free all RX buffer allocated for the FW
+ *
+ * @ecrnx_hw: Main driver data
+ */
+#ifndef CONFIG_ECRNX_ESWIN
+static void ecrnx_ipc_rxbuf_elems_deallocs(struct ecrnx_hw *ecrnx_hw)
+{
+ struct ecrnx_ipc_skb_elem *elem;
+ int i, nb = ecrnx_hw->ipc_env->rx_bufnb;
+
+ if (!ecrnx_hw->rxbuf_elems)
+ return;
+
+ for (i = 0, elem = ecrnx_hw->rxbuf_elems; i < nb; i++, elem++) {
+ ecrnx_ipc_skb_elem_deallocs(ecrnx_hw, elem, ecrnx_hw->ipc_env->rx_bufsz, DMA_FROM_DEVICE);
+ }
+
+ kfree(ecrnx_hw->rxbuf_elems);
+ ecrnx_hw->rxbuf_elems = NULL;
+}
+#endif
+
+#else /* ! CONFIG_ECRNX_SOFTMAC */
+
+/**
+ * ecrnx_ipc_rxdesc_elem_repush() - Repush a rxdesc to FW
+ *
+ * @ecrnx_hw: Main driver data
+ * @elem: Rx desc to repush
+ *
+ * Once rx buffer has been received, the rxdesc used by FW to upload this
+ * buffer can be re-used for another rx buffer.
+ */
+#ifndef CONFIG_ECRNX_ESWIN
+void ecrnx_ipc_rxdesc_elem_repush(struct ecrnx_hw *ecrnx_hw,
+ struct ecrnx_ipc_elem *elem)
+{
+ struct rxdesc_tag *rxdesc = elem->addr;
+ rxdesc->status = 0;
+ dma_sync_single_for_device(ecrnx_hw->dev, elem->dma_addr,
+ sizeof(struct rxdesc_tag), DMA_BIDIRECTIONAL);
+ ipc_host_rxdesc_push(ecrnx_hw->ipc_env, elem, (u32)elem->dma_addr);
+}
+#endif
+/**
+ * ecrnx_ipc_rxbuf_elem_allocs() - Allocate and push a RX buffer for the FW
+ *
+ * @ecrnx_hw: Main driver data
+ */
+#ifndef CONFIG_ECRNX_ESWIN
+int ecrnx_ipc_rxbuf_elem_allocs(struct ecrnx_hw *ecrnx_hw)
+{
+ struct sk_buff *skb;
+ struct hw_rxhdr *hw_rxhdr;
+ dma_addr_t dma_addr;
+ int size = ecrnx_hw->ipc_env->rx_bufsz;
+ int nb, idx;
+
+ skb = dev_alloc_skb(size);
+ if (unlikely(!skb)) {
+ dev_err(ecrnx_hw->dev, "Failed to allocate rx buffer\n");
+ return -ENOMEM;
+ }
+
+ dma_addr = dma_map_single(ecrnx_hw->dev, skb->data, size, DMA_FROM_DEVICE);
+
+ if (unlikely(dma_mapping_error(ecrnx_hw->dev, dma_addr))) {
+ dev_err(ecrnx_hw->dev, "Failed to map rx buffer\n");
+ goto err_skb;
+ }
+
+ hw_rxhdr = (struct hw_rxhdr *)skb->data;
+ hw_rxhdr->pattern = 0;
+ dma_sync_single_for_device(ecrnx_hw->dev,
+ dma_addr + offsetof(struct hw_rxhdr, pattern),
+ sizeof(hw_rxhdr->pattern), DMA_BIDIRECTIONAL);
+
+ /* Find first free slot */
+ nb = 0;
+ idx = ecrnx_hw->rxbuf_elems.idx;
+ while (ecrnx_hw->rxbuf_elems.skb[idx] && nb < ECRNX_RXBUFF_MAX) {
+ idx = ( idx + 1 ) % ECRNX_RXBUFF_MAX;
+ nb++;
+ }
+
+ if (WARN((nb == ECRNX_RXBUFF_MAX), "No more free space for rxbuff")) {
+ goto err_dma;
+ }
+
+ ecrnx_hw->rxbuf_elems.skb[idx] = skb;
+
+ /* Save info in skb control buffer */
+ ECRNX_RXBUFF_DMA_ADDR_SET(skb, dma_addr);
+ ECRNX_RXBUFF_PATTERN_SET(skb, ecrnx_rxbuff_pattern);
+ ECRNX_RXBUFF_IDX_SET(skb, idx);
+
+ /* Push buffer to FW */
+ ipc_host_rxbuf_push(ecrnx_hw->ipc_env, ECRNX_RXBUFF_IDX_TO_HOSTID(idx),
+ dma_addr);
+
+ /* Save idx so that on next push the free slot will be found quicker */
+ ecrnx_hw->rxbuf_elems.idx = ( idx + 1 ) % ECRNX_RXBUFF_MAX;
+
+ return 0;
+
+ err_dma:
+ dma_unmap_single(ecrnx_hw->dev, dma_addr, size, DMA_FROM_DEVICE);
+ err_skb:
+ dev_kfree_skb(skb);
+ return -ENOMEM;
+
+ return 0;
+}
+#endif
+
+/**
+ * ecrnx_ipc_rxbuf_elem_repush() - Repush a rxbuf to FW
+ *
+ * @ecrnx_hw: Main driver data
+ * @skb: Skb to repush
+ *
+ * In case a skb is not forwarded to upper layer it can be re-used.
+ * It is assumed that @skb has been verified before calling this function and
+ * that it is a valid rx buffer
+ * (i.e. skb == ecrnx_hw->rxbuf_elems.skb[ECRNX_RXBUFF_IDX_GET(skb)])
+ */
+#ifndef CONFIG_ECRNX_ESWIN
+void ecrnx_ipc_rxbuf_elem_repush(struct ecrnx_hw *ecrnx_hw,
+ struct sk_buff *skb)
+{
+ dma_addr_t dma_addr;
+ struct hw_rxhdr *hw_rxhdr = (struct hw_rxhdr *)skb->data;
+ int idx;
+
+ /* reset pattern */
+ hw_rxhdr->pattern = 0;
+ dma_addr = ECRNX_RXBUFF_DMA_ADDR_GET(skb);
+ dma_sync_single_for_device(ecrnx_hw->dev,
+ dma_addr + offsetof(struct hw_rxhdr, pattern),
+ sizeof(hw_rxhdr->pattern), DMA_BIDIRECTIONAL);
+
+ /* re-push buffer to FW */
+ idx = ECRNX_RXBUFF_IDX_GET(skb);
+
+ ipc_host_rxbuf_push(ecrnx_hw->ipc_env, ECRNX_RXBUFF_IDX_TO_HOSTID(idx),
+ dma_addr);
+}
+#endif
+
+/**
+ * ecrnx_ipc_rxbuf_elems_allocs() - Allocate and push all RX buffer for the FW
+ *
+ * @ecrnx_hw: Main driver data
+ */
+#ifndef CONFIG_ECRNX_ESWIN
+static int ecrnx_ipc_rxbuf_elems_allocs(struct ecrnx_hw *ecrnx_hw)
+{
+ //int i, nb = ecrnx_hw->ipc_env->rx_bufnb;
+ int i, nb = 0;
+
+ for (i = 0; i < ECRNX_RXBUFF_MAX; i++) {
+ ecrnx_hw->rxbuf_elems.skb[i] = NULL;
+ }
+ ecrnx_hw->rxbuf_elems.idx = 0;
+
+ for (i = 0; i < nb; i++) {
+ if (ecrnx_ipc_rxbuf_elem_allocs(ecrnx_hw)) {
+ dev_err(ecrnx_hw->dev, "Failed to allocate rx buf %d/%d\n",
+ i + 1, nb);
+ return -ENOMEM;
+ }
+ }
+ return 0;
+}
+#endif
+
+/**
+ * ecrnx_ipc_rxbuf_elems_deallocs() - Free all RX buffer allocated for the FW
+ *
+ * @ecrnx_hw: Main driver data
+ */
+#ifndef CONFIG_ECRNX_ESWIN
+static void ecrnx_ipc_rxbuf_elems_deallocs(struct ecrnx_hw *ecrnx_hw)
+{
+ struct sk_buff *skb;
+ int i;
+
+ for (i = 0; i < ECRNX_RXBUFF_MAX; i++) {
+ if (ecrnx_hw->rxbuf_elems.skb[i]) {
+ skb = ecrnx_hw->rxbuf_elems.skb[i];
+ dma_unmap_single(ecrnx_hw->dev, ECRNX_RXBUFF_DMA_ADDR_GET(skb),
+ ecrnx_hw->ipc_env->rx_bufsz, DMA_FROM_DEVICE);
+ dev_kfree_skb(skb);
+ ecrnx_hw->rxbuf_elems.skb[i] = NULL;
+ }
+ }
+}
+#endif
+
+/**
+ * ecrnx_ipc_rxbuf_elem_pull() - Extract a skb from local table
+ *
+ * @ecrnx_hw: Main driver data
+ * @skb: SKb to extract for table
+ *
+ * After checking that skb is actually a pointer of local table, extract it
+ * from the table.
+ * When buffer is removed, DMA mapping is remove which has the effect to
+ * synchronize the buffer for the cpu.
+ * To be called before passing skb to upper layer.
+ */
+#ifndef CONFIG_ECRNX_ESWIN
+void ecrnx_ipc_rxbuf_elem_pull(struct ecrnx_hw *ecrnx_hw, struct sk_buff *skb)
+{
+ unsigned int idx = ECRNX_RXBUFF_IDX_GET(skb);
+
+ if (ECRNX_RXBUFF_VALID_IDX(idx) && (ecrnx_hw->rxbuf_elems.skb[idx] == skb)) {
+ dma_addr_t dma_addr = ECRNX_RXBUFF_DMA_ADDR_GET(skb);
+ ecrnx_hw->rxbuf_elems.skb[idx] = NULL;
+ dma_unmap_single(ecrnx_hw->dev, dma_addr,
+ ecrnx_hw->ipc_env->rx_bufsz, DMA_FROM_DEVICE);
+ } else {
+ WARN(1, "Incorrect rxbuff idx skb=%p table[%u]=%p", skb, idx,
+ idx < ECRNX_RXBUFF_MAX ? ecrnx_hw->rxbuf_elems.skb[idx] : NULL);
+ }
+
+ /* Reset the pattern and idx */
+ ECRNX_RXBUFF_PATTERN_SET(skb, 0);
+ ECRNX_RXBUFF_IDX_SET(skb, ECRNX_RXBUFF_MAX);
+}
+#endif
+
+/**
+ * ecrnx_ipc_rxbuf_elem_sync() - Sync part of a RX buffer
+ *
+ * @ecrnx_hw: Main driver data
+ * @skb: SKb to sync
+ * @len: Len to sync
+ *
+ * After checking that skb is actually a pointer of local table, sync @p len
+ * bytes of the buffer for CPU. Buffer is not removed from the table
+ */
+#ifndef CONFIG_ECRNX_ESWIN
+void ecrnx_ipc_rxbuf_elem_sync(struct ecrnx_hw *ecrnx_hw, struct sk_buff *skb,
+ int len)
+{
+ unsigned int idx = ECRNX_RXBUFF_IDX_GET(skb);
+
+ if (ECRNX_RXBUFF_VALID_IDX(idx) && (ecrnx_hw->rxbuf_elems.skb[idx] == skb)) {
+ dma_addr_t dma_addr = ECRNX_RXBUFF_DMA_ADDR_GET(skb);
+ dma_sync_single_for_cpu(ecrnx_hw->dev, dma_addr, len, DMA_FROM_DEVICE);
+ } else {
+ WARN(1, "Incorrect rxbuff idx skb=%p table[%u]=%p", skb, idx,
+ idx < ECRNX_RXBUFF_MAX ? ecrnx_hw->rxbuf_elems.skb[idx] : NULL);
+ }
+}
+#endif
+#endif /* ! CONFIG_ECRNX_SOFTMAC */
+
+/**
+ * ecrnx_elems_deallocs() - Deallocate IPC storage elements.
+ * @ecrnx_hw: Main driver data
+ *
+ * This function deallocates all the elements required for communications with
+ * LMAC, such as Rx Data elements, MSGs elements, ...
+ * This function should be called in correspondence with the allocation function.
+ */
+#ifndef CONFIG_ECRNX_ESWIN
+static void ecrnx_elems_deallocs(struct ecrnx_hw *ecrnx_hw)
+{
+ ecrnx_ipc_rxbuf_elems_deallocs(ecrnx_hw);
+ ecrnx_ipc_unsup_rx_vec_elems_deallocs(ecrnx_hw);
+#ifdef CONFIG_ECRNX_FULLMAC
+ ecrnx_ipc_elem_pool_deallocs(&ecrnx_hw->e2arxdesc_pool);
+#endif
+ ecrnx_ipc_elem_pool_deallocs(&ecrnx_hw->e2amsgs_pool);
+ ecrnx_ipc_elem_pool_deallocs(&ecrnx_hw->dbgmsgs_pool);
+ ecrnx_ipc_elem_pool_deallocs(&ecrnx_hw->e2aradars_pool);
+ ecrnx_ipc_elem_var_deallocs(ecrnx_hw, &ecrnx_hw->pattern_elem);
+ ecrnx_ipc_elem_var_deallocs(ecrnx_hw, &ecrnx_hw->dbgdump_elem.buf);
+}
+#endif
+
+/**
+ * ecrnx_elems_allocs() - Allocate IPC storage elements.
+ * @ecrnx_hw: Main driver data
+ *
+ * This function allocates all the elements required for communications with
+ * LMAC, such as Rx Data elements, MSGs elements, ...
+ * This function should be called in correspondence with the deallocation function.
+ */
+static int ecrnx_elems_allocs(struct ecrnx_hw *ecrnx_hw)
+{
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+#ifndef CONFIG_ECRNX_ESWIN
+ if (dma_set_coherent_mask(ecrnx_hw->dev, DMA_BIT_MASK(32)))
+ goto err_alloc;
+ if (ecrnx_ipc_elem_pool_allocs(ecrnx_hw, &ecrnx_hw->e2amsgs_pool,
+ ecrnx_hw->ipc_env->ipc_e2amsg_bufnb,
+ ecrnx_hw->ipc_env->ipc_e2amsg_bufsz,
+ "ecrnx_ipc_e2amsgs_pool",
+ ipc_host_msgbuf_push))
+ goto err_alloc;
+
+ if (ecrnx_ipc_elem_pool_allocs(ecrnx_hw, &ecrnx_hw->dbgmsgs_pool,
+ ecrnx_hw->ipc_env->ipc_dbg_bufnb,
+ ecrnx_hw->ipc_env->ipc_dbg_bufsz,
+ "ecrnx_ipc_dbgmsgs_pool",
+ ipc_host_dbgbuf_push))
+ goto err_alloc;
+
+ if (ecrnx_ipc_elem_pool_allocs(ecrnx_hw, &ecrnx_hw->e2aradars_pool,
+ ecrnx_hw->ipc_env->radar_bufnb,
+ ecrnx_hw->ipc_env->radar_bufsz,
+ "ecrnx_ipc_e2aradars_pool",
+ ipc_host_radarbuf_push))
+ goto err_alloc;
+
+ if (ecrnx_ipc_unsup_rx_vec_elems_allocs(ecrnx_hw))
+ #error 1111
+ goto err_alloc;
+
+ if (ecrnx_ipc_elem_var_allocs(ecrnx_hw, &ecrnx_hw->pattern_elem,
+ sizeof(u32), DMA_TO_DEVICE,
+ NULL, &ecrnx_rxbuff_pattern,
+ ipc_host_patt_addr_push))
+ goto err_alloc;
+
+ if (ecrnx_ipc_elem_var_allocs(ecrnx_hw, &ecrnx_hw->dbgdump_elem.buf,
+ sizeof(struct dbg_debug_dump_tag),
+ DMA_FROM_DEVICE, NULL, NULL,
+ ipc_host_dbginfobuf_push))
+ goto err_alloc;
+
+ /*
+ * Note that the RX buffers are no longer allocated here as their size depends on the
+ * FW configuration, which is not available at that time.
+ * They will be allocated when checking the parameter compatibility between the driver
+ * and the underlying components (i.e. during the ecrnx_handle_dynparams() execution)
+ */
+
+#ifdef CONFIG_ECRNX_FULLMAC
+ if (ecrnx_ipc_elem_pool_allocs(ecrnx_hw, &ecrnx_hw->e2arxdesc_pool,
+ ecrnx_hw->ipc_env->rxdesc_nb,
+ sizeof(struct rxdesc_tag),
+ "ecrnx_ipc_e2arxdesc_pool",
+ ipc_host_rxdesc_push))
+ goto err_alloc;
+
+#endif /* CONFIG_ECRNX_FULLMAC */
+
+ return 0;
+
+err_alloc:
+ ecrnx_elems_deallocs(ecrnx_hw);
+ return -ENOMEM;
+#else
+ return 0;
+#endif
+}
+
+/**
+ * ecrnx_ipc_msg_push() - Push a msg to IPC queue
+ *
+ * @ecrnx_hw: Main driver data
+ * @msg_buf: Pointer to message
+ * @len: Size, in bytes, of message
+ */
+void ecrnx_ipc_msg_push(struct ecrnx_hw *ecrnx_hw, void *msg_buf, uint16_t len)
+{
+ ecrnx_hw->msg_tx++;
+ ipc_host_msg_push(ecrnx_hw->ipc_env, msg_buf, len);
+}
+
+/**
+ * ecrnx_ipc_txdesc_push() - Push a txdesc to FW
+ *
+ * @ecrnx_hw: Main driver data
+ * @tx_desc: Pointer on &struct txdesc_api to push to FW
+ * @hostid: Pointer save in ipc env to retrieve tx buffer upon confirmation.
+ * @hw_queue: Hw queue to push txdesc to
+ * @user: User position to push the txdesc to. It must be set to 0 if MU-MIMMO
+ * is not used.
+ */
+void ecrnx_ipc_txdesc_push(struct ecrnx_hw *ecrnx_hw, void *tx_desc,
+ void *hostid, int hw_queue, int user)
+{
+
+#if !defined(CONFIG_ECRNX_ESWIN_SDIO) && !defined(CONFIG_ECRNX_ESWIN_USB)
+ volatile struct txdesc_host *txdesc_host;
+ u32 *src, *dst;
+ int i;
+
+ txdesc_host = ipc_host_txdesc_get(ecrnx_hw->ipc_env, hw_queue, user);
+ BUG_ON(!txdesc_host);
+
+ dst = (typeof(dst))&txdesc_host->api;
+ src = (typeof(src))tx_desc;
+ for (i = 0; i < sizeof(txdesc_host->api) / sizeof(*src); i++)
+ *dst++ = *src++;
+
+ wmb(); /* vs desc */
+
+ ipc_host_txdesc_push(ecrnx_hw->ipc_env, hw_queue, user, hostid);
+#else
+ ecrnx_frame_send(ecrnx_hw, tx_desc, hostid, hw_queue, user);
+#endif
+}
+
+/**
+ * ecrnx_ipc_fw_trace_desc_get() - Return pointer to the start of trace
+ * description in IPC environment
+ *
+ * @ecrnx_hw: Main driver data
+ */
+void *ecrnx_ipc_fw_trace_desc_get(struct ecrnx_hw *ecrnx_hw)
+{
+#ifndef CONFIG_ECRNX_ESWIN
+ return (void *)&(ecrnx_hw->ipc_env->shared->trace_pattern);
+#else
+ return NULL;
+#endif
+}
+
+#ifndef CONFIG_ECRNX_ESWIN
+/**
+ * ecrnx_ipc_sta_buffer_init - Initialize counter of bufferred data for a given sta
+ *
+ * @ecrnx_hw: Main driver data
+ * @sta_idx: Index of the station to initialize
+ */
+void ecrnx_ipc_sta_buffer_init(struct ecrnx_hw *ecrnx_hw, int sta_idx)
+{
+ int i;
+ volatile u32_l *buffered;
+
+ if (sta_idx >= NX_REMOTE_STA_MAX)
+ return;
+
+ buffered = ecrnx_hw->ipc_env->shared->buffered[sta_idx];
+
+ for (i = 0; i < TID_MAX; i++) {
+ *buffered++ = 0;
+ }
+}
+#endif
+/**
+ * ecrnx_ipc_sta_buffer - Update counter of bufferred data for a given sta
+ *
+ * @ecrnx_hw: Main driver data
+ * @sta: Managed station
+ * @tid: TID on which data has been added or removed
+ * @size: Size of data to add (or remove if < 0) to STA buffer.
+ */
+void ecrnx_ipc_sta_buffer(struct ecrnx_hw *ecrnx_hw, struct ecrnx_sta *sta, int tid, int size)
+{
+#ifndef CONFIG_ECRNX_ESWIN
+ u32_l *buffered;
+
+ if (!sta)
+ return;
+
+ if ((sta->sta_idx >= NX_REMOTE_STA_MAX) || (tid >= TID_MAX))
+ return;
+
+ buffered = &ecrnx_hw->ipc_env->shared->buffered[sta->sta_idx][tid];
+
+ if (size < 0) {
+ size = -size;
+ if (*buffered < size)
+ *buffered = 0;
+ else
+ *buffered -= size;
+ } else {
+ // no test on overflow
+ *buffered += size;
+ }
+#endif
+}
+
+/**
+ * ecrnx_msgind() - IRQ handler callback for %IPC_IRQ_E2A_MSG
+ *
+ * @pthis: Pointer to main driver data
+ * @hostid: Pointer to IPC elem from e2amsgs_pool
+ */
+static u8 ecrnx_msgind(void *pthis, void *hostid)
+{
+ struct ecrnx_hw *ecrnx_hw = pthis;
+ u8 ret = 0;
+#ifndef CONFIG_ECRNX_ESWIN
+ struct ecrnx_ipc_elem *elem = hostid;
+ struct ipc_e2a_msg *msg = elem->addr;
+
+ REG_SW_SET_PROFILING(ecrnx_hw, SW_PROF_MSGIND);
+
+ /* Look for pattern which means that this hostbuf has been used for a MSG */
+ if (msg->pattern != IPC_MSGE2A_VALID_PATTERN) {
+ ret = -1;
+ goto msg_no_push;
+ }
+#else
+ struct ipc_e2a_msg *msg = NULL;
+
+ ecrnx_hw->msg_rx++;
+ ECRNX_DBG("%s enter 0x%x, 0x%x!!\n", __func__, pthis, hostid);
+ if(!pthis || !hostid){
+ ECRNX_ERR(" %s input param error!! \n", __func__);
+ return ret;
+ }
+ msg = hostid;
+#endif
+ /* Relay further actions to the msg parser */
+ ecrnx_rx_handle_msg(ecrnx_hw, msg);
+
+#ifndef CONFIG_ECRNX_ESWIN
+ /* Reset the msg element and re-use it */
+ msg->pattern = 0;
+ wmb();
+
+ /* Push back the buffer to the LMAC */
+ ipc_host_msgbuf_push(ecrnx_hw->ipc_env, elem, elem->dma_addr);
+
+msg_no_push:
+ REG_SW_CLEAR_PROFILING(ecrnx_hw, SW_PROF_MSGIND);
+#endif
+ ECRNX_DBG("%s exit!!", __func__);
+ return ret;
+}
+
+/**
+ * ecrnx_msgackind() - IRQ handler callback for %IPC_IRQ_E2A_MSG_ACK
+ *
+ * @pthis: Pointer to main driver data
+ * @hostid: Pointer to command acknoledged
+ */
+static u8 ecrnx_msgackind(void *pthis, void *hostid)
+{
+ struct ecrnx_hw *ecrnx_hw = (struct ecrnx_hw *)pthis;
+
+ ecrnx_hw->msg_tx_done++;
+ ecrnx_hw->cmd_mgr.llind(&ecrnx_hw->cmd_mgr, (struct ecrnx_cmd *)hostid);
+ return -1;
+}
+
+/**
+ * ecrnx_radarind() - IRQ handler callback for %IPC_IRQ_E2A_RADAR
+ *
+ * @pthis: Pointer to main driver data
+ * @hostid: Pointer to IPC elem from e2aradars_pool
+ */
+static u8 ecrnx_radarind(void *pthis, void *hostid)
+{
+#ifdef CONFIG_ECRNX_RADAR
+ struct ecrnx_hw *ecrnx_hw = pthis;
+ struct ecrnx_ipc_elem *elem = hostid;
+ struct radar_pulse_array_desc *pulses = elem->addr;
+ u8 ret = 0;
+ int i;
+
+ /* Look for pulse count meaning that this hostbuf contains RADAR pulses */
+ if (pulses->cnt == 0) {
+ ret = -1;
+ goto radar_no_push;
+ }
+
+ if (ecrnx_radar_detection_is_enable(&ecrnx_hw->radar, pulses->idx)) {
+ /* Save the received pulses only if radar detection is enabled */
+ for (i = 0; i < pulses->cnt; i++) {
+ struct ecrnx_radar_pulses *p = &ecrnx_hw->radar.pulses[pulses->idx];
+
+ p->buffer[p->index] = pulses->pulse[i];
+ p->index = (p->index + 1) % ECRNX_RADAR_PULSE_MAX;
+ if (p->count < ECRNX_RADAR_PULSE_MAX)
+ p->count++;
+ }
+
+ /* Defer pulse processing in separate work */
+ if (! work_pending(&ecrnx_hw->radar.detection_work))
+ schedule_work(&ecrnx_hw->radar.detection_work);
+ }
+
+ /* Reset the radar element and re-use it */
+ pulses->cnt = 0;
+#ifndef CONFIG_ECRNX_ESWIN
+ wmb();
+
+ /* Push back the buffer to the LMAC */
+ ipc_host_radarbuf_push(ecrnx_hw->ipc_env, elem, (u32)elem->dma_addr);
+#endif
+radar_no_push:
+ return ret;
+#else
+ return -1;
+#endif
+}
+
+/**
+ * ecrnx_prim_tbtt_ind() - IRQ handler callback for %IPC_IRQ_E2A_TBTT_PRIM
+ *
+ * @pthis: Pointer to main driver data
+ */
+static void ecrnx_prim_tbtt_ind(void *pthis)
+{
+#ifdef CONFIG_ECRNX_SOFTMAC
+ struct ecrnx_hw *ecrnx_hw = (struct ecrnx_hw *)pthis;
+ ecrnx_tx_bcns(ecrnx_hw);
+#endif /* CONFIG_ECRNX_SOFTMAC */
+}
+
+/**
+ * ecrnx_sec_tbtt_ind() - IRQ handler callback for %IPC_IRQ_E2A_TBTT_SEC
+ *
+ * @pthis: Pointer to main driver data
+ */
+static void ecrnx_sec_tbtt_ind(void *pthis)
+{
+}
+
+/**
+ * ecrnx_dbgind() - IRQ handler callback for %IPC_IRQ_E2A_DBG
+ *
+ * @pthis: Pointer to main driver data
+ * @hostid: Pointer to IPC elem from dbgmsgs_pool
+ */
+
+#ifdef CONFIG_ECRNX_ESWIN_USB
+ extern void usb_dbg_printf(void * data, int len);
+#endif
+static u8 ecrnx_dbgind(void *pthis, void *hostid)
+{
+ u8 ret = 0;
+#ifndef CONFIG_ECRNX_ESWIN
+ struct ecrnx_hw *ecrnx_hw = (struct ecrnx_hw *)pthis;
+ struct ecrnx_ipc_elem *elem = hostid;
+ struct ipc_dbg_msg *dbg_msg = elem->addr;
+
+ REG_SW_SET_PROFILING(ecrnx_hw, SW_PROF_DBGIND);
+
+ /* Look for pattern which means that this hostbuf has been used for a MSG */
+ if (dbg_msg->pattern != IPC_DBG_VALID_PATTERN) {
+ ret = -1;
+ goto dbg_no_push;
+ }
+
+ /* Display the string */
+ //printk("%s %s", (char *)FW_STR, (char *)dbg_msg->string);
+
+ /* Reset the msg element and re-use it */
+ dbg_msg->pattern = 0;
+ wmb();
+
+ /* Push back the buffer to the LMAC */
+ ipc_host_dbgbuf_push(ecrnx_hw->ipc_env, elem, (u32)elem->dma_addr);
+
+dbg_no_push:
+ REG_SW_CLEAR_PROFILING(ecrnx_hw, SW_PROF_DBGIND);
+
+#else
+ struct sk_buff *skb = (struct sk_buff *)hostid;
+#ifdef CONFIG_ECRNX_ESWIN_USB
+ usb_dbg_printf(skb->data, skb->len);
+#else
+ uint8_t string[IPC_DBG_PARAM_SIZE] = {0};
+ if(skb->len < IPC_DBG_PARAM_SIZE)
+ {
+ memcpy(string, skb->data, skb->len);
+ }
+ else
+ {
+ printk("waring: string buff no enough \n");
+ memcpy(string, skb->data, IPC_DBG_PARAM_SIZE-1);
+ }
+ ECRNX_PRINT("%s %s", (char *)FW_STR, (char *)string);
+#endif
+#endif
+
+ return ret;
+}
+
+/**
+ * ecrnx_ipc_rxbuf_init() - Allocate and initialize RX buffers.
+ *
+ * @ecrnx_hw: Main driver data
+ * @rx_bufsz: Size of the buffer to be allocated
+ *
+ * This function updates the RX buffer size according to the parameter and allocates the
+ * RX buffers
+ */
+#ifndef CONFIG_ECRNX_ESWIN
+int ecrnx_ipc_rxbuf_init(struct ecrnx_hw *ecrnx_hw, uint32_t rx_bufsz)
+{
+ ecrnx_hw->ipc_env->rx_bufsz = rx_bufsz;
+ return(ecrnx_ipc_rxbuf_elems_allocs(ecrnx_hw));
+}
+#endif
+/**
+ * ecrnx_ipc_init() - Initialize IPC interface.
+ *
+ * @ecrnx_hw: Main driver data
+ * @shared_ram: Pointer to shared memory that contains IPC shared struct
+ *
+ * This function initializes IPC interface by registering callbacks, setting
+ * shared memory area and calling IPC Init function.
+ * It should be called only once during driver's lifetime.
+ */
+int ecrnx_ipc_init(struct ecrnx_hw *ecrnx_hw, u8 *shared_ram)
+{
+ struct ipc_host_cb_tag cb;
+ int res;
+ ECRNX_DBG("%s entry!!", __func__);
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ /* initialize the API interface */
+ cb.recv_data_ind = ecrnx_rxdataind;
+ cb.recv_radar_ind = ecrnx_radarind;
+ cb.recv_msg_ind = ecrnx_msgind;
+ cb.recv_msgack_ind = ecrnx_msgackind;
+ cb.recv_dbg_ind = ecrnx_dbgind;
+ cb.send_data_cfm = ecrnx_txdatacfm;
+ cb.handle_data_cfm = ecrnx_handle_tx_datacfm;
+
+ cb.prim_tbtt_ind = ecrnx_prim_tbtt_ind;
+ cb.sec_tbtt_ind = ecrnx_sec_tbtt_ind;
+ cb.recv_unsup_rx_vec_ind = ecrnx_unsup_rx_vec_ind;
+
+ /* set the IPC environment */
+ ecrnx_hw->ipc_env = (struct ipc_host_env_tag *)
+ kzalloc(sizeof(struct ipc_host_env_tag), GFP_KERNEL);
+
+ if (!ecrnx_hw->ipc_env)
+ return -ENOMEM;
+
+ /* call the initialization of the IPC */
+ ipc_host_init(ecrnx_hw->ipc_env, &cb,
+ (struct ipc_shared_env_tag *)shared_ram, ecrnx_hw);
+
+ ecrnx_cmd_mgr_init(&ecrnx_hw->cmd_mgr);
+
+ ecrnx_rx_reord_init(ecrnx_hw);
+#ifdef CONFIG_ECRNX_ESWIN_SDIO
+ ecrnx_sdio_init(ecrnx_hw);
+#elif defined(CONFIG_ECRNX_ESWIN_USB)
+ ecrnx_usb_init(ecrnx_hw);
+#endif
+
+ res = ecrnx_elems_allocs(ecrnx_hw);
+ if (res) {
+
+ kfree(ecrnx_hw->ipc_env);
+ ecrnx_hw->ipc_env = NULL;
+ }
+ ECRNX_DBG("%s exit!!", __func__);
+ return res;
+}
+
+/**
+ * ecrnx_ipc_deinit() - Release IPC interface
+ *
+ * @ecrnx_hw: Main driver data
+ */
+void ecrnx_ipc_deinit(struct ecrnx_hw *ecrnx_hw)
+{
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ ecrnx_ipc_tx_drain(ecrnx_hw);
+ ecrnx_cmd_mgr_deinit(&ecrnx_hw->cmd_mgr);
+#ifndef CONFIG_ECRNX_ESWIN
+ ecrnx_elems_deallocs(ecrnx_hw);
+#endif
+
+ ecrnx_rx_reord_deinit(ecrnx_hw);
+#ifdef CONFIG_ECRNX_ESWIN_SDIO
+ if (ecrnx_hw->ipc_env->shared) {
+ kfree(ecrnx_hw->ipc_env->shared);
+ ecrnx_hw->ipc_env->shared = NULL;
+ }
+ ecrnx_sdio_deinit(ecrnx_hw);
+#elif defined(CONFIG_ECRNX_ESWIN_USB)
+ ecrnx_usb_deinit(ecrnx_hw);
+#endif
+
+ if (ecrnx_hw->ipc_env) {
+ kfree(ecrnx_hw->ipc_env);
+ ecrnx_hw->ipc_env = NULL;
+ }
+}
+
+/**
+ * ecrnx_ipc_start() - Start IPC interface
+ *
+ * @ecrnx_hw: Main driver data
+ */
+void ecrnx_ipc_start(struct ecrnx_hw *ecrnx_hw)
+{
+ ipc_host_enable_irq(ecrnx_hw->ipc_env, IPC_IRQ_E2A_ALL);
+}
+
+/**
+ * ecrnx_ipc_stop() - Stop IPC interface
+ *
+ * @ecrnx_hw: Main driver data
+ */
+void ecrnx_ipc_stop(struct ecrnx_hw *ecrnx_hw)
+{
+ ipc_host_disable_irq(ecrnx_hw->ipc_env, IPC_IRQ_E2A_ALL);
+}
+
+/**
+ * ecrnx_ipc_tx_drain() - Flush IPC TX buffers
+ *
+ * @ecrnx_hw: Main driver data
+ *
+ * This assumes LMAC is still (tx wise) and there's no TX race until LMAC is up
+ * tx wise.
+ * This also lets both IPC sides remain in sync before resetting the LMAC,
+ * e.g with ecrnx_send_reset.
+ */
+void ecrnx_ipc_tx_drain(struct ecrnx_hw *ecrnx_hw)
+{
+ int i, j;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ if (!ecrnx_hw->ipc_env) {
+ printk(KERN_CRIT "%s: bypassing (restart must have failed)\n", __func__);
+ return;
+ }
+
+ for (i = 0; i < ECRNX_HWQ_NB; i++) {
+ for (j = 0; j < nx_txuser_cnt[i]; j++) {
+ struct sk_buff *skb;
+ while ((skb = (struct sk_buff *)ipc_host_tx_flush(ecrnx_hw->ipc_env, i, j))) {
+ struct ecrnx_sw_txhdr *sw_txhdr =
+ ((struct ecrnx_txhdr *)skb->data)->sw_hdr;
+#ifndef CONFIG_ECRNX_ESWIN
+ #ifdef CONFIG_ECRNX_AMSDUS_TX
+ if (sw_txhdr->desc.host.packet_cnt > 1) {
+ struct ecrnx_amsdu_txhdr *amsdu_txhdr;
+ list_for_each_entry(amsdu_txhdr, &sw_txhdr->amsdu.hdrs, list) {
+ dma_unmap_single(ecrnx_hw->dev, amsdu_txhdr->dma_addr,
+ amsdu_txhdr->map_len, DMA_TO_DEVICE);
+ dev_kfree_skb_any(amsdu_txhdr->skb);
+ }
+ }
+ #endif
+ kmem_cache_free(ecrnx_hw->sw_txhdr_cache, sw_txhdr);
+ dma_unmap_single(ecrnx_hw->dev, sw_txhdr->dma_addr,
+ sw_txhdr->map_len, DMA_TO_DEVICE);
+#endif
+ skb_pull(skb, sw_txhdr->headroom);
+#ifdef CONFIG_ECRNX_SOFTMAC
+ ieee80211_free_txskb(ecrnx_hw->hw, skb);
+#else
+ dev_kfree_skb_any(skb);
+#endif /* CONFIG_ECRNX_SOFTMAC */
+ }
+ }
+ }
+}
+
+/**
+ * ecrnx_ipc_tx_pending() - Check if TX pframes are pending at FW level
+ *
+ * @ecrnx_hw: Main driver data
+ */
+bool ecrnx_ipc_tx_pending(struct ecrnx_hw *ecrnx_hw)
+{
+ return ipc_host_tx_frames_pending(ecrnx_hw->ipc_env);
+}
+
+/**
+ * ecrnx_error_ind() - %DBG_ERROR_IND message callback
+ *
+ * @ecrnx_hw: Main driver data
+ *
+ * This function triggers the UMH script call that will indicate to the user
+ * space the error that occurred and stored the debug dump. Once the UMH script
+ * is executed, the ecrnx_umh_done() function has to be called.
+ */
+void ecrnx_error_ind(struct ecrnx_hw *ecrnx_hw)
+{
+ struct ecrnx_ipc_elem_var *elem = &ecrnx_hw->dbgdump_elem.buf;
+ struct dbg_debug_dump_tag *dump = elem->addr;
+
+ dma_sync_single_for_device(ecrnx_hw->dev, elem->dma_addr, elem->size,
+ DMA_FROM_DEVICE);
+ dev_err(ecrnx_hw->dev, "(type %d): dump received\n",
+ dump->dbg_info.error_type);
+
+#ifdef CONFIG_ECRNX_DEBUGFS
+ ecrnx_hw->debugfs.trace_prst = true;
+ ecrnx_trigger_um_helper(&ecrnx_hw->debugfs);
+#endif
+}
+
+/**
+ * ecrnx_umh_done() - Indicate User Mode helper finished
+ *
+ * @ecrnx_hw: Main driver data
+ *
+ */
+void ecrnx_umh_done(struct ecrnx_hw *ecrnx_hw)
+{
+ if (!test_bit(ECRNX_DEV_STARTED, &ecrnx_hw->flags))
+ return;
+
+ /* this assumes error_ind won't trigger before ipc_host_dbginfobuf_push
+ is called and so does not irq protect (TODO) against error_ind */
+#ifdef CONFIG_ECRNX_DEBUGFS
+ ecrnx_hw->debugfs.trace_prst = false;
+#ifndef CONFIG_ECRNX_ESWIN
+ ipc_host_dbginfobuf_push(ecrnx_hw->ipc_env, ecrnx_hw->dbgdump_elem.buf.dma_addr);
+#endif
+#endif
+}
diff --git a/drivers/net/wireless/eswin/ecrnx_utils.h b/drivers/net/wireless/eswin/ecrnx_utils.h
new file mode 100644
index 000000000000..09315b248d8a
--- /dev/null
+++ b/drivers/net/wireless/eswin/ecrnx_utils.h
@@ -0,0 +1,209 @@
+/**
+ * ecrnx_ipc_utils.h
+ *
+ * IPC utility function declarations
+ *
+ * Copyright (C) ESWIN 2015-2020
+ */
+#ifndef _ECRNX_IPC_UTILS_H_
+#define _ECRNX_IPC_UTILS_H_
+
+#include <linux/dma-mapping.h>
+#include <linux/dmapool.h>
+#include <linux/skbuff.h>
+
+#include "lmac_msg.h"
+
+enum ecrnx_dev_flag {
+ ECRNX_DEV_RESTARTING,
+ ECRNX_DEV_STACK_RESTARTING,
+ ECRNX_DEV_STARTED,
+ ECRNX_DEV_ADDING_STA,
+};
+
+struct ecrnx_hw;
+struct ecrnx_sta;
+
+/**
+ * struct ecrnx_ipc_elem - Generic IPC buffer of fixed size
+ *
+ * @addr: Host address of the buffer.
+ * @dma_addr: DMA address of the buffer.
+ */
+struct ecrnx_ipc_elem {
+ void *addr;
+ dma_addr_t dma_addr;
+};
+
+/**
+ * struct ecrnx_ipc_elem_pool - Generic pool of IPC buffers of fixed size
+ *
+ * @nb: Number of buffers currenlty allocated in the pool
+ * @buf: Array of buffers (size of array is @nb)
+ * @pool: DMA pool in which buffers have been allocated
+ */
+struct ecrnx_ipc_elem_pool {
+ int nb;
+ struct ecrnx_ipc_elem *buf;
+ struct dma_pool *pool;
+};
+
+/**
+ * struct ecrnx_ipc_elem - Generic IPC buffer of variable size
+ *
+ * @addr: Host address of the buffer.
+ * @dma_addr: DMA address of the buffer.
+ * @size: Size, in bytes, of the buffer
+ */
+struct ecrnx_ipc_elem_var {
+ void *addr;
+ dma_addr_t dma_addr;
+ size_t size;
+};
+
+/**
+ * struct ecrnx_ipc_dbgdump_elem - IPC buffer for debug dump
+ *
+ * @mutex: Mutex to protect access to debug dump
+ * @buf: IPC buffer
+ */
+struct ecrnx_ipc_dbgdump_elem {
+ struct mutex mutex;
+ struct ecrnx_ipc_elem_var buf;
+};
+
+//static const u32 ecrnx_rxbuff_pattern = 0xCAFEFADE;
+static const u32 ecrnx_rxbuff_pattern = 0xAAAAAA00;
+
+
+/*
+ * Maximum Length of Radiotap header vendor specific data(in bytes)
+ */
+#define RADIOTAP_HDR_VEND_MAX_LEN 16
+
+/*
+ * Maximum Radiotap Header Length without vendor specific data (in bytes)
+ */
+#define RADIOTAP_HDR_MAX_LEN 80
+
+/*
+ * Unsupported HT Frame data length (in bytes)
+ */
+#define UNSUP_RX_VEC_DATA_LEN 2
+
+/**
+ * struct ecrnx_ipc_skb_elem - IPC buffer for SKB element
+ *
+ * @skb: Pointer to the skb buffer allocated
+ * @dma_addr: DMA address of the data buffer fo skb
+ *
+ */
+struct ecrnx_ipc_skb_elem {
+ struct sk_buff *skb;
+ dma_addr_t dma_addr;
+};
+
+#ifdef CONFIG_ECRNX_FULLMAC
+
+/* Maximum number of rx buffer the fw may use at the same time */
+#define ECRNX_RXBUFF_MAX (64 * NX_REMOTE_STA_MAX)
+
+/**
+ * struct ecrnx_ipc_rxbuf_elems - IPC buffers for RX
+ *
+ * @skb: Array of buffer push to FW.
+ * @idx: Index of the last pushed skb.(Use to find the next free entry quicker)
+ *
+ * Note: contrary to softmac version, dma_addr are stored inside skb->cb.
+ * (cf &struct ecrnx_skb_cb)
+ */
+struct ecrnx_ipc_rxbuf_elems {
+ struct sk_buff *skb[ECRNX_RXBUFF_MAX];
+ int idx;
+};
+
+/**
+ * struct ecrnx_skb_cb - Control Buffer structure for RX buffer
+ *
+ * @dma_addr: DMA address of the data buffer
+ * @pattern: Known pattern (used to check pointer on skb)
+ * @idx: Index in &struct ecrnx_hw.rxbuff_table that contains address of this
+ * buffer
+ */
+struct ecrnx_skb_cb {
+ dma_addr_t dma_addr;
+ uint32_t pattern;
+ uint32_t idx;
+};
+
+#define ECRNX_RXBUFF_DMA_ADDR_SET(skbuff, addr) \
+ ((struct ecrnx_skb_cb *)(skbuff->cb))->dma_addr = addr
+#define ECRNX_RXBUFF_DMA_ADDR_GET(skbuff) \
+ ((struct ecrnx_skb_cb *)(skbuff->cb))->dma_addr
+
+#define ECRNX_RXBUFF_PATTERN_SET(skbuff, pat) \
+ ((struct ecrnx_skb_cb *)(skbuff->cb))->pattern = pat
+#define ECRNX_RXBUFF_PATTERN_GET(skbuff) \
+ ((struct ecrnx_skb_cb *)(skbuff->cb))->pattern
+
+#define ECRNX_RXBUFF_IDX_SET(skbuff, val) \
+ ((struct ecrnx_skb_cb *)(skbuff->cb))->idx = val
+#define ECRNX_RXBUFF_IDX_GET(skbuff) \
+ ((struct ecrnx_skb_cb *)(skbuff->cb))->idx
+
+#define ECRNX_RXBUFF_VALID_IDX(idx) ((idx) < ECRNX_RXBUFF_MAX)
+
+/* Used to ensure that hostid set to fw is never 0 */
+#define ECRNX_RXBUFF_IDX_TO_HOSTID(idx) ((idx) + 1)
+#define ECRNX_RXBUFF_HOSTID_TO_IDX(hostid) ((hostid) - 1)
+
+#endif /* CONFIG_ECRNX_FULLMAC */
+
+
+#ifdef CONFIG_ECRNX_SOFTMAC
+int ecrnx_ipc_rxbuf_elem_allocs(struct ecrnx_hw *ecrnx_hw,
+ struct ecrnx_ipc_skb_elem *elem);
+void ecrnx_ipc_rxbuf_elem_repush(struct ecrnx_hw *ecrnx_hw,
+ struct ecrnx_ipc_skb_elem *elem);
+#else
+int ecrnx_ipc_rxbuf_elem_allocs(struct ecrnx_hw *ecrnx_hw);
+void ecrnx_ipc_rxbuf_elem_pull(struct ecrnx_hw *ecrnx_hw, struct sk_buff *skb);
+void ecrnx_ipc_rxbuf_elem_sync(struct ecrnx_hw *ecrnx_hw, struct sk_buff *skb,
+ int len);
+void ecrnx_ipc_rxdesc_elem_repush(struct ecrnx_hw *ecrnx_hw,
+ struct ecrnx_ipc_elem *elem);
+void ecrnx_ipc_rxbuf_elem_repush(struct ecrnx_hw *ecrnx_hw,
+ struct sk_buff *skb);
+#endif /* CONFIG_ECRNX_SOFTMAC */
+
+void ecrnx_printf(const char *fmt, ...);
+void ecrnx_ipc_msg_push(struct ecrnx_hw *ecrnx_hw, void *msg_buf, uint16_t len);
+void ecrnx_ipc_txdesc_push(struct ecrnx_hw *ecrnx_hw, void *tx_desc,
+ void *hostid, int hw_queue, int user);
+void *ecrnx_ipc_fw_trace_desc_get(struct ecrnx_hw *ecrnx_hw);
+int ecrnx_ipc_rxbuf_init(struct ecrnx_hw *ecrnx_hw, uint32_t rx_bufsz);
+int ecrnx_ipc_init(struct ecrnx_hw *ecrnx_hw, u8 *shared_ram);
+void ecrnx_ipc_deinit(struct ecrnx_hw *ecrnx_hw);
+void ecrnx_ipc_start(struct ecrnx_hw *ecrnx_hw);
+void ecrnx_ipc_stop(struct ecrnx_hw *ecrnx_hw);
+void ecrnx_ipc_tx_drain(struct ecrnx_hw *ecrnx_hw);
+bool ecrnx_ipc_tx_pending(struct ecrnx_hw *ecrnx_hw);
+
+struct ipc_host_env_tag;
+int ecrnx_ipc_elem_var_allocs(struct ecrnx_hw *ecrnx_hw,
+ struct ecrnx_ipc_elem_var *elem, size_t elem_size,
+ enum dma_data_direction dir,
+ void *buf, const void *init,
+ void (*push)(struct ipc_host_env_tag *, uint32_t));
+void ecrnx_ipc_elem_var_deallocs(struct ecrnx_hw *ecrnx_hw,
+ struct ecrnx_ipc_elem_var *elem);
+int ecrnx_ipc_unsup_rx_vec_elem_allocs(struct ecrnx_hw *ecrnx_hw,
+ struct ecrnx_ipc_skb_elem *elem);
+
+void ecrnx_error_ind(struct ecrnx_hw *ecrnx_hw);
+void ecrnx_umh_done(struct ecrnx_hw *ecrnx_hw);
+
+void ecrnx_ipc_sta_buffer_init(struct ecrnx_hw *ecrnx_hw, int sta_idx);
+void ecrnx_ipc_sta_buffer(struct ecrnx_hw *ecrnx_hw, struct ecrnx_sta *sta, int tid, int size);
+
+#endif /* _ECRNX_IPC_UTILS_H_ */
diff --git a/drivers/net/wireless/eswin/ecrnx_version.h b/drivers/net/wireless/eswin/ecrnx_version.h
new file mode 100644
index 000000000000..c56e94253bac
--- /dev/null
+++ b/drivers/net/wireless/eswin/ecrnx_version.h
@@ -0,0 +1,10 @@
+#ifndef _ECRNX_VERSION_H_
+#define _ECRNX_VERSION_H_
+
+
+static inline void ecrnx_print_version(void)
+{
+ printk(ECRNX_VERS_BANNER"\n");
+}
+
+#endif /* _ECRNX_VERSION_H_ */
diff --git a/drivers/net/wireless/eswin/eswin_port/eswin_utils.c b/drivers/net/wireless/eswin/eswin_port/eswin_utils.c
new file mode 100644
index 000000000000..bd75cc6c6505
--- /dev/null
+++ b/drivers/net/wireless/eswin/eswin_port/eswin_utils.c
@@ -0,0 +1,194 @@
+/**
+ ******************************************************************************
+ *
+ * @file eswin_utils.c
+ *
+ *@brief msg and management frame send functions
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+
+#include <linux/dma-mapping.h>
+#include <linux/etherdevice.h>
+#include <net/sock.h>
+
+#include "ecrnx_defs.h"
+#include "ecrnx_tx.h"
+#include "ecrnx_msg_tx.h"
+#ifdef CONFIG_ECRNX_FULLMAC
+#include "ecrnx_mesh.h"
+#endif
+#include "ecrnx_events.h"
+#include "ecrnx_compat.h"
+
+#include "eswin_utils.h"
+
+#if 0
+#if defined(CONFIG_ECRNX_ESWIN_SDIO)
+#include "sdio_host_interface.h"
+#elif defined(CONFIG_ECRNX_ESWIN_USB)
+#include "usb_host_interface.h"
+#endif
+#endif
+
+typedef int (*fn_send_t)(void * buff, int len, int flag);
+
+
+fn_send_t ecrnx_host_send;
+
+
+void ecrnx_send_handle_register(void * fn)
+{
+ ecrnx_host_send = (fn_send_t)fn;
+}
+
+
+int host_send(void *buff, int len, int flag)
+{
+ //ECRNX_DBG("%s:len:%d,flag %#x \n", __func__, len, flag);
+ return ecrnx_host_send(buff, len, flag);
+}
+
+void ecrnx_frame_send(struct ecrnx_hw *ecrnx_hw, struct txdesc_api *tx_desc,
+ struct sk_buff *skb, int hw_queue, int user)
+{
+ u32_l tx_flag;
+ //struct ecrnx_txhdr *txhdr = NULL;
+ struct ecrnx_sw_txhdr *sw_txhdr = NULL;
+
+ ecrnx_hw->data_tx++;
+ //send tx desc
+ tx_flag = ((user << FLAG_TXDESC_USER_SHIFT) & FLAG_TXDESC_USER_MASK) | ((hw_queue << FLAG_TXDESC_QUEUE_SHIFT) & FLAG_TXDESC_QUEUE_MASK);
+ tx_flag |= TX_FLAG_TX_DESC & FLAG_MSG_TYPE_MASK;
+#if 0
+ ECRNX_DBG("%s, user %d, queue %d, flag %#x, hostid 0x%08x, skb 0x%08x\n", __func__,
+#ifdef CONFIG_ECRNX_SPLIT_TX_BUF
+ user, hw_queue, tx_flag, tx_desc->host.packet_addr[0], (u32_l)skb);
+ tx_desc->host.packet_addr[0] = (u32_l)skb;
+#else
+ user, hw_queue, tx_flag, tx_desc->host.packet_addr, (u32_l)skb);
+ tx_desc->host.packet_addr = (u32_l)skb;
+#endif
+#endif
+
+#if defined(CONFIG_ECRNX_ESWIN_SDIO)
+ uint8_t *data = skb->data;
+ sw_txhdr = container_of(tx_desc, struct ecrnx_sw_txhdr, desc);
+ data += sw_txhdr->offset;
+ data -= ECRNX_TX_TXDESC_API_ALIGN;
+ memcpy(data, tx_desc, sizeof(struct txdesc_api));
+
+ host_send((void*)data, ECRNX_TX_TXDESC_API_ALIGN + sw_txhdr->frame_len, tx_flag);
+
+#elif defined(CONFIG_ECRNX_ESWIN_USB)
+ uint8_t *data;
+ struct ecrnx_txhdr *txhdr = (struct ecrnx_txhdr *)skb->data;
+
+ sw_txhdr = txhdr->sw_hdr;
+ skb_pull(skb, sw_txhdr->offset - ECRNX_TX_TXDESC_API_ALIGN - sizeof(u32_l));
+ memcpy(skb->data, (char*)&tx_flag, sizeof(u32_l));
+ memcpy((uint8_t *)skb->data + sizeof(u32_l), (char*)tx_desc, sizeof(struct txdesc_api));
+
+ data = skb->data - sizeof(ptr_addr);
+ memcpy(data, (char*)&txhdr, sizeof(void *)); //store the ecrnx thhdr to the skb, cfm need use it
+ host_send((void *)skb, skb->len, tx_flag);
+#endif
+
+#ifdef CONFIG_ECRNX_AMSDUS_TX
+ //send amsdu sub frame
+ if (sw_txhdr->desc.host.flags & TXU_CNTRL_AMSDU) {
+ struct ecrnx_amsdu_txhdr *amsdu_txhdr;
+ printk("...........amsdu tx\n");
+ list_for_each_entry(amsdu_txhdr, &sw_txhdr->amsdu.hdrs, list) {
+ printk("...........amsdu tx: %x, %u\n", amsdu_txhdr->send_pos, amsdu_txhdr->map_len);
+#if defined(CONFIG_ECRNX_ESWIN_SDIO)
+ host_send((void*)amsdu_txhdr->send_pos, amsdu_txhdr->map_len, tx_flag);
+#elif defined(CONFIG_ECRNX_ESWIN_USB)
+ struct sk_buff *amsdu_skb = dev_alloc_skb(amsdu_txhdr->map_len + sizeof(int));
+
+ memcpy(amsdu_skb->data, &tx_flag, sizeof(int));
+ memcpy((char*)amsdu_skb->data + sizeof(int), amsdu_txhdr->send_pos, amsdu_txhdr->map_len);
+ amsdu_skb->len = amsdu_txhdr->map_len + sizeof(int);
+ host_send((void *)amsdu_skb, amsdu_skb->len, tx_flag);
+#endif
+ }
+ }
+#endif
+ return;
+}
+
+static inline void ecrnx_bcn_param_send(struct mm_bcn_change_req *req)
+{
+ if (req->bcn_ptr && req->bcn_len) {
+ host_send((void*)req->bcn_ptr, req->bcn_len, TX_FLAG_MSG_BEACON_IE);
+ }
+}
+
+static inline void ecrnx_scanie_param_send(struct scan_start_req *req)
+{
+ if (req->add_ies && req->add_ie_len) {
+ host_send((void*)req->add_ies, req->add_ie_len, TX_FLAG_MSG_SCAN_IE);
+ }
+}
+
+static inline void ecrnx_scanuie_param_send(struct scanu_start_req *req)
+{
+ if (req->add_ies && req->add_ie_len) {
+ host_send((void*)req->add_ies, req->add_ie_len, TX_FLAG_MSG_SCANU_IE);
+ }
+}
+
+static inline void ecrnx_apm_param_send(struct apm_start_req *req)
+{
+ if (req->bcn_addr && req->bcn_len) {
+ host_send((void*)req->bcn_addr, req->bcn_len, TX_FLAG_MSG_SOFTAP_IE);
+ }
+}
+
+void ecrnx_msg_send(struct ecrnx_cmd *cmd, uint16_t len)
+{
+ void *param = cmd->a2e_msg->param;
+ int param_len = cmd->a2e_msg->param_len;
+
+ //cmd->a2e_msg->hostid = (u64_l)cmd;
+ memcpy(cmd->a2e_msg->hostid, &cmd, sizeof(struct ecrnx_cmd *));
+
+ switch (cmd->a2e_msg->id) {
+ case MM_BCN_CHANGE_REQ:
+ if (param_len >= sizeof(struct mm_bcn_change_req)) {
+ ecrnx_bcn_param_send((struct mm_bcn_change_req *)param);
+ }
+ break;
+ case SCAN_START_REQ:
+ if (param_len >= sizeof(struct scan_start_req)) {
+ ecrnx_scanie_param_send((struct scan_start_req *)param);
+ }
+ break;
+ case SCANU_START_REQ:
+ if (param_len >= sizeof(struct scanu_start_req)) {
+ ecrnx_scanuie_param_send((struct scanu_start_req *)param);
+ }
+ break;
+ case APM_START_REQ:
+ if (param_len >= sizeof(struct apm_start_req)) {
+ ecrnx_apm_param_send((struct apm_start_req *)param);
+ }
+ break;
+ default:
+ break;
+ }
+
+ host_send((void*)cmd->a2e_msg, len, TX_FLAG_MSG_E);
+}
+
+const struct ecrnx_element *cfg80211_find_ecrnx_elem(u8 eid, const u8 *ies, int len)
+{
+ const struct ecrnx_element *elem;
+ for_each_ecrnx_element(elem, ies, len) {
+ if (elem->id == (eid))
+ return elem;
+ }
+ return NULL;
+}
diff --git a/drivers/net/wireless/eswin/eswin_port/eswin_utils.h b/drivers/net/wireless/eswin/eswin_port/eswin_utils.h
new file mode 100644
index 000000000000..1258c889ad47
--- /dev/null
+++ b/drivers/net/wireless/eswin/eswin_port/eswin_utils.h
@@ -0,0 +1,123 @@
+/**
+ ******************************************************************************
+ *
+ * @file eswin_utils.h
+ *
+ * @brief File containing the definition of tx/rx info and debug descriptors.
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+
+#ifndef _ESWIN_UTILS_H_
+#define _ESWIN_UTILS_H_
+
+#if defined(CONFIG_ECRNX_ESWIN_SDIO)
+// for param in sdio irq flag
+#define FLAG_MSG_TYPE_MASK 0x0f
+
+// queue in sdio irq flag
+#define FLAG_TXDESC_QUEUE_SHIFT 5
+#define FLAG_TXDESC_QUEUE_MASK 0xe0
+#define FLAG_TXDESC_USER_SHIFT 4
+#define FLAG_TXDESC_USER_MASK 0x10
+
+// tag in tx payload param
+//#define FLAG_TXPAYLOAD_PARAM_SHIFT 6
+//#define FLAG_TXPAYLOAD_PARAM_MASK 0xffc0 //10 bits
+#define TX_DESC_TAG_LEN 492 //SLAVE fhost_tx_desc_tag_len
+
+#elif defined(CONFIG_ECRNX_ESWIN_USB)
+// for param in sdio irq flag
+#define FLAG_MSG_TYPE_MASK 0x00ff
+
+// queue in sdio irq flag
+#define FLAG_TXDESC_USER_SHIFT 8
+#define FLAG_TXDESC_USER_MASK 0x0f00
+#define FLAG_TXDESC_QUEUE_SHIFT 12
+#define FLAG_TXDESC_QUEUE_MASK 0xf000
+#endif
+
+typedef enum {
+ TX_FLAG_INVALID,
+ TX_FLAG_MSG_E,
+ TX_FLAG_MSG_SCAN_IE,
+ TX_FLAG_MSG_SCANU_IE,
+ TX_FLAG_MSG_BEACON_IE,
+ TX_FLAG_MSG_PROBE_IE,
+ TX_FLAG_MSG_SOFTAP_IE,
+ TX_FLAG_TX_DESC,
+ TX_FLAG_MSG_DEBUGFS_IE,
+ TX_FLAG_IWPRIV_IE,
+#ifdef CONFIG_ECRNX_WIFO_CAIL
+ TX_FLAG_AMT_IWPRIV_IE, //10
+#endif
+
+ TX_FLAG_MAX,
+}host_tx_flag_e;
+
+typedef enum {
+ DBG_TYPE_D, //debug msg type
+ DBG_TYPE_I, //info msg type
+ DBG_TYPE_W, //warning msg type
+ DBG_TYPE_E, //error msg type
+ DBG_TYPE_O, //this type means always output
+ DBG_TYPE_MAX
+}dbg_print_type_e;
+
+typedef struct {
+ uint8_t direct; // slave log director, 0 is print by uart, 1 is send to host
+ dbg_print_type_e dbg_level; //slave debug level
+}dbg_req_t;
+
+typedef struct {
+ uint32_t hostid;
+ uint32_t tag;
+}data_req_msg_t;
+
+typedef struct {
+#ifdef CONFIG_ECRNX_ESWIN_SDIO
+ uint32_t resvd; /* for sdio */
+ uint32_t rlen; /* for sdio */
+#endif
+ uint32_t frm_type;
+}dispatch_hdr_t;
+
+/**
+ * struct pulse_elem - elements in pulse queue
+ * @ts: time stamp in usecs
+ */
+struct agg_elem {
+ struct list_head head;
+ struct sk_buff* skb;
+ /// Traffic Index
+ uint8_t tid;
+ uint16_t real_offset; /* dma address align 4, real data - real_offset */
+ u32 sn;
+ /*bitmaps of agg frame to upaold*/
+ uint64_t agg_upload_idx;
+ /*bitmaps of agg frame upaold status, 0 is forward, 1 is delete*/
+ uint64_t agg_upload_status;
+};
+
+/**
+ * struct pulse_elem - elements in pulse queue
+ * @ts: time stamp in usecs
+ */
+struct defrag_elem {
+ struct list_head head;
+ struct sk_buff* skb;
+ /// Traffic Index
+ uint8_t tid;
+ uint16_t real_offset; /* dma address align 4, real data - real_offset */
+ u32 sn;
+};
+
+void ecrnx_frame_send(struct ecrnx_hw *ecrnx_hw, struct txdesc_api *tx_desc,
+ struct sk_buff *skb, int hw_queue, int user);
+void ecrnx_msg_send(struct ecrnx_cmd *cmd, uint16_t len);
+int host_send(void *buff, int len, int flag);
+const struct ecrnx_element *cfg80211_find_ecrnx_elem(u8 eid, const u8 *ies, int len);
+
+#endif
diff --git a/drivers/net/wireless/eswin/feature_config/6600_config b/drivers/net/wireless/eswin/feature_config/6600_config
new file mode 100644
index 000000000000..4d24d336b645
--- /dev/null
+++ b/drivers/net/wireless/eswin/feature_config/6600_config
@@ -0,0 +1,87 @@
+export ECRNX_MODULE_NAME=wlan_ecr6600
+
+# Enable 6600 hal config
+export CONFIG_6600_HAL=y
+# Enable A-MSDU support (need FW support)
+## Select this if FW is compiled with AMSDU support
+export CONFIG_ECRNX_SPLIT_TX_BUF=n
+
+## Select this TO send AMSDU
+export CONFIG_ECRNX_AMSDUS_TX=n
+
+# Enable BFMER support (need FW support)
+export CONFIG_ECRNX_BFMER=n
+CONFIG_ECRNX_MUMIMO_TX=n
+
+# Enable handling of radar event
+export CONFIG_ECRNX_RADAR=y
+
+# Enable HW queue for Broadcast/Multicast traffic (need FW support)
+export CONFIG_ECRNX_BCMC=y
+
+# Enable Monitor+Data interface support (need FW support)
+export CONFIG_ECRNX_MON_DATA=n
+
+# extra DEBUG config
+export CONFIG_ECRNX_SW_PROFILING=n
+export CONFIG_ECRNX_DBG=y
+export CONFIG_ECRNX_DBG_LEVEL=3
+export CONFIG_DEBUG_FS=y
+export CONFIG_ECRNX_DEBUGFS_CUSTOM ?= y
+# CONFIG PLATFORM
+export CONFIG_ECRNX_ESWIN=y
+export CONFIG_ECRNX_ESWIN_SDIO=y
+
+export CONFIG_ECRNX_ESWIN_USB=n
+
+# CONFIG SYSTERM TEST
+export CONFIG_TEST_ESWIN_SDIO=n
+
+# CONFIG SYSTERM TEST
+export CONFIG_STANDALONE_WIFI=n
+
+# CONFIG SYSTERM TEST
+export CONFIG_STANDALONE_WIFI_BLE=y
+# Enable BFMER support (need FW support)
+export CONFIG_ECRNX_HE=y
+
+# Enable P2P Listen
+export CONFIG_ECRNX_P2P=y
+# Enable 5G
+export CONFIG_ECRNX_5G=n
+
+#CONFIG SDIO WIFI CALIBRATION
+export CONFIG_ECRNX_WIFO_CAIL=y
+
+#
+# WAITING FOR KCONFIG {
+#
+export CONFIG_ECRNX_SOFTMAC=n
+export CONFIG_ECRNX_FULLMAC=m
+export CONFIG_ECRNX_FHOST=n
+
+#
+# DEBUG OPTIONS
+export CONFIG_ECRNX_UM_HELPER_DFLT="/dini/dini_bin/ecrnx_umh.sh"
+
+#
+# FW ARCH:
+export CONFIG_ECRNX_SDM=n
+export CONFIG_ECRNX_TL4=n
+
+# IPC version
+export CONFIG_ECRNX_OLD_IPC=n
+
+# Support of P2P DebugFS for enabling/disabling NoA and OppPS
+export CONFIG_ECRNX_P2P_DEBUGFS=n
+
+# config_ceva_rtos = y use ceva rtos and add task_cli id
+# config_ceva_rtos = n use freertos and no task_cli id
+#export CONFIG_CEVA_RTOS=n
+
+export NX_VIRT_DEV_MAX=3
+export NX_REMOTE_STA_MAX=2
+export NX_MU_GROUP_MAX=62
+export NX_TXDESC_CNT=4
+export NX_TX_MAX_RATES=4
+export NX_CHAN_CTXT_CNT=3
diff --git a/drivers/net/wireless/eswin/feature_config/6600u_config b/drivers/net/wireless/eswin/feature_config/6600u_config
new file mode 100644
index 000000000000..2c997519f411
--- /dev/null
+++ b/drivers/net/wireless/eswin/feature_config/6600u_config
@@ -0,0 +1,98 @@
+export ECRNX_MODULE_NAME=wlan_ecr6600u_usb
+
+# Enable A-MSDU support (need FW support)
+## Select this if FW is compiled with AMSDU support
+export CONFIG_ECRNX_SPLIT_TX_BUF=n
+
+## Select this TO send AMSDU
+export CONFIG_ECRNX_AMSDUS_TX=n
+
+# Enable BFMER support (need FW support)
+export CONFIG_ECRNX_BFMER=n
+CONFIG_ECRNX_MUMIMO_TX=n
+
+# Enable handling of radar event
+export CONFIG_ECRNX_RADAR=y
+
+# Enable HW queue for Broadcast/Multicast traffic (need FW support)
+export CONFIG_ECRNX_BCMC=y
+
+# Enable Monitor+Data interface support (need FW support)
+export CONFIG_ECRNX_MON_DATA=n
+
+#Tx and RX data processing method
+export CONFIG_ECRNX_WORKQUEUE=n
+export CONFIG_ECRNX_TASKLET=n
+export CONFIG_ECRNX_KTHREAD=y
+
+# extra DEBUG config
+export CONFIG_ECRNX_SW_PROFILING=n
+export CONFIG_ECRNX_DBG=y
+export CONFIG_ECRNX_DBG_LEVEL=3
+export CONFIG_DEBUG_FS=n
+export CONFIG_ECRNX_DEBUGFS_CUSTOM ?= n
+# Support of P2P DebugFS for enabling/disabling NoA and OppPS
+export CONFIG_ECRNX_P2P_DEBUGFS=n
+
+# CONFIG PLATFORM
+export CONFIG_ECRNX_ESWIN=y
+export CONFIG_ECRNX_ESWIN_SDIO=n
+
+export CONFIG_ECRNX_ESWIN_USB=y
+
+# CONFIG SYSTERM TEST
+export CONFIG_TEST_ESWIN_SDIO=n
+
+# CONFIG SYSTERM TEST
+export CONFIG_STANDALONE_WIFI=n
+
+# CONFIG SYSTERM TEST
+export CONFIG_STANDALONE_WIFI_BLE=y
+# Enable BFMER support (need FW support)
+export CONFIG_ECRNX_HE=y
+
+# Enable P2P Listen
+export CONFIG_ECRNX_P2P=n
+# Enable 5G
+export CONFIG_ECRNX_5G=n
+
+#CONFIG SDIO WIFI CALIBRATION
+export CONFIG_ECRNX_WIFO_CAIL=y
+
+#
+# WAITING FOR KCONFIG {
+#
+export CONFIG_ECRNX_SOFTMAC=n
+export CONFIG_ECRNX_FULLMAC=m
+export CONFIG_ECRNX_FHOST=n
+
+#
+#Build for Android Platform
+export CONFIG_ECRNX_ANDRIOD=y
+
+#
+# DEBUG OPTIONS
+export CONFIG_ECRNX_UM_HELPER_DFLT="/dini/dini_bin/ecrnx_umh.sh"
+
+#
+# FW ARCH:
+export CONFIG_ECRNX_SDM=n
+export CONFIG_ECRNX_TL4=n
+
+# IPC version
+export CONFIG_ECRNX_OLD_IPC=n
+
+# config_ceva_rtos = y use ceva rtos and add task_cli id
+# config_ceva_rtos = n use freertos and no task_cli id
+export CONFIG_CEVA_RTOS=y
+
+export NX_VIRT_DEV_MAX=3
+export NX_REMOTE_STA_MAX=4
+export NX_MU_GROUP_MAX=62
+export NX_TXDESC_CNT=4
+export NX_TX_MAX_RATES=4
+export NX_CHAN_CTXT_CNT=3
+
+# config gpio for 6600u power key
+#export CONFIG_POWERKEY_GPIO=4
+
diff --git a/drivers/net/wireless/eswin/fullmac/Makefile b/drivers/net/wireless/eswin/fullmac/Makefile
new file mode 100644
index 000000000000..8479e35ac1bc
--- /dev/null
+++ b/drivers/net/wireless/eswin/fullmac/Makefile
@@ -0,0 +1,167 @@
+$(ECRNX_MODULE_NAME)-y := ecrnx_msg_tx.o \
+ ecrnx_msg_rx.o \
+ ecrnx_utils.o \
+ ecrnx_cmds.o \
+ ecrnx_cfgfile.o \
+ ecrnx_strs.o \
+ ecrnx_txq.o \
+ ecrnx_mod_params.o \
+ ecrnx_platform.o \
+ ipc_host.o \
+ hal_desc.o \
+ ecrnx_iwpriv.o \
+ fw_head_check.o \
+ slave_log_buf.o \
+ $(MAC_SRC)/ecrnx_tdls.o \
+ $(MAC_SRC)/ecrnx_mesh.o \
+ $(MAC_SRC)/ecrnx_main.o \
+ $(MAC_SRC)/ecrnx_rx.o \
+ $(MAC_SRC)/ecrnx_tx.o \
+ $(MAC_SRC)/ecrnx_calibration_data.o
+
+ifeq ($(CONFIG_ECRNX_ESWIN_SDIO), y)
+$(ECRNX_MODULE_NAME)-y += sdio/sdio.o \
+ sdio/ecrnx_sdio.o \
+ sdio/core.o \
+ sdio/fw.o \
+ eswin_port/eswin_utils.o
+ifeq ($(CONFIG_ECRNX_ESWIN_SDIO), y)
+#$(ECRNX_MODULE_NAME)-y += sdio/debug.o
+endif
+endif
+
+ifeq ($(CONFIG_ECRNX_ESWIN_USB), y)
+$(ECRNX_MODULE_NAME)-y += usb/usb.o \
+ usb/ecrnx_usb.o \
+ usb/core.o \
+ usb/fw.o \
+ eswin_port/eswin_utils.o
+
+endif
+
+ifeq ($(CONFIG_ECRNX_WIFO_CAIL), y)
+$(ECRNX_MODULE_NAME)-y += $(MAC_SRC)/ecrnx_amt.o
+endif
+
+$(ECRNX_MODULE_NAME)-$(CONFIG_ECRNX_RADAR) += ecrnx_radar.o
+$(ECRNX_MODULE_NAME)-$(CONFIG_DEBUG_FS) += ecrnx_debugfs.o
+$(ECRNX_MODULE_NAME)-$(CONFIG_DEBUG_FS) += ecrnx_fw_dump.o
+$(ECRNX_MODULE_NAME)-$(CONFIG_DEBUG_FS) += ecrnx_fw_trace.o
+$(ECRNX_MODULE_NAME)-$(CONFIG_ECRNX_NL80211_TESTMODE) += ecrnx_testmode.o
+$(ECRNX_MODULE_NAME)-$(CONFIG_ECRNX_BFMER) += ecrnx_bfmer.o
+$(ECRNX_MODULE_NAME)-$(CONFIG_ECRNX_MUMIMO_TX) += ecrnx_mu_group.o
+$(ECRNX_MODULE_NAME)-$(CONFIG_ECRNX_DBG) += ecrnx_debug.o
+$(ECRNX_MODULE_NAME)-$(CONFIG_ECRNX_P2P) += $(MAC_SRC)/ecrnx_p2p.o
+$(ECRNX_MODULE_NAME)-$(CONFIG_ECRNX_DEBUGFS_CUSTOM) += $(MAC_SRC)/ecrnx_debugfs_func.o
+$(ECRNX_MODULE_NAME)-$(CONFIG_ECRNX_DEBUGFS_CUSTOM) += $(MAC_SRC)/ecrnx_debugfs_custom.o
+
+
+EXTRA_CFLAGS+=-Wno-date-time
+EXTRA_CFLAGS+=-Wno-declaration-after-statement
+EXTRA_CFLAGS+=-Wno-format
+EXTRA_CFLAGS+=-Wno-unused-variable
+EXTRA_CFLAGS+=-Wno-misleading-indentation
+EXTRA_CFLAGS+=-Wno-maybe-uninitialized
+#EXTRA_CFLAGS+=-Wno-int-conversion
+#EXTRA_CFLAGS+=-Wno-unused-function
+#EXTRA_CFLAGS+=-Wno-undef
+#EXTRA_CFLAGS+=-Wno-discarded-qualifiers
+#EXTRA_CFLAGS+=-Wno-unused-label
+
+
+ccflags-y := -DCONFIG_ECRNX_FULLMAC
+
+ccflags-y += -I$(DRIVER_PATH)
+ccflags-y += -I$(DRIVER_PATH)/$(MAC_SRC)
+ccflags-y += -I$(DRIVER_PATH)/eswin_port/
+
+ifeq ($(CONFIG_ECRNX_ESWIN_SDIO), y)
+ccflags-y += -I$(DRIVER_PATH)/sdio/
+else
+ccflags-y += -I$(DRIVER_PATH)/usb/
+endif
+
+ccflags-$(CONFIG_ECRNX_RADAR) += -DCONFIG_ECRNX_RADAR
+ccflags-$(CONFIG_ECRNX_MON_DATA) += -DCONFIG_ECRNX_MON_DATA
+ccflags-$(CONFIG_ECRNX_BFMER) += -DCONFIG_ECRNX_BFMER
+ccflags-$(CONFIG_ECRNX_SPLIT_TX_BUF) += -DCONFIG_ECRNX_SPLIT_TX_BUF
+ifeq ($(CONFIG_ECRNX_SPLIT_TX_BUF), y)
+ccflags-$(CONFIG_ECRNX_AMSDUS_TX) += -DCONFIG_ECRNX_AMSDUS_TX
+endif
+ccflags-$(CONFIG_ECRNX_SW_PROFILING) += -DCONFIG_ECRNX_SW_PROFILING
+ccflags-$(CONFIG_ECRNX_MUMIMO_TX) += -DCONFIG_ECRNX_MUMIMO_TX
+
+ifeq ($(CONFIG_ECRNX_MUMIMO_TX), y)
+ccflags-y += -DCONFIG_USER_MAX=2
+else
+ccflags-y += -DCONFIG_USER_MAX=1
+endif
+
+ifeq ($(CONFIG_ECRNX_BCMC), y)
+ccflags-y += -DNX_TXQ_CNT=5
+else
+ccflags-y += -DNX_TXQ_CNT=4
+endif
+
+# FW PLATFORM
+ifeq ($(CONFIG_ECRNX_DEBUGFS_CUSTOM), y)
+ccflags-$(CONFIG_ECRNX_DEBUGFS_CUSTOM) += -DCONFIG_ECRNX_DEBUGFS_CUSTOM
+endif
+
+ifeq ($(CONFIG_ECRNX_ESWIN), y)
+ccflags-$(CONFIG_ECRNX_ESWIN) += -DCONFIG_ECRNX_ESWIN
+endif
+
+ifeq ($(CONFIG_ECRNX_ESWIN_SDIO), y)
+ccflags-$(CONFIG_ECRNX_ESWIN_SDIO) += -DCONFIG_ECRNX_ESWIN_SDIO
+endif
+
+# SDIO TEST
+ifeq ($(CONFIG_TEST_ESWIN_SDIO), y)
+ccflags-$(CONFIG_TEST_ESWIN_SDIO) += -DCONFIG_TEST_ESWIN_SDIO
+endif
+
+ifeq ($(CONFIG_ECRNX_ESWIN_USB), y)
+ccflags-$(CONFIG_ECRNX_ESWIN_USB) += -DCONFIG_ECRNX_ESWIN_USB
+endif
+
+
+# standalone wifi
+ifeq ($(CONFIG_STANDALONE_WIFI), y)
+ccflags-$(CONFIG_STANDALONE_WIFI) += -DCONFIG_STANDALONE_WIFI
+endif
+
+# standalone wifi+ble
+ifeq ($(CONFIG_STANDALONE_WIFI_BLE), y)
+ccflags-$(CONFIG_STANDALONE_WIFI_BLE) += -DCONFIG_STANDALONE_WIFI_BLE
+endif
+
+# HE config
+ifeq ($(CONFIG_ECRNX_HE), y)
+ccflags-$(CONFIG_ECRNX_HE) += -DCONFIG_ECRNX_HE
+endif
+
+# P2P config
+ifeq ($(CONFIG_ECRNX_P2P), y)
+ccflags-$(CONFIG_ECRNX_P2P) += -DCONFIG_ECRNX_P2P
+endif
+
+# 5G config
+ifeq ($(CONFIG_ECRNX_5G), y)
+ccflags-$(CONFIG_ECRNX_5G) += -DCONFIG_ECRNX_5G
+endif
+
+# Android platform config
+ifeq ($(CONFIG_ECRNX_ANDRIOD), y)
+ccflags-$(CONFIG_ECRNX_ANDRIOD) += -DCONFIG_ECRNX_ANDRIOD
+endif
+
+# For old kernel (<=3.19)
+ifeq ($(shell test $(VERSION) -lt 4 -a "$(CONFIG_VENDOR_ECRNX)" = y ; echo $$?),0)
+ccflags-y += -DCONFIG_VENDOR_ECRNX_VHT_NO80
+endif
+
+#CONFIG SDIO WIFI CALIBRATION
+ifeq ($(CONFIG_ECRNX_WIFO_CAIL), y)
+ccflags-$(CONFIG_ECRNX_WIFO_CAIL) += -DCONFIG_ECRNX_WIFO_CAIL
+endif
diff --git a/drivers/net/wireless/eswin/fullmac/ecrnx_amt.c b/drivers/net/wireless/eswin/fullmac/ecrnx_amt.c
new file mode 100644
index 000000000000..5aa4384f99a8
--- /dev/null
+++ b/drivers/net/wireless/eswin/fullmac/ecrnx_amt.c
@@ -0,0 +1,122 @@
+/**
+ ******************************************************************************
+ *
+ * @file ecrnx_amt.c
+ *
+ * @brief Entry point of the ECRNX driver
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+
+#include "ecrnx_main.h"
+#include "eswin_utils.h"
+#include "ecrnx_calibration_data.h"
+
+#ifdef CONFIG_ECRNX_WIFO_CAIL
+#define AMT_GAIN_DELTA_MSG_FLAG ("gaindelta=")
+#define AMT_GAIN_DELTA_MSG_BUFF_LEN 60 //strlen(AMT_GAIN_DELTA_MSG_FLAG) + sizeof(gain_delta)
+static char gain_delta_msg_buf[AMT_GAIN_DELTA_MSG_BUFF_LEN];
+extern bool set_gain;
+
+struct ecrnx_iwpriv_amt_vif amt_vif;
+#if defined(CONFIG_WIRELESS_EXT) && defined(CONFIG_WEXT_PRIV)
+extern const struct iw_handler_def ecrnx_wext_private_handler;
+#endif
+
+static int eswin_netdev_amt_open(struct net_device *ndev)
+{
+ ECRNX_DBG("amt netdev open\n");
+
+ return 0;
+}
+
+static int eswin_netdev_amt_stop(struct net_device *ndev)
+{
+ ECRNX_DBG("amt netdev stop\n");
+
+ return 0;
+}
+
+static netdev_tx_t eswin_netdev_amt_start_xmit(struct sk_buff *skb,
+ struct net_device *ndev)
+{
+ if (skb)
+ dev_kfree_skb_any(skb);
+
+ return NETDEV_TX_OK;
+}
+
+static const struct net_device_ops eswin_netdev_ops_amt = {
+ .ndo_open = eswin_netdev_amt_open,
+ .ndo_stop = eswin_netdev_amt_stop,
+ .ndo_start_xmit = eswin_netdev_amt_start_xmit
+};
+
+static int ecrnx_amt_gain_delta_msg_send(void)
+{
+ if (set_gain != true)
+ return -1;
+
+ memcpy(gain_delta_msg_buf, AMT_GAIN_DELTA_MSG_FLAG, strlen(AMT_GAIN_DELTA_MSG_FLAG));
+ memcpy((gain_delta_msg_buf + strlen(AMT_GAIN_DELTA_MSG_FLAG)), gain_delta, GAIN_DELTA_CFG_BUF_SIZE);
+
+ host_send(gain_delta_msg_buf, sizeof(gain_delta_msg_buf), TX_FLAG_AMT_IWPRIV_IE);
+
+ return 0;
+}
+
+int ecrnx_amt_init(void)
+{
+ struct net_device *ndev;
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0))
+ ndev = alloc_netdev(0, "amt0", ether_setup);
+#else
+ ndev = alloc_netdev(0, "amt0", NET_NAME_UNKNOWN, ether_setup);
+#endif
+ if (!ndev) {
+ ECRNX_DBG("alloc netdev fail !!");
+ return -1;
+ }
+
+ amt_vif.ndev = ndev;
+ amt_vif.rxlen = 0;
+ init_waitqueue_head(&amt_vif.rxdataq);
+
+ ndev->netdev_ops = &eswin_netdev_ops_amt;
+#if defined(CONFIG_WIRELESS_EXT) && defined(CONFIG_WEXT_PRIV)
+ ndev->wireless_handlers = &ecrnx_wext_private_handler;
+#endif
+ /* clear the mac address */
+ memset(ndev->dev_addr, 0, ETH_ALEN);
+
+ if (register_netdev(ndev) != 0)
+ goto err_dev;
+
+ ecrnx_amt_gain_delta_msg_send();
+
+ ECRNX_PRINT(" %s:register the amt net device success\n", __func__);
+ return 0;
+
+err_dev:
+ ECRNX_ERR(" %s couldn't register the amt net device!!", __func__);
+ free_netdev(ndev);
+ amt_vif.ndev = NULL;
+ return -1;
+}
+
+void ecrnx_amt_deinit(void)
+{
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ if (amt_vif.ndev)
+ unregister_netdev(amt_vif.ndev);
+
+ amt_vif.ndev = NULL;
+}
+#endif
+
+
+
+
diff --git a/drivers/net/wireless/eswin/fullmac/ecrnx_amt.h b/drivers/net/wireless/eswin/fullmac/ecrnx_amt.h
new file mode 100644
index 000000000000..dc06e78c519a
--- /dev/null
+++ b/drivers/net/wireless/eswin/fullmac/ecrnx_amt.h
@@ -0,0 +1,21 @@
+/**
+ ******************************************************************************
+ *
+ * @file ecrnx_amt.h
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+
+#ifndef _ECRNX_AMT_H_
+#define _ECRNX_AMT_H_
+
+#ifdef CONFIG_ECRNX_WIFO_CAIL
+int ecrnx_amt_init(void);
+void ecrnx_amt_deinit(void);
+
+extern struct ecrnx_iwpriv_amt_vif amt_vif;
+#endif
+
+#endif /* _ECRNX_AMT_H_ */
diff --git a/drivers/net/wireless/eswin/fullmac/ecrnx_calibration_data.c b/drivers/net/wireless/eswin/fullmac/ecrnx_calibration_data.c
new file mode 100644
index 000000000000..1c845ee7ee29
--- /dev/null
+++ b/drivers/net/wireless/eswin/fullmac/ecrnx_calibration_data.c
@@ -0,0 +1,27 @@
+/**
+ ****************************************************************************************
+ *
+ * @file ecrnx_calibration_data.c
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ****************************************************************************************
+ */
+
+/**
+ * INCLUDE FILES
+ ****************************************************************************************
+ */
+#include "ecrnx_calibration_data.h"
+
+/**
+ * TYPE DEFINITIONS
+ ****************************************************************************************
+ */
+wifi_cal_data_t cal_result;
+s8 gain_delta[GAIN_DELTA_CFG_BUF_SIZE] = { //base on 11b 11M, ht20/40 mcs7
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 11b */
+ 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, /* 11g */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 11n_20M mcs 0 - 7 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 11n_40M mcs 0 - 7 */
+ -2, -2, -2, -2, -2, -2, -2, -2, -2, -2}; /* 11ax mcs 0 - 9 */
diff --git a/drivers/net/wireless/eswin/fullmac/ecrnx_calibration_data.h b/drivers/net/wireless/eswin/fullmac/ecrnx_calibration_data.h
new file mode 100644
index 000000000000..ddf8301e9ce4
--- /dev/null
+++ b/drivers/net/wireless/eswin/fullmac/ecrnx_calibration_data.h
@@ -0,0 +1,80 @@
+/**
+ ****************************************************************************************
+ *
+ * @file ecrnx_calibration_data.h
+ *
+ * @brief Calibration Data function declarations
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ****************************************************************************************
+ */
+
+#ifndef _ECRNX_CALIBRATION_H_
+#define _ECRNX_CALIBRATION_H_
+#include "lmac_types.h"
+/**
+ * INCLUDE FILES
+ ****************************************************************************************
+ */
+
+/**
+ * DEFINES
+ ****************************************************************************************
+ */
+#define CAL_MAC_ADDR_LEN 6
+#define CAL_FORMAT_CLASS 3
+#define CAL_MCS_CNT 10
+#define CAL_CHAN_CNT 3
+#define IS_LOW_CHAN(ch) ((ch) >= 1 && (ch) <= 4)
+#define IS_MID_CHAN(ch) ((ch) >= 5 && (ch) <= 8)
+#define IS_HIG_CHAN(ch) ((ch) >= 9 && (ch) <= 14)
+#define GAIN_DELTA_CFG_BUF_SIZE (CAL_FORMAT_MAX * CAL_MCS_CNT)
+
+
+typedef enum {
+ CHAN_LEVEL_LOW,
+ CHAN_LEVEL_MID,
+ CHAN_LEVEL_HIGH,
+ CHAN_LEVEL_MAX,
+} chan_level_e;
+
+typedef enum {
+ CAL_FORMAT_11B,
+ CAL_FORMAT_11G,
+ CAL_FORMAT_11N,
+ CAL_FORMAT_11N_40M,
+ CAL_FORMAT_11AX,
+ CAL_FORMAT_MAX
+} wifi_cal_format_e;
+
+typedef struct {
+ uint8_t gain[CHAN_LEVEL_MAX][CAL_FORMAT_MAX][CAL_MCS_CNT];
+}tx_gain_cal_t;
+
+typedef struct {
+ uint8_t fine;
+ uint8_t corase;
+ uint8_t swl;
+}cfo_cal_t;
+
+typedef struct {
+ tx_gain_cal_t tx_gain;
+ cfo_cal_t cfo_cal;
+ uint8_t mac_addr[CAL_MAC_ADDR_LEN];
+ uint32_t lol;
+}wifi_cal_data_t;
+
+/**
+ * TYPE DEFINITIONS
+ ****************************************************************************************
+ */
+
+extern s8 gain_delta[];
+extern wifi_cal_data_t cal_result;
+/**
+ * FUNCTION DECLARATIONS
+ ****************************************************************************************
+ */
+
+#endif /* _ECRNX_CALIBRATION_H_ */
diff --git a/drivers/net/wireless/eswin/fullmac/ecrnx_debugfs_custom.c b/drivers/net/wireless/eswin/fullmac/ecrnx_debugfs_custom.c
new file mode 100644
index 000000000000..d5ec98ca4bf6
--- /dev/null
+++ b/drivers/net/wireless/eswin/fullmac/ecrnx_debugfs_custom.c
@@ -0,0 +1,1171 @@
+#include "ecrnx_debugfs_custom.h"
+#include "ecrnx_debugfs_func.h"
+#include "slave_log_buf.h"
+#include <linux/file.h>
+
+#ifdef CONFIG_ECRNX_DEBUGFS
+#define DEFINE_SHOW_ATTRIBUTE(__name) \
+static int __name ## _open(struct inode *inode, struct file *file) \
+{ \
+ return single_open(file, __name ## _show, inode->i_private); \
+} \
+ \
+static const struct file_operations __name ## _fops = { \
+ .owner = THIS_MODULE, \
+ .open = __name ## _open, \
+ .read = seq_read, \
+ .llseek = seq_lseek, \
+ .release = single_release, \
+}
+
+#define DEFINE_SHOW_ATTRIBUTE_EX(__name) \
+static int __name ## _open(struct inode *inode, struct file *file) \
+{ \
+ return single_open(file, __name ## _show, inode->i_private); \
+} \
+ \
+static const struct file_operations __name ## _fops = { \
+ .owner = THIS_MODULE, \
+ .open = __name ## _open, \
+ .read = seq_read, \
+ .write = __name ## _write, \
+ .llseek = seq_lseek, \
+ .release = single_release, \
+}
+
+static long custom_sys_write(unsigned int fd, const char __user *buf, size_t count)
+{
+ long ret = -EBADF;
+ struct file *file = NULL;
+ mm_segment_t status;
+ loff_t offset;
+
+ if (!buf) {
+ printk("Write buffer was empty.\n");
+ return ret;
+ }
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+ file = fget(fd);
+ if (file) {
+ offset = file->f_pos;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)
+ ret = kernel_write(file, buf, count, (loff_t *)&offset);
+#else
+ ret = kernel_write(file, buf, count, 0);
+#endif
+ file->f_pos = offset;
+ fput(file);
+ }
+#else
+ print_hex_dump(KERN_DEBUG, DBG_PREFIX, DUMP_PREFIX_NONE, 16, 1, buf, count, false);
+#endif
+ return ret;
+}
+
+
+void seq_reg_display_u32(struct seq_file *seq, u32 addr, u32 *reg, u32 len)
+{
+ u32 i;
+
+ for (i=0; i<len; i++)
+ {
+ if (i%4 == 0)
+ seq_printf(seq, "0x%08X ", (addr+ 4*i));
+
+ seq_printf(seq, " 0x%08X", reg[i]);
+
+ if (i%4 == 3)
+ seq_printf(seq, "\n");
+ }
+
+ if (len % 4)
+ seq_printf(seq, "\n");
+}
+
+void seq_reg_display_u8(struct seq_file *seq, u32 addr, u8 *reg, u32 len)
+{
+ u32 i;
+
+ for (i=0; i<len; i++)
+ {
+ if (i%16 == 0)
+ seq_printf(seq, "0x%04X ", (addr+i)&0xFFFF);
+
+ seq_printf(seq, " %02X", reg[i]);
+
+ if (i%16 == 7)
+ seq_printf(seq, " ");
+
+ if (i%16 == 15)
+ seq_printf(seq, "\n");
+ }
+
+ if (len % 16)
+ seq_printf(seq, "\n");
+}
+void read_reg_display_u32(u32 addr, u32 *reg, u32 len)
+{
+ u32 i;
+ u32 size = len*11 + (len + 3)/4*16 + len/4;
+ u32 point = 0;
+ char * buf = NULL;
+
+ buf = kmalloc(size, GFP_ATOMIC);
+ if(buf == NULL)
+ {
+ return;
+ }
+ for (i=0; i<len; i++)
+ {
+ if (i%4 == 0)
+ {
+ sprintf(buf+point,"addr:0x%08X", (addr+ 4*i));
+ point += 15;
+ buf[point] = '\n';
+ point += 1;
+
+ }
+
+ sprintf(buf+point,"0x%08X", reg[i]);
+ point += 10;
+ buf[point] = '\n';
+ point += 1;
+
+
+ if (i%4 == 3)
+ {
+ buf[point] = '\n';
+ point += 1;
+ }
+ }
+ custom_sys_write(0,buf,size);
+ if(buf != NULL)
+ {
+ kfree(buf);
+ buf = NULL;
+ }
+
+}
+/*
+void reg_display_u32(u32 addr, u32 *reg, u32 len)
+{
+ u32 i;
+
+ for (i=0; i<len; i++)
+ {
+ if (i%4 == 0)
+ ECRNX_PRINT("0x%08X", (addr+ 4*i));
+
+ ECRNX_PRINT("0x%08X", reg[i]);
+
+ if (i%4 == 3)
+ printk("\n");
+ }
+
+ if (len % 4)
+ printk("\n");
+}
+*/
+void reg_display_u8(u32 addr, u8 *reg, u32 len)
+{
+ u32 i;
+
+ for (i=0; i<len; i++)
+ {
+ if (i%16 == 0)
+ ECRNX_PRINT("0x%04X ", (addr+i)&0xFFFF);
+
+ ECRNX_PRINT(" %02X", reg[i]);
+
+ if (i%16 == 7)
+ printk(" ");
+
+ if (i%16 == 15)
+ printk("\n");
+ }
+
+ if (len % 16)
+ printk("\n");
+}
+
+static int drv_cfg_show(struct seq_file *seq, void *v)
+{
+//#define DRV_CFG_DETAILS
+
+ u8 host_ver[32];
+
+ ecrnx_host_ver_get(host_ver);
+
+ seq_printf(seq, "Kernel Version : %d.%d.%d\n", LINUX_VERSION_CODE >> 16, (LINUX_VERSION_CODE >> 8)&0xFF, LINUX_VERSION_CODE&0xFF);
+ seq_printf(seq, "Driver Version : %s\n", host_ver);
+ seq_printf(seq, "------------------------------------------------\n");
+
+#ifdef CONFIG_ECRNX_ESWIN
+ seq_printf(seq, "CONFIG_ECRNX_ESWIN=1\n");
+#else
+ #ifdef DRV_CFG_DETAILS
+ seq_printf(seq, "CONFIG_ECRNX_ESWIN=0\n");
+ #endif
+#endif
+
+
+#ifdef CONFIG_ECRNX_FULLMAC
+ seq_printf(seq, "CONFIG_ECRNX_FULLMAC=1\n");
+#else
+ #ifdef DRV_CFG_DETAILS
+ seq_printf(seq, "CONFIG_ECRNX_FULLMAC=0\n");
+ #endif
+#endif
+
+
+#ifdef CONFIG_ECRNX_SOFTMAC
+ seq_printf(seq, "CONFIG_ECRNX_SOFTMAC=1\n");
+#else
+ #ifdef DRV_CFG_DETAILS
+ seq_printf(seq, "CONFIG_ECRNX_SOFTMAC=0\n");
+ #endif
+#endif
+
+
+#ifdef CONFIG_ECRNX_ESWIN_USB
+ seq_printf(seq, "CONFIG_ECRNX_ESWIN_USB=1\n");
+#else
+ #ifdef DRV_CFG_DETAILS
+ seq_printf(seq, "CONFIG_ECRNX_ESWIN_USB=0\n");
+ #endif
+#endif
+
+
+#ifdef CONFIG_ECRNX_ESWIN_SDIO
+ seq_printf(seq, "CONFIG_ECRNX_ESWIN_SDIO=1\n");
+#else
+ #ifdef DRV_CFG_DETAILS
+ seq_printf(seq, "CONFIG_ECRNX_ESWIN_SDIO=0\n");
+ #endif
+#endif
+
+
+#ifdef CONFIG_ECRNX_HE
+ seq_printf(seq, "CONFIG_ECRNX_HE=1\n");
+#else
+ #ifdef DRV_CFG_DETAILS
+ seq_printf(seq, "CONFIG_ECRNX_HE=0\n");
+ #endif
+#endif
+
+
+#ifdef CONFIG_ECRNX_P2P
+ seq_printf(seq, "CONFIG_ECRNX_P2P=1\n");
+#else
+ #ifdef DRV_CFG_DETAILS
+ seq_printf(seq, "CONFIG_ECRNX_P2P=0\n");
+ #endif
+#endif
+
+
+#ifdef CONFIG_ECRNX_FHOST
+ seq_printf(seq, "CONFIG_ECRNX_FHOST=1\n");
+#else
+ #ifdef DRV_CFG_DETAILS
+ seq_printf(seq, "CONFIG_ECRNX_FHOST=0\n");
+ #endif
+#endif
+
+
+#ifdef CONFIG_ECRNX_SDM
+ seq_printf(seq, "CONFIG_ECRNX_SDM=1\n");
+#else
+ #ifdef DRV_CFG_DETAILS
+ seq_printf(seq, "CONFIG_ECRNX_SDM=0\n");
+ #endif
+#endif
+
+
+#ifdef CONFIG_ECRNX_TL4
+ seq_printf(seq, "CONFIG_ECRNX_TL4=1\n");
+#else
+ #ifdef DRV_CFG_DETAILS
+ seq_printf(seq, "CONFIG_ECRNX_TL4=0\n");
+ #endif
+#endif
+
+
+#ifdef CONFIG_ECRNX_MUMIMO_TX
+ seq_printf(seq, "CONFIG_ECRNX_MUMIMO_TX=1\n");
+#else
+ #ifdef DRV_CFG_DETAILS
+ seq_printf(seq, "CONFIG_ECRNX_MUMIMO_TX=0\n");
+ #endif
+#endif
+
+
+#ifdef CONFIG_ECRNX_RADAR
+ seq_printf(seq, "CONFIG_ECRNX_RADAR=1\n");
+#else
+ #ifdef DRV_CFG_DETAILS
+ seq_printf(seq, "CONFIG_ECRNX_RADAR=0\n");
+ #endif
+#endif
+
+
+#ifdef CONFIG_ECRNX_BCMC
+ seq_printf(seq, "CONFIG_ECRNX_BCMC=1\n");
+#else
+ #ifdef DRV_CFG_DETAILS
+ seq_printf(seq, "CONFIG_ECRNX_BCMC=0\n");
+ #endif
+#endif
+
+
+#ifdef CONFIG_ECRNX_MON_DATA
+ seq_printf(seq, "CONFIG_ECRNX_MON_DATA=1\n");
+#else
+ #ifdef DRV_CFG_DETAILS
+ seq_printf(seq, "CONFIG_ECRNX_MON_DATA=0\n");
+ #endif
+#endif
+
+
+#ifdef CONFIG_ECRNX_SW_PROFILING
+ seq_printf(seq, "CONFIG_ECRNX_SW_PROFILING=1\n");
+#else
+ #ifdef DRV_CFG_DETAILS
+ seq_printf(seq, "CONFIG_ECRNX_SW_PROFILING=0\n");
+ #endif
+#endif
+
+
+#ifdef CONFIG_ECRNX_DBG
+ seq_printf(seq, "CONFIG_ECRNX_DBG=1\n");
+#else
+ #ifdef DRV_CFG_DETAILS
+ seq_printf(seq, "CONFIG_ECRNX_DBG=0\n");
+ #endif
+#endif
+
+
+#ifdef CFG_WOW_SUPPORT
+ seq_printf(seq, "CFG_WOW_SUPPORT=1\n");
+#else
+ #ifdef DRV_CFG_DETAILS
+ seq_printf(seq, "CFG_WOW_SUPPORT=0\n");
+ #endif
+#endif
+
+ return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(drv_cfg);
+
+
+static int log_level_show(struct seq_file *seq, void *v)
+{
+ LOG_CTL_ST log;
+
+ if (0 == ecrnx_log_level_get(&log))
+ seq_printf(seq, "log_level:%d dir:%d\n", log.level, log.dir);
+ else
+ seq_printf(seq, "\n");
+
+ return 0;
+}
+
+static ssize_t log_level_write(struct file *filp, const char __user *buffer,
+ size_t count, loff_t *ppos)
+{
+ u8 kbuf[128]={0};
+ u32 level,dir;
+
+ if (count > 6) // 255:0\n
+ return -1;
+
+ if (0 != copy_from_user(kbuf, buffer, count))
+ return -1;
+ sscanf(kbuf, "%d:%d\n", &level, &dir);
+
+ if (dir > 1)
+ return -1;
+
+ ecrnx_fw_log_level_set(level, dir);
+
+ return count;
+}
+DEFINE_SHOW_ATTRIBUTE_EX(log_level);
+
+static ssize_t mac_log_read(struct file *file , char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ring_buffer *buf_handle;
+ int len = 0;
+ char buf[512] = {0};
+ if (false == ecrnx_log_host_enable())
+ return 0;
+#ifdef CONFIG_ECRNX_ESWIN_USB
+ buf_handle = usb_dbg_buf_get();
+ ring_buffer_scrolling_display(buf_handle,true);
+ while(buf_handle->show)
+ {
+ len = ring_buffer_len(buf_handle);
+ len = min(len, 512);
+ //ECRNX_PRINT("mac_log_read len:%d %d %d", len,buf_handle->write_point, buf_handle->read_point);
+ if(len > 0)
+ {
+ ring_buffer_get(buf_handle,buf,len);
+ custom_sys_write(0,buf,len);
+ msleep(35);
+ }
+ else
+ {
+ msleep(500);
+ }
+ cond_resched();
+ }
+#endif
+
+ return count;
+}
+
+static ssize_t mac_log_write(struct file *filp, const char __user *buffer,
+ size_t count, loff_t *ppos)
+{
+ u8 kbuf[128]={0};
+ u32 show;
+ struct ring_buffer *buf_handle;
+
+ if (count > 2) // 255:0\n
+ return -1;
+
+ if (0 != copy_from_user(kbuf, buffer, count))
+ return -1;
+ sscanf(kbuf, "%d:%d\n", &show);
+#ifdef CONFIG_ECRNX_ESWIN_USB
+ if (show == 0)
+ {
+ buf_handle = usb_dbg_buf_get();
+ ring_buffer_scrolling_display(buf_handle, false);
+ }
+#endif
+
+ return count;
+}
+
+static const struct file_operations new_mac_log_fops = { \
+ .read = mac_log_read, \
+ .write = mac_log_write, \
+};
+
+static int ver_info_show(struct seq_file *seq, void *v)
+{
+ u8 host_ver[32];
+ u8 build_time[32];
+
+ ecrnx_host_ver_get(host_ver);
+ ecrnx_build_time_get(build_time);
+
+ seq_printf(seq, "%s\n", host_ver);
+ seq_printf(seq, "build time: %s\n", build_time);
+
+ return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(ver_info);
+
+extern bin_head_data head;
+static int fw_info_show(struct seq_file *seq, void *v)
+{
+ struct tm timenow = {0};
+
+ localtime(&timenow, head.UTC_time);
+ if(head.fw_Info != NULL)
+ {
+ seq_printf(seq, "sdk_verson:%s\n", head.fw_Info);
+ }
+ seq_printf(seq, "fw_crc:0x%8x\n", head.crc32);
+ seq_printf(seq, "fw_build_time: %04d-%02d-%02d %02d:%02d:%02d\n",(int)timenow.tm_year,timenow.tm_mon,timenow.tm_mday,timenow.tm_hour,timenow.tm_min,timenow.tm_sec);
+ return 0;
+}
+
+DEFINE_SHOW_ATTRIBUTE(fw_info);
+
+struct dentry *ecr6600u_dir = NULL;
+struct dentry *drv_cfg, *log_level, *mac_log, *ver_info, *fw_info;
+
+int ecrnx_debugfs_info_init(void *private_data)
+{
+ ecr6600u_dir = debugfs_create_dir("ecr6600u", NULL);
+ if (!ecr6600u_dir)
+ goto Fail;
+
+ drv_cfg = debugfs_create_file("drv_cfg", 0444, ecr6600u_dir, private_data, &drv_cfg_fops);
+ if (!drv_cfg)
+ goto Fail;
+
+ log_level = debugfs_create_file("log_level", 0666, ecr6600u_dir, private_data, &log_level_fops);
+ if (!log_level)
+ goto Fail;
+
+ mac_log = debugfs_create_file("maclog", 0444, ecr6600u_dir, private_data, &new_mac_log_fops);
+ if (!mac_log)
+ goto Fail;
+
+ ver_info = debugfs_create_file("ver_info", 0444, ecr6600u_dir, private_data, &ver_info_fops);
+ if (!ver_info)
+ goto Fail;
+
+ fw_info = debugfs_create_file("fw_info", 0444, ecr6600u_dir, private_data, &fw_info_fops);
+ if (!fw_info)
+ goto Fail;
+ return 0;
+
+Fail:
+ return -ENOENT;
+}
+
+
+static int rf_info_show(struct seq_file *seq, void *v)
+{
+ RF_INFO_ST cur, oper;
+
+ if (0 == ecrnx_rf_info_get(seq, IF_STA, &cur, &oper))
+ {
+ seq_printf(seq, "cur_ch=%d, cur_bw=%d, cur_ch_offset=%d\n", cur.ch, cur.bw, cur.ch_offset);
+ // seq_printf(seq, "oper_ch=%d, oper_bw=%d, oper_ch_offset=%d\n", oper.ch, oper.bw, oper.ch_offset);
+ }
+
+ return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(rf_info);
+
+static int mac_reg_dump_show(struct seq_file *seq, void *v)
+{
+ ecrnx_mac_reg_dump(seq);
+
+ return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(mac_reg_dump);
+
+static int rf_reg_dump_show(struct seq_file *seq, void *v)
+{
+ ecrnx_rf_reg_dump(seq);
+
+ return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(rf_reg_dump);
+
+static int bb_reg_dump_show(struct seq_file *seq, void *v)
+{
+ ecrnx_bb_reg_dump(seq);
+
+ return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(bb_reg_dump);
+
+static int country_code_show(struct seq_file *seq, void *v)
+{
+ char country_code[3];
+ if (0 == ecrnx_country_code_get(seq, country_code))
+ {
+ seq_printf(seq, "%s\n", country_code);
+ }
+ else
+ {
+ seq_printf(seq, "UNSPECIFIED\n");
+ }
+
+ return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(country_code);
+
+static int mac_addr_show(struct seq_file *seq, void *v)
+{
+ u8 mac_addr_info[128];
+
+ if (0 == ecrnx_mac_addr_get_ex(seq, mac_addr_info))
+ {
+ seq_printf(seq, "%s", mac_addr_info);
+ }
+ else
+ {
+ seq_printf(seq, "--\n");
+ }
+
+ return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(mac_addr);
+
+static int efuse_map_show(struct seq_file *seq, void *v)
+{
+ ecrnx_efuse_map_dump(seq);
+
+ return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(efuse_map);
+
+static int read_reg_show(struct seq_file *seq, void *v)
+{
+ return 0;
+}
+static ssize_t read_reg_write(struct file *filp, const char __user *buffer,
+ size_t count, loff_t *ppos)
+{
+ u8 kbuf[128]={0};
+ u32 addr, len;
+ int ret_code;
+ char * read_reg_buf = NULL;
+
+ if (count > sizeof(kbuf))
+ return -1;
+
+ if (0 != copy_from_user(kbuf, buffer, count))
+ return -1;
+
+ ret_code = sscanf(kbuf, "0x%x %d\n", &addr, &len);
+ if (ret_code != 2)
+ {
+ ECRNX_ERR("Parameter error.\n");
+ return -1;
+ }
+
+ if ( addr%4 )
+ {
+ ECRNX_ERR("Address is invalid.\n");
+ return -1;
+ }
+
+ if (0 == ecrnx_slave_reg_read(addr, reg_buf, len * sizeof(reg_buf[0])))
+ read_reg_display_u32(addr, reg_buf, len);
+ return count;
+}
+DEFINE_SHOW_ATTRIBUTE_EX(read_reg);
+static int write_reg_show(struct seq_file *seq, void *v)
+{
+ return 0;
+}
+
+static ssize_t write_reg_write(struct file *filp, const char __user *buffer,
+ size_t count, loff_t *ppos)
+{
+ u8 kbuf[128]={0};
+ u32 addr, data;
+ int ret_code,ret = count;
+ char* write_reg_result = NULL;
+
+ if (count > sizeof(kbuf))
+ ret = -1;
+
+ if (0 != copy_from_user(kbuf, buffer, count))
+ ret = -1;
+
+ ret_code = sscanf(kbuf, "0x%x 0x%x\n", &addr, &data);
+ if (ret_code != 2)
+ {
+ write_reg_result = "Parameter error.\n";
+ ret = -1;
+ }
+
+ if ( addr%4 )
+ {
+ write_reg_result = "Address is invalid.\n";
+ ret = -1;
+ }
+
+ if (0 != ecrnx_slave_reg_write(addr, data, 1))
+ {
+ write_reg_result = "Write error.\n";
+ ret = -1;
+ }
+ else
+ write_reg_result = "Write success.\n";
+ if(write_reg_result != NULL)
+ {
+ custom_sys_write(0,write_reg_result,strlen(write_reg_result));
+ }
+ return ret;
+}
+DEFINE_SHOW_ATTRIBUTE_EX(write_reg);
+
+static int rf_tx_rx_info_show(struct seq_file *seq, void *v)
+{
+// u8 *resp = NULL;
+// u8 cli[]="rf_get_rssi 0 1 2";
+
+// ecrnx_slave_cli_send(cli, resp);
+
+ return 0;
+}
+
+static ssize_t rf_tx_rx_info_write(struct file *filp, const char __user *buffer,
+ size_t count, loff_t *ppos)
+{
+ u8 kbuf[128]={0};
+
+ if (count > sizeof(kbuf))
+ return -1;
+
+ if (0 != copy_from_user(kbuf, buffer, count))
+ return -1;
+
+ cli_cmd_parse(kbuf);
+
+ return count;
+}
+
+DEFINE_SHOW_ATTRIBUTE_EX(rf_tx_rx_info);
+
+struct dentry *hw_dir = NULL;
+struct dentry *hw_rf_info, *hw_mac_reg_dump, *hw_rf_reg_dump, *hw_bb_reg_dump, *hw_country_code, *hw_mac_addr;
+struct dentry *hw_efuse_map, *hw_read_reg, *hw_write_reg, *hw_rf_tx_rx_info;
+
+int ecrnx_debugfs_hw_init(void *private_data)
+{
+ if (!ecr6600u_dir)
+ return -ENOENT;
+
+ hw_dir = debugfs_create_dir("hw", ecr6600u_dir);
+ if (!hw_dir)
+ return -ENOENT;
+
+ hw_rf_info = debugfs_create_file("rf_info", 0444, hw_dir, private_data, &rf_info_fops);
+ if (!hw_rf_info)
+ goto Fail;
+
+ hw_mac_reg_dump = debugfs_create_file("mac_reg_dump", 0444, hw_dir, private_data, &mac_reg_dump_fops);
+ if (!hw_mac_reg_dump)
+ goto Fail;
+
+ hw_rf_reg_dump = debugfs_create_file("rf_reg_dump", 0444, hw_dir, private_data, &rf_reg_dump_fops);
+ if (!hw_rf_reg_dump)
+ goto Fail;
+
+ hw_bb_reg_dump = debugfs_create_file("bb_reg_dump", 0444, hw_dir, private_data, &bb_reg_dump_fops);
+ if (!hw_bb_reg_dump)
+ goto Fail;
+
+ hw_country_code = debugfs_create_file("country_code", 0444, hw_dir, private_data, &country_code_fops);
+ if (!hw_country_code)
+ goto Fail;
+
+ hw_mac_addr = debugfs_create_file("mac_addr", 0444, hw_dir, private_data, &mac_addr_fops);
+ if (!hw_mac_addr)
+ goto Fail;
+
+ hw_efuse_map = debugfs_create_file("efuse_map", 0444, hw_dir, private_data, &efuse_map_fops);
+ if (!hw_efuse_map)
+ goto Fail;
+
+ hw_read_reg = debugfs_create_file("read_reg", 0222, hw_dir, private_data, &read_reg_fops);
+ if (!hw_read_reg)
+ goto Fail;
+
+ hw_write_reg = debugfs_create_file("write_reg", 0222, hw_dir, private_data, &write_reg_fops);
+ if (!hw_write_reg)
+ goto Fail;
+
+ hw_rf_tx_rx_info = debugfs_create_file("rf_tx_rx_info", 0666, hw_dir, private_data, &rf_tx_rx_info_fops);
+ if (!hw_rf_tx_rx_info)
+ goto Fail;
+
+ return 0;
+
+Fail:
+ debugfs_remove_recursive(hw_dir);
+ hw_dir = NULL;
+ return -ENOENT;
+}
+
+#ifndef MAC_FMT
+#define MAC_FMT "%02x:%02x:%02x:%02x:%02x:%02x"
+#endif
+#ifndef MAC_ARG
+#define MAC_ARG(x) ((u8 *)(x))[0], ((u8 *)(x))[1], ((u8 *)(x))[2], ((u8 *)(x))[3], ((u8 *)(x))[4], ((u8 *)(x))[5]
+#endif
+
+static int survey_info_show(struct seq_file *seq, void *v)
+{
+ int index = 0;
+ struct ecrnx_hw *ecrnx_hw;
+ struct ecrnx_debugfs_survey_info_tbl *entry;
+
+ if (seq->private != NULL)
+ ecrnx_hw = seq->private;
+ else
+ return -1;
+
+//seq_printf(seq, "index bssid ch RSSI SdBm Noise age flag ssid \n");
+ seq_printf(seq, "%5s %-17s %3s %-3s %-4s %-4s %5s %32s %32s\n", "index", "bssid", "ch", "RSSI", "SdBm", "Noise", "age", "flag", "ssid");
+
+ list_for_each_entry(entry, &ecrnx_hw->debugfs_survey_info_tbl_ptr, list) {
+ seq_printf(seq, "%5d "MAC_FMT" %3d %3d %4d %4d %5d %32s %32s\n",
+ ++index,
+ MAC_ARG(entry->bssid),
+ entry->ch,
+ entry->rssi,
+ entry->sdbm,
+ entry->noise,
+ entry->age,
+ entry->flag,
+ entry->ssid);
+ }
+
+ return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(survey_info);
+
+static int sta_info_show(struct seq_file *seq, void *v)
+{
+ s8 noise_dbm;
+ struct cfg80211_chan_def chandef;
+ u32 bw_array[]={20, 20, 40, 80, 80, 160, 5, 10};
+
+ if (0 == ecrnx_signal_level_get(seq, IF_STA, &noise_dbm))
+ seq_printf(seq, "signal level : %d dBm\n", noise_dbm);
+ else
+ seq_printf(seq, "signal level : \n");
+
+ if (0 == ecrnx_channel_get(seq, IF_STA, &chandef))
+ {
+ seq_printf(seq, "frequency : %d MHz\n", chandef.center_freq1);
+ seq_printf(seq, "bandwidth : %d MHz\n", bw_array[chandef.width]);
+ }
+ else
+ {
+ seq_printf(seq, "frequency : \n");
+ seq_printf(seq, "bandwidth : \n");
+ }
+
+ return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(sta_info);
+
+static int ht_info_show(struct seq_file *seq, void *v)
+{
+ struct cfg80211_chan_def chandef;
+ u32 bw_array[]={20, 20, 40, 80, 80, 160, 5, 10};
+
+ if (0 == ecrnx_channel_get(seq, IF_STA, &chandef))
+ seq_printf(seq, "bandwidth : %d MHz\n", bw_array[chandef.width]);
+ else
+ seq_printf(seq, "bandwidth : \n");
+
+ return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(ht_info);
+
+
+struct dentry *wlan0_dir = NULL;
+struct dentry *wlan0_survey_info, *wlan0_sta_info, *wlan0_ht_info;;
+
+int ecrnx_debugfs_wlan0_init(void *private_data)
+{
+ if (!ecr6600u_dir)
+ return -ENOENT;
+
+ wlan0_dir = debugfs_create_dir("wlan0", ecr6600u_dir);
+ if (!wlan0_dir)
+ return -ENOENT;
+
+ wlan0_survey_info = debugfs_create_file("survey_info", 0444, wlan0_dir, private_data, &survey_info_fops);
+ if (!wlan0_survey_info)
+ goto Fail;
+
+ wlan0_sta_info = debugfs_create_file("sta_info", 0444, wlan0_dir, private_data, &sta_info_fops);
+ if (!wlan0_sta_info)
+ goto Fail;
+
+ wlan0_ht_info = debugfs_create_file("ht_info", 0444, wlan0_dir, private_data, &ht_info_fops);
+ if (!wlan0_ht_info)
+ goto Fail;
+
+ return 0;
+
+Fail:
+ debugfs_remove_recursive(wlan0_dir);
+ wlan0_dir = NULL;
+ return -ENOENT;
+}
+
+static int ap_info_show(struct seq_file *seq, void *v)
+{
+ struct cfg80211_chan_def chandef;
+ u32 bw_array[]={20, 20, 40, 80, 80, 160, 5, 10};
+ u8 ssid[IEEE80211_MAX_SSID_LEN];
+ u8 sta_mac[NX_REMOTE_STA_MAX][ETH_ALEN+1];
+ u32 i;
+
+ seq_printf(seq, "AP Vendor ESWIN\n");
+
+ seq_printf(seq, "ap info\n");
+ if (0 == ecrnx_ssid_get(seq, IF_AP, ssid))
+ seq_printf(seq, "ssid : %s\n", ssid);
+ else
+ seq_printf(seq, "ssid : \n");
+
+ if (0 == ecrnx_channel_get(seq, IF_AP, &chandef))
+ {
+ seq_printf(seq, "cur_channel=%d, cur_bwmode=%d(%dMHz), cur_ch_offset=0\n",
+ (chandef.center_freq1 - 2412)/5 + 1, chandef.width, bw_array[chandef.width]);
+ }
+ else
+ {
+ seq_printf(seq, "cur_channel=, cur_bwmode=(MHz), cur_ch_offset=\n");
+ }
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0))
+ if ((chandef.width <= NL80211_CHAN_WIDTH_10) && (chandef.width >= NL80211_CHAN_WIDTH_20_NOHT))
+ {
+ if (NL80211_CHAN_WIDTH_20_NOHT == chandef.width)
+ {
+ seq_printf(seq, "ht_en=0\n");
+ }
+ else
+ {
+ seq_printf(seq, "ht_en=1\n");
+ }
+ }
+ else
+ {
+ seq_printf(seq, "ht_en=0\n");
+ }
+#endif
+
+ seq_printf(seq, "wireless_mode=0xb(B/G/N), rtsen=0, cts2slef=0\n");
+ seq_printf(seq, "state=0x10, aid=0, macid=32, raid=0\n");
+ seq_printf(seq, "qos_en=1, init_rate=0\n");
+ seq_printf(seq, "bwmode=0, ch_offset=0, sgi_20m=1,sgi_40m=1\n");
+ seq_printf(seq, "ampdu_enable = 1\n");
+ seq_printf(seq, "agg_enable_bitmap=0, candidate_tid_bitmap=0\n");
+ seq_printf(seq, "ldpc_cap=0x0, stbc_cap=0x0, beamform_cap=0x0\n");
+
+
+ seq_printf(seq, "\n");
+ seq_printf(seq, "station info\n");
+ memset(sta_mac, 0x00, sizeof(sta_mac));
+ if (0 == ecrnx_sta_mac_get(seq, IF_AP, sta_mac))
+ {
+ for (i=0; i<NX_REMOTE_STA_MAX; i++)
+ {
+ if (sta_mac[i][0])
+ seq_printf(seq, "sta's macaddr:%02X:%02X:%02X:%02X:%02X:%02X\n", sta_mac[i][1], sta_mac[i][2],
+ sta_mac[i][3], sta_mac[i][4], sta_mac[i][5], sta_mac[i][6]);
+ }
+ }
+
+ return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(ap_info);
+
+static int sta_info_in_ap_show(struct seq_file *seq, void *v)
+{
+ struct ecrnx_debugfs_sta *sta;
+
+ if (seq->private == NULL)
+ ECRNX_ERR("error sta_info_in_ap_show\n");
+
+ sta = seq->private;
+
+ seq_printf(seq, "cur_channel=%d, cur_bwmode=%d\n", sta->ch_idx, sta->width);
+ //seq_printf(m, "wireless_mode=0x%x(%s), rtsen=%d, cts2slef=%d\n", psta->wireless_mode, wl_mode, psta->rtsen, psta->cts2self);
+ seq_printf(seq, "aid=%d\n", sta->aid);
+
+ seq_printf(seq, "qos_en=%d, ht_en=%d\n", sta->qos, sta->ht);
+ seq_printf(seq, "sgi_20m=%d,sgi_40m=%d\n", sta->sgi_20m, sta->sgi_40m);
+ seq_printf(seq, "ampdu_enable = %d\n", sta->ampdu_enable);
+ seq_printf(seq, "agg_enable_bitmap=%x, candidate_tid_bitmap=%x\n", sta->agg_enable_bitmap, sta->candidate_tid_bitmap);
+ seq_printf(seq, "ldpc_cap=0x%x, stbc_cap=0x%x, beamform_cap=0x%x\n", sta->ldpc_cap, sta->stbc_cap, sta->beamform_cap);
+
+ return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(sta_info_in_ap);
+
+struct dentry *ap0_dir = NULL;
+struct dentry *ap0_ap_info;
+struct dentry *stas[NX_REMOTE_STA_MAX];
+
+int ecrnx_debugfs_ap0_init(void *private_data)
+{
+ if (!ecr6600u_dir)
+ return -ENOENT;
+
+ ap0_dir = debugfs_create_dir("ap0", ecr6600u_dir);
+ if (!ap0_dir)
+ return -ENOENT;
+
+ ap0_ap_info = debugfs_create_file("ap_info", 0444, ap0_dir, private_data, &ap_info_fops);
+ if (!ap0_ap_info)
+ goto Fail;
+
+ return 0;
+
+Fail:
+ debugfs_remove_recursive(ap0_dir);
+ ap0_dir = NULL;
+ return -ENOENT;
+}
+
+void ecrnx_debugfs_sta_in_ap_init(void *private_data)
+{
+ int index;
+ char file_name[19];
+ struct ecrnx_debugfs_sta *sta;
+
+ sta = private_data;
+ index = sta->sta_idx;
+
+ sprintf(file_name, "%02x-%02x-%02x-%02x-%02x-%02x", MAC_ARG(sta->mac_addr));
+ stas[index] = debugfs_create_file(file_name, 0444, ap0_dir, private_data, &sta_info_in_ap_fops);
+ ECRNX_PRINT("%s:file_name: %s, sta idx:%d,stas:0x%p \n", __func__, file_name, sta->sta_idx, stas[index]);
+}
+
+void ecrnx_debugfs_sta_in_ap_del(u8 sta_idx)
+{
+ struct dentry *dentry_sta;
+ struct ecrnx_debugfs_sta *debugfs_sta;
+
+ ECRNX_PRINT("%s: sta_idx:%d, stas:0x%p \n", __func__, sta_idx, stas[sta_idx]);
+ dentry_sta = stas[sta_idx];
+ if(!dentry_sta)
+ {
+ ECRNX_ERR("error sta_idx!!!\n");
+ return;
+ }
+
+ if (dentry_sta->d_inode)
+ {
+ debugfs_sta = dentry_sta->d_inode->i_private;
+ ECRNX_DBG("%s: dentry_sta->d_inode:0x%p, debugfs_sta:0x%p \n", __func__, dentry_sta->d_inode, debugfs_sta);
+ if (debugfs_sta)
+ {
+ debugfs_remove(dentry_sta);
+ kfree(debugfs_sta);
+ }
+ }
+
+ stas[sta_idx] = NULL;
+}
+
+static int p2p0_survey_info_show(struct seq_file *seq, void *v)
+{
+ survey_info_show(seq, v);
+
+ return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(p2p0_survey_info);
+
+static int p2p_info_show(struct seq_file *seq, void *v)
+{
+ s8 noise_dbm;
+ struct cfg80211_chan_def chandef;
+ u32 bw_array[]={20, 20, 40, 80, 80, 160, 5, 10};
+
+ if (0 == ecrnx_signal_level_get(seq, IF_P2P, &noise_dbm))
+ seq_printf(seq, "signal level : %d dBm\n", noise_dbm);
+ else
+ seq_printf(seq, "signal level : \n");
+
+ if (0 == ecrnx_channel_get(seq, IF_P2P, &chandef))
+ {
+ seq_printf(seq, "frequency : %d MHz\n", chandef.center_freq1);
+ seq_printf(seq, "bandwidth : %d MHz\n", bw_array[chandef.width]);
+ }
+ else
+ {
+ seq_printf(seq, "frequency : \n");
+ seq_printf(seq, "bandwidth : \n");
+ }
+
+ if (NL80211_IFTYPE_P2P_CLIENT == ecrnx_p2p_role_get(seq, IF_P2P))
+ {
+ seq_printf(seq, "type : P2P-client\n");
+ }
+ else if(NL80211_IFTYPE_P2P_GO == ecrnx_p2p_role_get(seq, IF_P2P))
+ {
+ seq_printf(seq, "type : P2P-GO\n");
+ }
+ else
+ {
+ seq_printf(seq, "type : \n");
+ }
+
+ return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(p2p_info);
+
+static int p2p0_ht_info_show(struct seq_file *seq, void *v)
+{
+ struct cfg80211_chan_def chandef;
+ u32 bw_array[]={20, 20, 40, 80, 80, 160, 5, 10};
+
+ if (0 == ecrnx_channel_get(seq, IF_P2P, &chandef))
+ seq_printf(seq, "bandwidth : %d MHz\n", bw_array[chandef.width]);
+ else
+ seq_printf(seq, "bandwidth : \n");
+
+ return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(p2p0_ht_info);
+
+struct dentry *p2p0_dir = NULL;
+struct dentry *p2p0_survey_info, *p2p0_p2p_info, *p2p0_ht_info;
+
+int ecrnx_debugfs_p2p0_init(void *private_data)
+{
+ if (!ecr6600u_dir)
+ return -ENOENT;
+
+ p2p0_dir = debugfs_create_dir("p2p0", ecr6600u_dir);
+ if (!p2p0_dir)
+ return -ENOENT;
+
+ p2p0_survey_info = debugfs_create_file("survey_info", 0444, p2p0_dir, private_data, &p2p0_survey_info_fops);
+ if (!p2p0_survey_info)
+ goto Fail;
+
+ p2p0_p2p_info = debugfs_create_file("p2p_info", 0444, p2p0_dir, private_data, &p2p_info_fops);
+ if (!p2p0_p2p_info)
+ goto Fail;
+
+ p2p0_ht_info = debugfs_create_file("ht_info", 0444, p2p0_dir, private_data, &p2p0_ht_info_fops);
+ if (!p2p0_ht_info)
+ goto Fail;
+
+ return 0;
+
+Fail:
+ debugfs_remove_recursive(p2p0_dir);
+ p2p0_dir = NULL;
+ return -ENOENT;
+}
+
+void exrnx_debugfs_sruvey_info_tbl_init(void *private_data)
+{
+ struct ecrnx_hw *ecrnx_hw = (struct ecrnx_hw*)private_data;
+ INIT_LIST_HEAD(&ecrnx_hw->debugfs_survey_info_tbl_ptr);
+}
+
+int ecrnx_debugfs_init(void *private_data)
+{
+ ecrnx_debugfs_info_init(private_data);
+ ecrnx_debugfs_hw_init(private_data);
+ ecrnx_debugfs_wlan0_init(private_data);
+ ecrnx_debugfs_ap0_init(private_data);
+ ecrnx_debugfs_p2p0_init(private_data);
+ exrnx_debugfs_sruvey_info_tbl_init(private_data);
+
+ memset(&debugfs_info, 0, sizeof(debugfs_info_t));
+ init_waitqueue_head(&debugfs_resp.rxdataq);
+
+ return 0;
+}
+#ifdef CONFIG_ECRNX_ESWIN_USB
+extern struct ring_buffer buf_handle;
+#endif
+
+void ecrnx_debugfs_exit(void)
+{
+ if (ecr6600u_dir != NULL)
+ debugfs_remove_recursive(ecr6600u_dir);
+#ifdef CONFIG_ECRNX_ESWIN_USB
+ ring_buffer_deinit(&buf_handle);
+#endif
+
+}
+#endif
+
diff --git a/drivers/net/wireless/eswin/fullmac/ecrnx_debugfs_custom.h b/drivers/net/wireless/eswin/fullmac/ecrnx_debugfs_custom.h
new file mode 100644
index 000000000000..244464a094bb
--- /dev/null
+++ b/drivers/net/wireless/eswin/fullmac/ecrnx_debugfs_custom.h
@@ -0,0 +1,20 @@
+#ifndef __ECRNX_DEBUGFS_CUSTOM_H_
+#define __ECRNX_DEBUGFS_CUSTOM_H_
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/seq_file.h>
+
+#include "lmac_types.h"
+#include "ecrnx_debugfs_func.h"
+#include "ecrnx_compat.h"
+
+
+int ecrnx_debugfs_init(void *private_data);
+void ecrnx_debugfs_exit(void);
+void ecrnx_debugfs_sta_in_ap_init(void *private_data);
+void ecrnx_debugfs_sta_in_ap_del(u8 sta_idx);
+
+#endif
+
+
diff --git a/drivers/net/wireless/eswin/fullmac/ecrnx_debugfs_func.c b/drivers/net/wireless/eswin/fullmac/ecrnx_debugfs_func.c
new file mode 100644
index 000000000000..f9a27a39f555
--- /dev/null
+++ b/drivers/net/wireless/eswin/fullmac/ecrnx_debugfs_func.c
@@ -0,0 +1,995 @@
+#include "ecrnx_debugfs_func.h"
+#include "ecrnx_debugfs_custom.h"
+
+#ifdef CONFIG_ECRNX_DEBUGFS
+
+debugfs_info_t debugfs_info;
+debugfs_resp_t debugfs_resp;
+
+u32 reg_buf[512]={0};
+
+REG_MAP_ST mac_reg_table[]={
+ {"INTC_IRQ_STATUS_ADDR", 0x00510000},
+ {"INTC_IRQ_UNMASK_SET_ADDR", 0x00510010},
+ {"INTC_IRQ_INDEX_ADDR", 0x00510040},
+ {"NXMAC_MAC_ADDR_LOW_ADDR", 0x00610010},
+ {"NXMAC_MAC_ADDR_HI_ADDR", 0x00610014},
+ {"NXMAC_STATE_CNTRL_ADDR", 0x00610038},
+ {"NXMAC_MAC_CNTRL_1_ADDR", 0x0061004c},
+ {"NXMAC_RX_CNTRL_ADDR", 0x00610060},
+ {"NXMAC_MAX_POWER_LEVEL_ADDR", 0x006100a0},
+ {"NXMAC_TIMINGS_1_ADDR", 0x006100e4},
+ {"NXMAC_RX_CNTRL_2_ADDR", 0x0061010c},
+ {"NXMAC_MONOTONIC_COUNTER_2_LO_ADDR", 0x00610120},
+ {"NXMAC_MONOTONIC_COUNTER_2_HI_ADDR", 0x00610124},
+ {"NXMAC_MAX_RX_LENGTH_ADDR", 0x00610150},
+ {"NXMAC_GEN_INT_STATUS_ADDR", 0x0061806c},
+ {"NXMAC_GEN_INT_ENABLE_ADDR", 0x00618074},
+ {"NXMAC_TX_RX_INT_STATUS_ADDR", 0x00618078},
+ {"NXMAC_TX_RX_INT_ENABLE_ADDR", 0x00618080},
+ {"NXMAC_DMA_CNTRL_SET_ADDR", 0x00618180},
+ {"NXMAC_DMA_STATUS_1_ADDR", 0x00618188},
+ {"NXMAC_DMA_STATUS_2_ADDR", 0x0061818c},
+ {"NXMAC_DMA_STATUS_3_ADDR", 0x00618190},
+ {"NXMAC_DMA_STATUS_4_ADDR", 0x00618194},
+ {"NXMAC_TX_BCN_HEAD_PTR_ADDR", 0x00618198},
+ {"NXMAC_TX_AC_0_HEAD_PTR_ADDR", 0x0061819c},
+ {"NXMAC_TX_AC_1_HEAD_PTR_ADDR", 0x006181a0},
+ {"NXMAC_TX_AC_2_HEAD_PTR_ADDR", 0x006181a4},
+ {"NXMAC_TX_AC_3_HEAD_PTR_ADDR", 0x006181a8},
+ {"NXMAC_RX_BUF_1_START_PTR_ADDR", 0x006181c8},
+ {"NXMAC_RX_BUF_1_END_PTR_ADDR", 0x006181cc},
+ {"NXMAC_RX_BUF_1_RD_PTR_ADDR", 0x006181d0},
+ {"NXMAC_RX_BUF_1_WR_PTR_ADDR", 0x006181d4},
+ {"NXMAC_RX_BUF_CONFIG_ADDR", 0x006181e8},
+ {"MDM_HDMCONFIG_ADDR", 0x00700000},
+};
+
+REG_MAP_ST rf_reg_table[]={
+ {"RF_REG_EXAMPLE", 0x00510000},
+};
+
+REG_MAP_ST bb_reg_table[]={
+ {"BB_REG_EXAMPLE", 0x00510000},
+};
+
+REG_MAP_ST efuse_map_table[]={
+ {"EFUSE_REG_EXAMPLE", 0x00510000},
+};
+
+int argc;
+char *argv[MAX_ARGV];
+char slave_cli_cmd[50];
+int arg_val[MAX_ARGV];
+
+
+ECRNX_CLI_TABLE_ST ecrnx_cli_tab[]={
+ {"macbyp start ", "macbyp_tx_start", 6, {{0,32},{0,32},{0,32},{0,32},{0,32},{0,32}}, cli_macbyp_start},
+ {"macbyp stop", "macbyp_tx_stop", 0, {}, cli_macbyp_stop},
+ {"rf txgain ", "rf_set_txgain", 1, {{0,32}}, cli_rf_txgain},
+ {"rf rxgain ", "rf_set_rxgain", 4, {{0,32},{0,32},{0,32},{0,32}}, cli_rf_rxgain},
+ {"rf chan ", "rf_set_chan", 2, {{0,32},{0,32}}, cli_rf_chan},
+};
+
+
+void ecrnx_debugfs_param_send(debugfs_info_t *req)
+{
+ if(debugfs_info.debugfs_type == SLAVE_LOG_LEVEL){
+ if (req->u.slave_log_level_t.debug_level < DBG_TYPE_D || req->u.slave_log_level_t.debug_level > DBG_TYPE_O) {
+ ECRNX_ERR("ecrnx debug param error!!!\n");
+ }
+ }
+
+ ECRNX_DBG("%s: fstype:%d, level:%d, dir:%d \n", __func__, req->debugfs_type, req->u.slave_log_level_t.debug_level, req->u.slave_log_level_t.debug_dir);
+ //print_hex_dump(KERN_INFO, "ecrnx_debugfs_send ", DUMP_PREFIX_ADDRESS, 32, 1,
+ // (u8*)req, sizeof(debugfs_info_t), false);
+ host_send(req, sizeof(debugfs_info_t), TX_FLAG_MSG_DEBUGFS_IE);
+}
+
+int ecrnx_log_level_get(LOG_CTL_ST *log)
+{
+ if (log == NULL)
+ return -1;
+
+ *log = log_ctl;
+
+ return 0;
+}
+
+int ecrnx_fw_log_level_set(u32 level, u32 dir)
+{
+ log_ctl.level = level;
+ log_ctl.dir = dir;
+
+ debugfs_info.debugfs_type = SLAVE_LOG_LEVEL;
+ debugfs_info.u.slave_log_level_t.debug_level = level;
+ debugfs_info.u.slave_log_level_t.debug_dir = dir;
+
+ ecrnx_debugfs_param_send(&debugfs_info);
+
+ return 0;
+}
+
+bool ecrnx_log_host_enable(void)
+{
+ if (log_ctl.dir)
+ return true;
+
+ return false;
+}
+
+int ecrnx_host_ver_get(u8 *ver)
+{
+ if (ver == NULL)
+ return -1;
+
+ sprintf(ver, "v%s", HOST_DRIVER_VERSION);
+
+ return 0;
+}
+
+int ecrnx_fw_ver_get(u8 *ver)
+{
+ if (ver == NULL)
+ return -1;
+
+ debugfs_info.debugfs_type = SLAVE_FW_INFO;
+ ecrnx_debugfs_param_send(&debugfs_info);
+
+ //wait for confirm
+ debugfs_resp.rxdatas = 0;
+ wait_event_interruptible_timeout(debugfs_resp.rxdataq, debugfs_resp.rxdatas, 1*HZ);
+
+ if (debugfs_resp.rxdatas)
+ memcpy(ver, debugfs_resp.rxdata, debugfs_resp.rxlen);
+ else
+ return -1;
+
+ return 0;
+}
+
+int ecrnx_build_time_get(u8 *build_time)
+{
+ if (build_time == NULL)
+ return -1;
+
+ sprintf(build_time, "%s %s", __DATE__, __TIME__);
+
+ return 0;
+}
+
+static int ecrnx_wdevs_get(struct seq_file *seq, struct wireless_dev **wdev)
+{
+ struct ecrnx_hw *ecrnx_hw;
+ u32 i;
+
+ if (seq->private == NULL)
+ return -1;
+
+ ecrnx_hw = seq->private;
+
+ for (i=0; i<NX_VIRT_STA_MAX; i++)
+ {
+ if (ecrnx_hw->vif_table[i] != NULL)
+ wdev[i] = &ecrnx_hw->vif_table[i]->wdev;
+ else
+ wdev[i] = NULL;
+ }
+
+ return 0;
+}
+
+static int ecrnx_wdev_match(struct wireless_dev **wdev, IF_TYPE_EN iftype, u32 *index)
+{
+ struct ecrnx_vif *ecrnx_vif;
+ u32 i;
+
+ for (i=0; i<NX_VIRT_STA_MAX; i++)
+ {
+ // 1. valid
+ if (wdev[i] == NULL)
+ continue;
+
+ // 2. up
+ ecrnx_vif = netdev_priv(wdev[i]->netdev);
+ if (ecrnx_vif->up == false)
+ continue;
+
+ // 3. type
+ switch(wdev[i]->iftype)
+ {
+ case NL80211_IFTYPE_STATION:
+ {
+ if (iftype == IF_STA)
+ {
+ *index = i;
+ return 0;
+ }
+ break;
+ }
+
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_AP_VLAN:
+ {
+ if (iftype == IF_AP)
+ {
+ *index = i;
+ return 0;
+ }
+ break;
+ }
+
+ case NL80211_IFTYPE_P2P_CLIENT:
+ case NL80211_IFTYPE_P2P_GO:
+ case NL80211_IFTYPE_P2P_DEVICE:
+ {
+ if (iftype == IF_P2P)
+ {
+ *index = i;
+ return 0;
+ }
+ break;
+ }
+
+ default :
+ break;
+ }
+ }
+
+ return -1;
+}
+
+int ecrnx_rf_info_get(struct seq_file *seq, IF_TYPE_EN iftype,RF_INFO_ST *cur, RF_INFO_ST *oper)
+{
+ struct wireless_dev *wdev[NX_VIRT_STA_MAX];
+ struct cfg80211_chan_def chandef;
+ u32 index;
+
+ if (0 != ecrnx_wdevs_get(seq, wdev))
+ return -1;
+
+ if ((cur == NULL) || (oper == NULL))
+ return -1;
+
+ if (0 != ecrnx_wdev_match(wdev, iftype, &index))
+ return -1;
+
+ if (0 != ecrnx_cfg80211_get_channel(wdev[index]->wiphy, wdev[index], &chandef))
+ return -1;
+
+ cur->ch = (chandef.center_freq1 - 2412)/5 + 1;
+ cur->bw = chandef.width;
+ cur->ch_offset = 0;
+
+ oper->ch = (chandef.center_freq1 - 2412)/5 + 1;
+ oper->bw = chandef.width;
+ oper->ch_offset = 0;
+
+ return 0;
+}
+
+int ecrnx_country_code_get(struct seq_file *seq, char *alpha2)
+{
+ struct ecrnx_hw *ecrnx_hw;
+
+ if (seq->private != NULL)
+ ecrnx_hw = seq->private;
+ else
+ return -1;
+
+ if (alpha2 == NULL)
+ return -1;
+
+ if (ecrnx_hw->wiphy == NULL)
+ return -1;
+
+ if (ecrnx_hw->wiphy->regd == NULL)
+ return -1;
+
+ memcpy(alpha2, ecrnx_hw->wiphy->regd->alpha2, sizeof(ecrnx_hw->wiphy->regd->alpha2));
+ alpha2[2] = '\0';
+
+ return 0;
+}
+
+int ecrnx_mac_addr_get(struct seq_file *seq, IF_TYPE_EN iftype, u8 *mac_addr)
+{
+ struct wireless_dev *wdev[NX_VIRT_STA_MAX];
+ u32 index;
+
+ if (0 != ecrnx_wdevs_get(seq, wdev))
+ return -1;
+
+ if (mac_addr == NULL)
+ return -1;
+
+ if (0 != ecrnx_wdev_match(wdev, iftype, &index))
+ return -1;
+
+ memcpy(mac_addr, wdev[index]->netdev->perm_addr, ETH_ALEN);
+
+ return 0;
+}
+
+int ecrnx_mac_addr_get_ex(struct seq_file *seq, u8 *mac_addr_info)
+{
+ struct wireless_dev *wdev[NX_VIRT_STA_MAX];
+ u32 i;
+
+ if (0 != ecrnx_wdevs_get(seq, wdev))
+ return -1;
+
+ if (mac_addr_info == NULL)
+ return -1;
+
+ mac_addr_info[0] = '\0';
+ for (i=0; i<NX_VIRT_STA_MAX; i++)
+ {
+ if (wdev[i] != NULL)
+ {
+ sprintf(mac_addr_info+strlen(mac_addr_info), "%s hw_port(%d) mac_addr=%02X:%02X:%02X:%02X:%02X:%02X\n", wdev[i]->netdev->name, wdev[i]->netdev->ifindex,
+ wdev[i]->netdev->perm_addr[0], wdev[i]->netdev->perm_addr[1], wdev[i]->netdev->perm_addr[2],
+ wdev[i]->netdev->perm_addr[3], wdev[i]->netdev->perm_addr[4], wdev[i]->netdev->perm_addr[5]);
+ }
+ }
+
+ return 0;
+}
+
+int ecrnx_channel_get(struct seq_file *seq, IF_TYPE_EN iftype, struct cfg80211_chan_def *chandef)
+{
+ struct wireless_dev *wdev[NX_VIRT_STA_MAX];
+ u32 index;
+
+ if (0 != ecrnx_wdevs_get(seq, wdev))
+ return -1;
+
+ if (chandef == NULL)
+ return -1;
+
+ if (0 != ecrnx_wdev_match(wdev, iftype, &index))
+ return -1;
+
+ return ecrnx_cfg80211_get_channel(wdev[index]->wiphy, wdev[index], chandef);
+}
+
+int ecrnx_p2p_role_get(struct seq_file *seq, IF_TYPE_EN iftype)
+{
+ struct wireless_dev *wdev[NX_VIRT_STA_MAX];
+ u32 index;
+
+ if (0 != ecrnx_wdevs_get(seq, wdev))
+ return -1;
+
+ if (0 != ecrnx_wdev_match(wdev, iftype, &index))
+ return -1;
+
+ return wdev[index]->iftype;
+}
+
+int ecrnx_bssid_get(struct seq_file *seq, IF_TYPE_EN iftype, u8 *bssid)
+{
+ struct station_info sinfo;
+ struct wireless_dev *wdev[NX_VIRT_STA_MAX];
+ u32 index;
+
+ if (0 != ecrnx_wdevs_get(seq, wdev))
+ return -1;
+
+ if (bssid == NULL)
+ return -1;
+
+ if (0 != ecrnx_wdev_match(wdev, iftype, &index))
+ return -1;
+
+ return ecrnx_cfg80211_dump_station(wdev[index]->wiphy, wdev[index]->netdev, 0, bssid, &sinfo);
+}
+
+int ecrnx_signal_level_get(struct seq_file *seq, IF_TYPE_EN iftype, s8 *noise_dbm)
+{
+ struct wireless_dev *wdev[NX_VIRT_STA_MAX];
+ struct ecrnx_hw *ecrnx_hw;
+ u32 index;
+
+ if (0 != ecrnx_wdevs_get(seq, wdev))
+ return -1;
+
+ if (noise_dbm == NULL)
+ return -1;
+
+ if (0 != ecrnx_wdev_match(wdev, iftype, &index))
+ return -1;
+
+ ecrnx_hw = wdev_priv(wdev[index]);
+ *noise_dbm = ecrnx_hw->survey[0].noise_dbm;
+
+ return 0;
+}
+
+int ecrnx_flags_get(struct seq_file *seq, IF_TYPE_EN iftype, u32 *flags)
+{
+ struct wireless_dev *wdev[NX_VIRT_STA_MAX];
+ u32 index;
+
+ if (0 != ecrnx_wdevs_get(seq, wdev))
+ return -1;
+
+ if (flags == NULL)
+ return -1;
+
+ if (0 != ecrnx_wdev_match(wdev, iftype, &index))
+ return -1;
+
+ *flags = wdev[index]->wiphy->flags;
+
+ return 0;
+}
+
+int ecrnx_ssid_get(struct seq_file *seq, IF_TYPE_EN iftype, char *ssid)
+{
+ struct wireless_dev *wdev[NX_VIRT_STA_MAX];
+ u32 index;
+
+ if (0 != ecrnx_wdevs_get(seq, wdev))
+ return -1;
+
+ if (ssid == NULL)
+ return -1;
+
+ if (0 != ecrnx_wdev_match(wdev, iftype, &index))
+ return -1;
+
+ memcpy(ssid, wdev[index]->ssid, IEEE80211_MAX_SSID_LEN);
+
+ return 0;
+}
+
+int ecrnx_sta_mac_get(struct seq_file *seq, IF_TYPE_EN iftype, u8 sta_mac[][ETH_ALEN+1])
+{
+ struct ecrnx_hw *ecrnx_hw;
+ u32 i;
+ u8 mac[ETH_ALEN] = {0};
+
+ if (seq->private != NULL)
+ ecrnx_hw = seq->private;
+ else
+ return -1;
+
+ // station table
+ for (i=0; i<NX_REMOTE_STA_MAX; i++)
+ {
+ if (ecrnx_hw->sta_table[i].valid == true)
+ {
+ if (0 == memcmp(mac, ecrnx_hw->sta_table[i].mac_addr, ETH_ALEN))
+ {
+ sta_mac[i][0] = 0xFF;
+ continue;
+ }
+
+ sta_mac[i][0] = 0xFF;
+ memcpy(&sta_mac[i][1], ecrnx_hw->sta_table[i].mac_addr, ETH_ALEN);
+
+ }
+ }
+
+ return 0;
+
+}
+
+int ecrnx_mac_reg_dump(struct seq_file *seq)
+{
+ u32 i, data;
+
+ seq_printf(seq, "%-34s %-11s %-10s\n", "name", "addr", "value");
+
+ for (i=0; i<(sizeof(mac_reg_table)/sizeof(mac_reg_table[0])); i++)
+ {
+ ecrnx_slave_reg_read(mac_reg_table[i].addr, &data, 1);
+ seq_printf(seq, "%-34s 0x%08X: 0x%08X\n", mac_reg_table[i].name, mac_reg_table[i].addr, data);
+ }
+
+ return 0;
+}
+
+int ecrnx_rf_reg_dump(struct seq_file *seq)
+{
+ u32 i, data;
+
+ seq_printf(seq, "%-34s %-11s %-10s\n", "name", "addr", "value");
+
+ for (i=0; i<(sizeof(rf_reg_table)/sizeof(rf_reg_table[0])); i++)
+ {
+ ecrnx_slave_reg_read(rf_reg_table[i].addr, &data, 1);
+ seq_printf(seq, "%-34s 0x%08X: 0x%08X\n", rf_reg_table[i].name, rf_reg_table[i].addr, data);
+ }
+
+ return 0;
+}
+
+int ecrnx_bb_reg_dump(struct seq_file *seq)
+{
+ u32 i, data;
+
+ seq_printf(seq, "%-34s %-11s %-10s\n", "name", "addr", "value");
+
+ for (i=0; i<(sizeof(bb_reg_table)/sizeof(bb_reg_table[0])); i++)
+ {
+ ecrnx_slave_reg_read(bb_reg_table[i].addr, &data, 1);
+ seq_printf(seq, "%-34s 0x%08X: 0x%08X\n", bb_reg_table[i].name, bb_reg_table[i].addr, data);
+ }
+
+ return 0;
+}
+
+int ecrnx_efuse_map_dump(struct seq_file *seq)
+{
+ u32 i, data;
+
+ seq_printf(seq, "%-34s %-11s %-10s\n", "name", "addr", "value");
+
+ for (i=0; i<(sizeof(efuse_map_table)/sizeof(efuse_map_table[0])); i++)
+ {
+ ecrnx_slave_reg_read(efuse_map_table[i].addr, &data, 1);
+ seq_printf(seq, "%-34s 0x%08X: 0x%08X\n", efuse_map_table[i].name, efuse_map_table[i].addr, data);
+ }
+
+ return 0;
+}
+
+int ecrnx_slave_reg_read(u32 addr, u32 *data, u32 len)
+{
+ if (data == NULL)
+ return -1;
+
+ debugfs_info.debugfs_type = SLAVE_READ_REG;
+ debugfs_info.u.slave_read_reg_t.reg = addr;
+ debugfs_info.u.slave_read_reg_t.len = len;
+ ecrnx_debugfs_param_send(&debugfs_info);
+
+ //wait for confirm
+ debugfs_resp.rxdatas = 0;
+ wait_event_interruptible_timeout(debugfs_resp.rxdataq, debugfs_resp.rxdatas, 1*HZ);
+
+ if (debugfs_resp.rxdatas)
+ memcpy((u8 *)data, (u8 *)debugfs_resp.rxdata, debugfs_resp.rxlen);
+
+ return 0;
+}
+
+int ecrnx_slave_reg_write(u32 addr, u32 data, u32 len)
+{
+ debugfs_info.debugfs_type = SLAVE_WRITE_REG;
+ debugfs_info.u.slave_write_reg_t.reg = addr;
+ debugfs_info.u.slave_write_reg_t.value = data;
+ ecrnx_debugfs_param_send(&debugfs_info);
+
+ //wait for confirm
+ debugfs_resp.rxdatas = 0;
+ wait_event_interruptible_timeout(debugfs_resp.rxdataq, debugfs_resp.rxdatas, 1*HZ);
+
+ return 0;
+}
+
+int ecrnx_slave_cli_send(u8 *cli, u8 *resp)
+{
+ if (cli == NULL)
+ return -1;
+
+ debugfs_info.debugfs_type = SLAVE_FUNC_INFO;
+ strcpy(debugfs_info.u.slave_cli_func_info_t.func_and_param, cli);
+ ecrnx_debugfs_param_send(&debugfs_info);
+
+ //wait for confirm
+ debugfs_resp.rxdatas = 0;
+ wait_event_interruptible_timeout(debugfs_resp.rxdataq, debugfs_resp.rxdatas, 1*HZ);
+
+ if ((debugfs_resp.rxdatas) && (debugfs_resp.rxlen))
+ {
+ ECRNX_PRINT("%s\n", debugfs_resp.rxdata);
+ }
+
+ return 0;
+}
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0))
+/**
+ * ether_addr_copy - Copy an Ethernet address
+ * @dst: Pointer to a six-byte array Ethernet address destination
+ * @src: Pointer to a six-byte array Ethernet address source
+ *
+ * Please note: dst & src must both be aligned to u16.
+ */
+static inline void ether_addr_copy(u8 *dst, const u8 *src)
+{
+#if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS)
+ *(u32 *)dst = *(const u32 *)src;
+ *(u16 *)(dst + 4) = *(const u16 *)(src + 4);
+#else
+ u16 *a = (u16 *)dst;
+ const u16 *b = (const u16 *)src;
+
+ a[0] = b[0];
+ a[1] = b[1];
+ a[2] = b[2];
+#endif
+}
+
+#endif
+void ecrnx_debugfs_survey_info_update(struct ecrnx_hw *ecrnx_hw, struct cfg80211_bss *bss)
+{
+ __le16 ie_cap = 0;
+ const u8 *ssid_elm;
+ const u8 *ie_wpa = NULL, *ie_wpa2 = NULL, *ie_wps = NULL;
+ const u8 *ie_p2p = NULL;
+
+ struct ecrnx_debugfs_survey_info_tbl *new_node, *entry, *tmp;
+ new_node = kzalloc(sizeof(struct ecrnx_debugfs_survey_info_tbl),
+ GFP_ATOMIC);
+
+ if (bss) {
+ const struct cfg80211_bss_ies *ies;
+
+ new_node->ch = ieee80211_frequency_to_channel(bss->channel->center_freq);
+ ether_addr_copy(new_node->bssid, bss->bssid);
+ new_node->rssi = bss->signal/100;
+
+ rcu_read_lock();
+
+ ie_wpa2 = ieee80211_bss_get_ie(bss, WLAN_EID_RSN);
+
+ ies = rcu_dereference(bss->ies);
+
+ ie_wpa = cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT,
+ WLAN_OUI_TYPE_MICROSOFT_WPA,
+ ies->data,
+ ies->len);
+
+ ie_wps = cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT,
+ WLAN_OUI_TYPE_MICROSOFT_WPS,
+ ies->data,
+ ies->len);
+
+ ie_p2p = cfg80211_find_vendor_ie(WLAN_OUI_WFA,
+ WLAN_OUI_TYPE_WFA_P2P,
+ ies->data,
+ ies->len);
+
+ ie_cap = cpu_to_le16(bss->capability);
+
+ ssid_elm = cfg80211_find_ie(WLAN_EID_SSID, ies->data, ies->len);
+ if (ssid_elm) {
+ if (ssid_elm[1] <= IEEE80211_MAX_SSID_LEN)
+ memcpy(new_node->ssid, ssid_elm + 2, ssid_elm[1]);
+ }
+
+ rcu_read_unlock();
+ }
+
+ sprintf(new_node->flag, "%s%s%s%s%s%s",
+ (ie_wpa) ? "[WPA]" : "",
+ (ie_wpa2) ? "[WPA2]" : "",
+ (!ie_wpa && !ie_wpa && ie_cap & BIT(4)) ? "[WEP]" : "",
+ (ie_wps) ? "[WPS]" : "",
+ (ie_cap & BIT(0)) ? "[ESS]" : "",
+ (ie_p2p) ? "[P2P]" : "");
+
+ INIT_LIST_HEAD(&new_node->list);
+
+ //if (!list_empty(&ecrnx_hw->debugfs_survey_info_tbl_ptr)) {
+ list_for_each_entry_safe(entry, tmp, &ecrnx_hw->debugfs_survey_info_tbl_ptr, list) {
+ if(memcmp(entry->bssid, new_node->bssid, ETH_ALEN) == 0) {
+ list_del(&entry->list);
+ kfree(entry);
+ }
+ }
+ //}
+ list_add_tail(&new_node->list, &ecrnx_hw->debugfs_survey_info_tbl_ptr);
+}
+
+void ecrnx_debugfs_noise_of_survey_info_update(struct ecrnx_hw *ecrnx_hw, struct ecrnx_survey_info *ecrnx_survey, int chan_no)
+{
+ struct ecrnx_debugfs_survey_info_tbl *entry;
+
+ list_for_each_entry(entry, &ecrnx_hw->debugfs_survey_info_tbl_ptr, list) {
+ if(entry->ch == chan_no) {
+ entry->noise = ecrnx_survey->noise_dbm;
+ }
+ }
+}
+
+void ecrnx_debugfs_add_station_in_ap_mode(struct ecrnx_hw *ecrnx_hw,
+ struct ecrnx_sta *sta, struct station_parameters *params)
+{
+ struct ecrnx_debugfs_sta *debugfs_sta;
+ //char file_name[18];
+
+ if(!sta || !params)
+ {
+ ECRNX_ERR("%s-%d:sta(%p) or params(%p) is null, return!\n", __func__, __LINE__, sta, params);
+ return;
+ }
+
+ debugfs_sta = kzalloc(sizeof(struct ecrnx_debugfs_sta), GFP_KERNEL);
+
+ if (!debugfs_sta)
+ ECRNX_ERR("error debugfs_sta kzalloc!\n");
+
+
+ debugfs_sta->sta_idx = sta->sta_idx;
+ debugfs_sta->aid = sta->aid;
+ debugfs_sta->ch_idx = sta->ch_idx;
+ debugfs_sta->ht = sta->ht;
+ ether_addr_copy(debugfs_sta->mac_addr, sta->mac_addr);
+ debugfs_sta->qos = sta->qos;
+ debugfs_sta->vht = sta->vht;
+ debugfs_sta->width = sta->width;
+
+ if(sta->ht)
+ {
+ if (params->ht_capa->cap_info & IEEE80211_HT_CAP_TX_STBC)
+ debugfs_sta->stbc_cap = 1;
+ if (params->ht_capa->cap_info & IEEE80211_HT_CAP_LDPC_CODING)
+ debugfs_sta->ldpc_cap = 1;
+ if (params->ht_capa->cap_info & IEEE80211_HT_CAP_SGI_20)
+ debugfs_sta->sgi_20m = 1;
+ if (params->ht_capa->cap_info & IEEE80211_HT_CAP_SGI_40)
+ debugfs_sta->sgi_40m = 1;
+ }
+
+ ecrnx_debugfs_sta_in_ap_init(debugfs_sta);
+}
+
+int cli_parse_args(uint8_t* str, char* argv[])
+{
+ int i = 0;
+ char* ch = (char *)str;
+
+ while(*ch == ' ') ch++;
+ if (*ch == '\n')
+ return 0;
+
+ while(*ch != '\0')
+ {
+ i++;
+
+ if (i > MAX_ARGV)
+ return 0;
+
+ argv[i-1] = ch;
+ while(*ch != '\0')
+ {
+ if(*ch == ' ')
+ break;
+ else
+ ch++;
+ }
+
+ *ch = '\0';
+ ch++;
+ while(*ch == ' ') {
+ ch++;
+ }
+ }
+
+ return i;
+}
+
+void argv_display(uint32_t argc, char* argv[])
+{
+ uint32_t i;
+
+ ECRNX_PRINT("argc is %d\n", argc);
+ for (i=0; i<argc; i++)
+ {
+ ECRNX_PRINT("param %d is %s\n", i, argv[i]);
+ }
+}
+
+int _atoi(char *pstr)
+{
+ int ret_integer = 0;
+ int integer_sign = 1;
+
+ if (pstr == NULL)
+ {
+ //printk("Pointer is NULL\n");
+ return 0;
+ }
+
+ while (*pstr == ' ')
+ {
+ pstr++;
+ }
+
+ if (*pstr == '-')
+ {
+ integer_sign = -1;
+ }
+ if (*pstr == '-' || *pstr == '+')
+ {
+ pstr++;
+ }
+
+ while (*pstr >= '0' && *pstr <= '9')
+ {
+ ret_integer = ret_integer * 10 + *pstr - '0';
+ pstr++;
+ }
+ ret_integer = integer_sign * ret_integer;
+
+ return ret_integer;
+}
+
+int cli_check_parm_num(uint32_t index, uint8_t *param)
+{
+ argc = cli_parse_args(param, argv);
+ //argv_display(argc, argv);
+
+ if (argc != ecrnx_cli_tab[index].param_num)
+ return -1;
+
+ return 0;
+}
+
+int cli_check_parm_range(uint32_t start_index, uint32_t index, uint32_t argc, char* argv[])
+{
+ uint32_t i;
+
+ for (i=start_index; i<argc; i++)
+ {
+ arg_val[i] = _atoi(argv[i]);
+ if ((arg_val[i] < ecrnx_cli_tab[index].param_range[i].range_min)
+ || (arg_val[i] > ecrnx_cli_tab[index].param_range[i].range_max))
+ {
+ ECRNX_PRINT("param %d is out of range! current %d, min %d, max %d\n",
+ i, arg_val[i], ecrnx_cli_tab[index].param_range[i].range_min, ecrnx_cli_tab[index].param_range[i].range_max);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+int cli_macbyp_start(uint32_t index, uint8_t *param)
+{
+ uint32_t i;
+
+ // parse parameters
+ if (0 != cli_check_parm_num(index, param))
+ return -1;
+
+ // check range value
+ if (0 != strcmp(argv[0], "11a")
+ && 0 != strcmp(argv[0], "11b")
+ && 0 != strcmp(argv[0], "11g")
+ && 0 != strcmp(argv[0], "11n")
+ && 0 != strcmp(argv[0], "11ac")
+ && 0 != strcmp(argv[0], "11ax"))
+ return -1;
+
+ if (0 != cli_check_parm_range(1, index, argc, argv))
+ return -1;
+
+ // package command
+ sprintf(slave_cli_cmd, "%s %s", ecrnx_cli_tab[index].map_cmd, argv[0]);
+ for (i=1; i<argc; i++)
+ sprintf(slave_cli_cmd+strlen(slave_cli_cmd), " %d", arg_val[i]);
+
+ //printk("--->The final command is: %s<-\n", slave_cli_cmd);
+ ecrnx_slave_cli_send(slave_cli_cmd, NULL);
+
+ return 0;
+}
+
+int cli_macbyp_stop(uint32_t index, uint8_t *param)
+{
+ // parse parameters
+ if (0 != cli_check_parm_num(index, param))
+ return -1;
+
+ // package command
+ sprintf(slave_cli_cmd, "%s", ecrnx_cli_tab[index].map_cmd);
+
+ //printk("---> The final command is: %s<-\n", slave_cli_cmd);
+ ecrnx_slave_cli_send(slave_cli_cmd, NULL);
+
+ return 0;
+}
+
+int cli_rf_txgain(uint32_t index, uint8_t *param)
+{
+ uint32_t i;
+
+ // parse parameters
+ if (0 != cli_check_parm_num(index, param))
+ return -1;
+
+ // check range value
+ if (0 != cli_check_parm_range(0, index, argc, argv))
+ return -1;
+
+ // package command
+ sprintf(slave_cli_cmd, "%s", ecrnx_cli_tab[index].map_cmd);
+ for (i=0; i<argc; i++)
+ sprintf(slave_cli_cmd+strlen(slave_cli_cmd), " %d", arg_val[i]);
+
+ //printk("---> The final command is: %s<-\n", slave_cli_cmd);
+ ecrnx_slave_cli_send(slave_cli_cmd, NULL);
+
+ return 0;
+}
+
+int cli_rf_rxgain(uint32_t index, uint8_t *param)
+{
+ uint32_t i;
+
+ // parse parameters
+ if (0 != cli_check_parm_num(index, param))
+ return -1;
+
+ // check range value
+ if (0 != cli_check_parm_range(0, index, argc, argv))
+ return -1;
+
+ // package command
+ sprintf(slave_cli_cmd, "%s", ecrnx_cli_tab[index].map_cmd);
+ for (i=0; i<argc; i++)
+ sprintf(slave_cli_cmd+strlen(slave_cli_cmd), " %d", arg_val[i]);
+
+ //printk("---> The final command is: %s<-\n", slave_cli_cmd);
+ ecrnx_slave_cli_send(slave_cli_cmd, NULL);
+
+ return 0;
+}
+
+int cli_rf_chan(uint32_t index, uint8_t *param)
+{
+ uint32_t i;
+
+ // parse parameters
+ if (0 != cli_check_parm_num(index, param))
+ return -1;
+
+ // check range value
+ if (0 != cli_check_parm_range(0, index, argc, argv))
+ return -1;
+
+ // package command
+ sprintf(slave_cli_cmd, "%s", ecrnx_cli_tab[index].map_cmd);
+ for (i=0; i<argc; i++)
+ sprintf(slave_cli_cmd+strlen(slave_cli_cmd), " %d", arg_val[i]);
+
+ //printk("---> The final command is: %s<-\n", slave_cli_cmd);
+ ecrnx_slave_cli_send(slave_cli_cmd, NULL);
+
+ return 0;
+}
+
+int cli_cmd_parse(uint8_t *cmd)
+{
+ uint32_t i;
+ int ret;
+
+ if (cmd == NULL)
+ return -1;
+
+ //printk("cmd is :%s<-\n", cmd);
+ for (i=0; i<sizeof(ecrnx_cli_tab)/sizeof(ecrnx_cli_tab[0]); i++)
+ {
+ if (0 == memcmp(cmd, ecrnx_cli_tab[i].cmd, strlen(ecrnx_cli_tab[i].cmd)))
+ {
+ //printk("index %d, cmd %s\n", i, ecrnx_cli_tab[i].cmd);
+ ret = ecrnx_cli_tab[i].cmd_func(i, cmd + strlen(ecrnx_cli_tab[i].cmd));
+ goto exit;
+ }
+ }
+
+ ECRNX_ERR("No matching commands!\n");
+
+ return -2;
+
+exit:
+ return ret;
+}
+#endif
+
diff --git a/drivers/net/wireless/eswin/fullmac/ecrnx_debugfs_func.h b/drivers/net/wireless/eswin/fullmac/ecrnx_debugfs_func.h
new file mode 100644
index 000000000000..2b7a0f045ea6
--- /dev/null
+++ b/drivers/net/wireless/eswin/fullmac/ecrnx_debugfs_func.h
@@ -0,0 +1,212 @@
+#ifndef __ECRNX_DEBUGFS_FUNC_H_
+#define __ECRNX_DEBUGFS_FUNC_H_
+
+#include "ecrnx_defs.h"
+#include "ecrnx_utils.h"
+#include "eswin_utils.h"
+#include "fw_head_check.h"
+
+#include <linux/etherdevice.h>
+
+
+
+#define HOST_DRIVER_VERSION "1.0.0"
+
+#define NX_VIRT_STA_MAX (NX_VIRT_DEV_MAX + NX_REMOTE_STA_MAX)
+
+typedef enum {
+ IF_STA = 0,
+ IF_AP,
+ IF_P2P
+} IF_TYPE_EN;
+
+typedef struct {
+ u8 ch;
+ u8 bw;
+ u32 ch_offset;
+} RF_INFO_ST;
+
+typedef struct {
+ u8 name[48];
+ u32 addr;
+}REG_MAP_ST;
+
+struct ecrnx_debugfs_survey_info_tbl
+{
+ struct list_head list;
+ u8 ssid[IEEE80211_MAX_SSID_LEN];
+ u8 bssid[ETH_ALEN];
+ u32 ch;
+ int rssi;
+ s32 sdbm;
+ s16 noise;
+ u32 age;
+ char flag[64];
+};
+
+struct ecrnx_debugfs_sta
+{
+ u8 mac_addr[ETH_ALEN];
+ u16 aid; /* association ID */
+ u8 sta_idx; /* Identifier of the station */
+ enum nl80211_chan_width width; /* Channel width */
+ u8 ch_idx; /* Identifier of the channel
+ context the station belongs to */
+ bool qos; /* Flag indicating if the station
+ supports QoS */
+ bool ht; /* Flag indicating if the station
+ supports HT */
+ bool vht; /* Flag indicating if the station
+ supports VHT */
+
+ bool sgi_20m;
+ bool sgi_40m;
+
+ u8 ampdu_enable;/* for enable Tx A-MPDU */
+
+ /* for processing Tx A-MPDU */
+ u8 agg_enable_bitmap;
+ /* u8 ADDBA_retry_count; */
+ u8 candidate_tid_bitmap;
+
+ u8 ldpc_cap;
+ u8 stbc_cap;
+ u8 beamform_cap;
+};
+
+#define MAX_ARGV 10
+
+typedef int (*_cmd_func)(uint32_t index, uint8_t *param);
+
+typedef struct{
+ uint32_t range_min;
+ uint32_t range_max;
+}ECRNX_CLI_PARAM_RANGE_ST;
+
+typedef struct {
+ char cmd[20];
+ char map_cmd[20];
+ uint32_t param_num;
+ ECRNX_CLI_PARAM_RANGE_ST param_range[MAX_ARGV];
+ _cmd_func cmd_func;
+}ECRNX_CLI_TABLE_ST;
+
+extern ECRNX_CLI_TABLE_ST ecrnx_cli_tab[];
+
+enum debugfs_type
+{
+ SLAVE_LOG_LEVEL = 1,
+ SLAVE_FW_INFO,
+ SLAVE_RF_INFO,
+ SLAVE_MAC_REG_DUMP,
+ SLAVE_RF_REG_DUMP,
+ SLAVE_BB_REG_DUMP,
+ SLAVE_COUNTRY_CODE,
+ SLAVE_EFUSE_MAP,
+ SLAVE_WOW_ENABLE,
+ SLAVE_WOW_WLAN_GOPI_INFO,
+ SLAVE_READ_REG,
+ SLAVE_WRITE_REG,
+ SLAVE_FUNC_INFO,
+ SLAVE_DEBUGFS_MAX,
+};
+
+typedef struct
+{
+ uint32_t debugfs_type;
+
+ union
+ {
+ struct
+ {
+ uint32_t debug_level;
+ uint32_t debug_dir;
+ }slave_log_level_t;
+
+ struct
+ {
+ uint32_t info;
+ uint32_t info_len;
+ }slave_fw_info_t;
+
+ struct
+ {
+ uint32_t reg;
+ uint32_t len;
+ }slave_read_reg_t;
+
+ struct
+ {
+ uint32_t reg;
+ uint32_t value;
+ }slave_write_reg_t;
+
+ struct
+ {
+ uint8_t func_and_param[56];
+ }slave_cli_func_info_t;
+
+ }u;
+}debugfs_info_t;
+
+typedef struct
+{
+ uint32_t debugfs_type;
+ unsigned char rxdata[ECRNX_RXSIZE];
+ int rxlen;
+ wait_queue_head_t rxdataq;
+ int rxdatas;
+}debugfs_resp_t;
+
+extern debugfs_info_t debugfs_info;
+extern debugfs_resp_t debugfs_resp;
+
+extern u32 reg_buf[512];
+
+void ecrnx_debug_param_send(dbg_req_t *req);
+
+struct ring_buffer *usb_dbg_buf_get(void);
+
+int ecrnx_cfg80211_dump_station(struct wiphy *wiphy, struct net_device *dev,
+ int idx, u8 *mac, struct station_info *sinfo);
+int ecrnx_cfg80211_get_channel(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ unsigned int link_id,
+ struct cfg80211_chan_def *chandef);
+int ecrnx_log_level_get(LOG_CTL_ST *log);
+int ecrnx_fw_log_level_set(u32 level, u32 dir);
+bool ecrnx_log_host_enable(void);
+int ecrnx_host_ver_get(u8 *ver);
+int ecrnx_fw_ver_get(u8 *ver);
+int ecrnx_build_time_get(u8 *build_time);
+int ecrnx_rf_info_get(struct seq_file *seq, IF_TYPE_EN iftype,RF_INFO_ST *cur, RF_INFO_ST *oper);
+int ecrnx_country_code_get(struct seq_file *seq, char *alpha2);
+int ecrnx_mac_addr_get(struct seq_file *seq, IF_TYPE_EN iftype, u8 *mac_addr);
+int ecrnx_mac_addr_get_ex(struct seq_file *seq, u8 *mac_addr_info);
+int ecrnx_channel_get(struct seq_file *seq, IF_TYPE_EN iftype, struct cfg80211_chan_def *chandef);
+int ecrnx_p2p_role_get(struct seq_file *seq, IF_TYPE_EN iftype);
+int ecrnx_bssid_get(struct seq_file *seq, IF_TYPE_EN iftype, u8 *bssid);
+int ecrnx_signal_level_get(struct seq_file *seq, IF_TYPE_EN iftype, s8 *noise_dbm);
+int ecrnx_flags_get(struct seq_file *seq, IF_TYPE_EN iftype, u32 *flags);
+int ecrnx_ssid_get(struct seq_file *seq, IF_TYPE_EN iftype, char *ssid);
+int ecrnx_sta_mac_get(struct seq_file *seq, IF_TYPE_EN iftype, u8 sta_mac[][ETH_ALEN+1]);
+int ecrnx_mac_reg_dump(struct seq_file *seq);
+int ecrnx_rf_reg_dump(struct seq_file *seq);
+int ecrnx_bb_reg_dump(struct seq_file *seq);
+int ecrnx_efuse_map_dump(struct seq_file *seq);
+int ecrnx_slave_reg_read(u32 addr, u32 *data, u32 len);
+int ecrnx_slave_reg_write(u32 addr, u32 data, u32 len);
+int ecrnx_slave_cli_send(u8 *cli, u8 *resp);
+void ecrnx_debugfs_survey_info_update(struct ecrnx_hw *ecrnx_hw, struct cfg80211_bss *bss);
+void ecrnx_debugfs_noise_of_survey_info_update(struct ecrnx_hw *ecrnx_hw,
+ struct ecrnx_survey_info *ecrnx_survey, int chan_no);
+void ecrnx_debugfs_add_station_in_ap_mode(struct ecrnx_hw *ecrnx_hw,
+ struct ecrnx_sta *sta, struct station_parameters *params);
+int cli_macbyp_start(uint32_t index, uint8_t *param);
+int cli_macbyp_stop(uint32_t index, uint8_t *param);
+int cli_rf_txgain(uint32_t index, uint8_t *param);
+int cli_rf_rxgain(uint32_t index, uint8_t *param);
+int cli_rf_chan(uint32_t index, uint8_t *param);
+int cli_cmd_parse(uint8_t *cmd);
+
+#endif
diff --git a/drivers/net/wireless/eswin/fullmac/ecrnx_defs.h b/drivers/net/wireless/eswin/fullmac/ecrnx_defs.h
new file mode 100644
index 000000000000..e0ece21d546e
--- /dev/null
+++ b/drivers/net/wireless/eswin/fullmac/ecrnx_defs.h
@@ -0,0 +1,1099 @@
+/**
+ ******************************************************************************
+ *
+ * @file ecrnx_defs.h
+ *
+ * @brief Main driver structure declarations for fullmac driver
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+
+#ifndef _ECRNX_DEFS_H_
+#define _ECRNX_DEFS_H_
+
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/dmapool.h>
+#include <linux/skbuff.h>
+#include <net/cfg80211.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/version.h>
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0))
+#include <linux/vmalloc.h>
+#include <linux/module.h>
+#endif
+#include "ecrnx_mod_params.h"
+#include "ecrnx_debugfs.h"
+#include "ecrnx_tx.h"
+#include "ecrnx_rx.h"
+#include "ecrnx_radar.h"
+#include "ecrnx_utils.h"
+#include "ecrnx_mu_group.h"
+#include "ecrnx_platform.h"
+#include "ecrnx_cmds.h"
+
+#include "ecrnx_p2p.h"
+#include "ecrnx_debug.h"
+#include "ecrnx_cfgfile.h"
+
+
+#define WPI_HDR_LEN 18
+#define WPI_PN_LEN 16
+#define WPI_PN_OFST 2
+#define WPI_MIC_LEN 16
+#define WPI_KEY_LEN 32
+#define WPI_SUBKEY_LEN 16 // WPI key is actually two 16bytes key
+
+#define LEGACY_PS_ID 0
+#define UAPSD_ID 1
+
+#define PS_SP_INTERRUPTED 255
+#define ECRNX_RXSIZE 1024
+
+#define CONFIG_ESWIN_RX_REORDER 1
+#define IEEE80211_MAX_AMPDU_BUF IEEE80211_MAX_AMPDU_BUF_HE
+
+#if defined(CONFIG_ECRNX_HE)
+extern struct ieee80211_sta_he_cap ecrnx_he_cap;
+#endif
+
+/**
+ * struct ecrnx_bcn - Information of the beacon in used (AP mode)
+ *
+ * @head: head portion of beacon (before TIM IE)
+ * @tail: tail portion of beacon (after TIM IE)
+ * @ies: extra IEs (not used ?)
+ * @head_len: length of head data
+ * @tail_len: length of tail data
+ * @ies_len: length of extra IEs data
+ * @tim_len: length of TIM IE
+ * @len: Total beacon len (head + tim + tail + extra)
+ * @dtim: dtim period
+ */
+struct ecrnx_bcn {
+ u8 *head;
+ u8 *tail;
+ u8 *ies;
+ size_t head_len;
+ size_t tail_len;
+ size_t ies_len;
+ size_t tim_len;
+ size_t len;
+ u8 dtim;
+};
+
+/**
+ * struct ecrnx_key - Key information
+ *
+ * @hw_idx: Idx of the key from hardware point of view
+ */
+struct ecrnx_key {
+ u8 hw_idx;
+};
+
+/**
+ * Structure containing information about a Mesh Path
+ */
+struct ecrnx_mesh_path {
+ struct list_head list; /* For ecrnx_vif.mesh_paths */
+ u8 path_idx; /* Path Index */
+ struct mac_addr tgt_mac_addr; /* Target MAC Address */
+ struct ecrnx_sta *nhop_sta; /* Pointer to the Next Hop STA */
+};
+
+struct ecrnx_mesh_proxy {
+ struct list_head list; /* For ecrnx_vif.mesh_proxy */
+ struct mac_addr ext_sta_addr; /* Address of the External STA */
+ struct mac_addr proxy_addr; /* Proxy MAC Address */
+ bool local; /* Indicate if interface is a proxy for the device */
+};
+
+/**
+ * struct ecrnx_csa - Information for CSA (Channel Switch Announcement)
+ *
+ * @vif: Pointer to the vif doing the CSA
+ * @bcn: Beacon to use after CSA
+ * @elem: IPC buffer to send the new beacon to the fw
+ * @chandef: defines the channel to use after the switch
+ * @count: Current csa counter
+ * @status: Status of the CSA at fw level
+ * @ch_idx: Index of the new channel context
+ * @work: work scheduled at the end of CSA
+ */
+struct ecrnx_csa {
+ struct ecrnx_vif *vif;
+ struct ecrnx_bcn bcn;
+ struct ecrnx_ipc_elem_var elem;
+ struct cfg80211_chan_def chandef;
+ int count;
+ int status;
+ int ch_idx;
+ struct work_struct work;
+};
+
+/// Possible States of the TDLS link.
+enum tdls_status_tag {
+ /// TDLS link is not active (no TDLS peer connected)
+ TDLS_LINK_IDLE,
+ /// TDLS Setup Request transmitted
+ TDLS_SETUP_REQ_TX,
+ /// TDLS Setup Response transmitted
+ TDLS_SETUP_RSP_TX,
+ /// TDLS link is active (TDLS peer connected)
+ TDLS_LINK_ACTIVE,
+ /// TDLS Max Number of states.
+ TDLS_STATE_MAX
+};
+
+/*
+ * Structure used to save information relative to the TDLS peer.
+ * This is also linked within the ecrnx_hw vifs list.
+ *
+ */
+struct ecrnx_tdls {
+ bool active; /* Indicate if TDLS link is active */
+ bool initiator; /* Indicate if TDLS peer is the TDLS initiator */
+ bool chsw_en; /* Indicate if channel switch is enabled */
+ u8 last_tid; /* TID of the latest MPDU transmitted over the
+ TDLS direct link to the TDLS STA */
+ u16 last_sn; /* Sequence number of the latest MPDU transmitted
+ over the TDLS direct link to the TDLS STA */
+ bool ps_on; /* Indicate if the power save is enabled on the
+ TDLS STA */
+ bool chsw_allowed; /* Indicate if TDLS channel switch is allowed */
+};
+
+
+/**
+ * enum ecrnx_ap_flags - AP flags
+ *
+ * @ECRNX_AP_ISOLATE Isolate clients (i.e. Don't brige packets transmitted by
+ * one client for another one)
+ */
+enum ecrnx_ap_flags {
+ ECRNX_AP_ISOLATE = BIT(0),
+ ECRNX_AP_USER_MESH_PM = BIT(1),
+ ECRNX_AP_CREATE_MESH_PATH = BIT(2),
+};
+
+/**
+ * enum ecrnx_sta_flags - STATION flags
+ *
+ * @ECRNX_STA_EXT_AUTH: External authentication is in progress
+ */
+enum ecrnx_sta_flags {
+ ECRNX_STA_EXT_AUTH = BIT(0),
+ ECRNX_STA_FT_OVER_DS = BIT(1),
+ ECRNX_STA_FT_OVER_AIR = BIT(2),
+};
+
+#ifdef CONFIG_ECRNX_WIFO_CAIL
+/**
+ * struct ecrnx_iwpriv_amt_vif - iwpriv amt VIF information
+ *
+ * @ndev: Pointer to the associated net device
+ */
+struct ecrnx_iwpriv_amt_vif {
+ struct net_device *ndev;
+
+ unsigned char rxdata[ECRNX_RXSIZE];
+ int rxlen;
+ wait_queue_head_t rxdataq;
+ int rxdatas;
+};
+#endif
+
+#define ECRNX_REORD_RX_MSDU_CNT (256)
+#define ECRNX_REORD_TIMEOUT (50)
+#define ECRNX_REORD_WINSIZE (64)
+#define SN_LESS(a, b) (((a-b)&0x800)!=0)
+#define SN_EQUAL(a, b) (a == b)
+
+
+struct reord_cntrl {
+ bool active;
+ bool valid;
+ u16 win_size;
+ u16 win_start;
+ struct ecrnx_hw *ecrnx_hw;
+ struct ecrnx_vif *ecrnx_vif;
+ spinlock_t reord_list_lock;
+ struct list_head reord_list;
+ struct timer_list reord_timer;
+ struct work_struct reord_timer_work;
+};
+
+struct reord_msdu_info {
+ struct sk_buff *skb;
+ u8 tid;
+ u8 need_pn_check;
+ u8 is_ga;
+ u16 sn;
+ struct hw_rxhdr *hw_rxhdr;
+ struct list_head reord_pending_list;
+ struct list_head rx_msdu_list;
+ struct reord_cntrl *preorder_ctrl;
+};
+
+/**
+ * struct ecrnx_vif - VIF information
+ *
+ * @list: List element for ecrnx_hw->vifs
+ * @ecrnx_hw: Pointer to driver main data
+ * @wdev: Wireless device
+ * @ndev: Pointer to the associated net device
+ * @net_stats: Stats structure for the net device
+ * @key: Conversion table between protocol key index and MACHW key index
+ * @drv_vif_index: VIF index at driver level (only use to identify active
+ * vifs in ecrnx_hw->avail_idx_map)
+ * @vif_index: VIF index at fw level (used to index ecrnx_hw->vif_table, and
+ * ecrnx_sta->vif_idx)
+ * @ch_index: Channel context index (within ecrnx_hw->chanctx_table)
+ * @up: Indicate if associated netdev is up (i.e. Interface is created at fw level)
+ * @use_4addr: Whether 4address mode should be use or not
+ * @is_resending: Whether a frame is being resent on this interface
+ * @roc_tdls: Indicate if the ROC has been called by a TDLS station
+ * @tdls_status: Status of the TDLS link
+ * @tdls_chsw_prohibited: Whether TDLS Channel Switch is prohibited or not
+ * @generation: Generation ID. Increased each time a sta is added/removed
+ *
+ * STA / P2P_CLIENT interfaces
+ * @flags: see ecrnx_sta_flags
+ * @ap: Pointer to the peer STA entry allocated for the AP
+ * @tdls_sta: Pointer to the TDLS station
+ * @ft_assoc_ies: Association Request Elements (only allocated for FT connection)
+ * @ft_assoc_ies_len: Size, in bytes, of the Association request elements.
+ * @ft_target_ap: Target AP for a BSS transition for FT over DS
+ *
+ * AP/P2P GO/ MESH POINT interfaces
+ * @flags: see ecrnx_ap_flags
+ * @sta_list: List of station connected to the interface
+ * @bcn: Beacon data
+ * @bcn_interval: beacon interval in TU
+ * @bcmc_index: Index of the BroadCast/MultiCast station
+ * @csa: Information about current Channel Switch Announcement (NULL if no CSA)
+ * @mpath_list: List of Mesh Paths (MESH Point only)
+ * @proxy_list: List of Proxies Information (MESH Point only)
+ * @mesh_pm: Mesh power save mode currently set in firmware
+ * @next_mesh_pm: Mesh power save mode for next peer
+ *
+ * AP_VLAN interfaces
+ * @mater: Pointer to the master interface
+ * @sta_4a: When AP_VLAN interface are used for WDS (i.e. wireless connection
+ * between several APs) this is the 'gateway' sta to 'master' AP
+ */
+struct ecrnx_vif {
+ struct list_head list;
+ struct ecrnx_hw *ecrnx_hw;
+ struct wireless_dev wdev;
+ struct net_device *ndev;
+ struct net_device_stats net_stats;
+ struct ecrnx_key key[6];
+ u8 drv_vif_index; /* Identifier of the VIF in driver */
+ u8 vif_index; /* Identifier of the station in FW */
+ u8 ch_index; /* Channel context identifier */
+ bool up; /* Indicate if associated netdev is up
+ (i.e. Interface is created at fw level) */
+ bool use_4addr; /* Should we use 4addresses mode */
+ bool is_resending; /* Indicate if a frame is being resent on this interface */
+ bool roc_tdls; /* Indicate if the ROC has been called by a
+ TDLS station */
+ u8 tdls_status; /* Status of the TDLS link */
+ bool tdls_chsw_prohibited; /* Indicate if TDLS Channel Switch is prohibited */
+ int generation;
+
+ unsigned char rxdata[ECRNX_RXSIZE];
+ int rxlen;
+ wait_queue_head_t rxdataq;
+ int rxdatas;
+
+ u32 mgmt_reg_stypes; //GavinGao
+
+ union
+ {
+ struct
+ {
+ u32 flags;
+ struct ecrnx_sta *ap; /* Pointer to the peer STA entry allocated for
+ the AP */
+ struct ecrnx_sta *tdls_sta; /* Pointer to the TDLS station */
+ u8 *ft_assoc_ies;
+ int ft_assoc_ies_len;
+ u8 ft_target_ap[ETH_ALEN];
+ } sta;
+ struct
+ {
+ u32 flags;
+ struct list_head sta_list; /* List of STA connected to the AP */
+ struct ecrnx_bcn bcn; /* beacon */
+ int bcn_interval;
+ u8 bcmc_index; /* Index of the BCMC sta to use */
+ struct ecrnx_csa *csa;
+
+ struct list_head mpath_list; /* List of Mesh Paths used on this interface */
+ struct list_head proxy_list; /* List of Proxies Information used on this interface */
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+ enum nl80211_mesh_power_mode mesh_pm; /* mesh power save mode currently set in firmware */
+ enum nl80211_mesh_power_mode next_mesh_pm; /* mesh power save mode for next peer */
+#endif
+ } ap;
+ struct
+ {
+ struct ecrnx_vif *master; /* pointer on master interface */
+ struct ecrnx_sta *sta_4a;
+ } ap_vlan;
+ };
+ uint64_t rx_pn[TID_MAX];
+};
+
+#define ECRNX_INVALID_VIF 0xFF
+#define ECRNX_VIF_TYPE(vif) (vif->wdev.iftype)
+
+/**
+ * Structure used to store information relative to PS mode.
+ *
+ * @active: True when the sta is in PS mode.
+ * If false, other values should be ignored
+ * @pkt_ready: Number of packets buffered for the sta in drv's txq
+ * (1 counter for Legacy PS and 1 for U-APSD)
+ * @sp_cnt: Number of packets that remain to be pushed in the service period.
+ * 0 means that no service period is in progress
+ * (1 counter for Legacy PS and 1 for U-APSD)
+ */
+struct ecrnx_sta_ps {
+ bool active;
+ u16 pkt_ready[2];
+ u16 sp_cnt[2];
+};
+
+/**
+ * struct ecrnx_rx_rate_stats - Store statistics for RX rates
+ *
+ * @table: Table indicating how many frame has been receive which each
+ * rate index. Rate index is the same as the one used by RC algo for TX
+ * @size: Size of the table array
+ * @cpt: number of frames received
+ */
+struct ecrnx_rx_rate_stats {
+ int *table;
+ int size;
+ int cpt;
+ int rate_cnt;
+};
+
+/**
+ * struct ecrnx_sta_stats - Structure Used to store statistics specific to a STA
+ *
+ * @last_rx: Hardware vector of the last received frame
+ * @rx_rate: Statistics of the received rates
+ */
+struct ecrnx_sta_stats {
+ u32 rx_pkts;
+ u32 tx_pkts;
+ u64 rx_bytes;
+ u64 tx_bytes;
+ unsigned long last_act;
+ struct hw_vect last_rx;
+#ifdef CONFIG_ECRNX_DEBUGFS
+ struct ecrnx_rx_rate_stats rx_rate;
+#endif
+};
+
+/*
+ * Structure used to save information relative to the managed stations.
+ */
+struct ecrnx_sta {
+ struct list_head list;
+ bool valid;
+ u8 mac_addr[ETH_ALEN];
+ u16 aid; /* association ID */
+ u8 sta_idx; /* Identifier of the station */
+ u8 vif_idx; /* Identifier of the VIF (fw id) the station
+ belongs to */
+ u8 vlan_idx; /* Identifier of the VLAN VIF (fw id) the station
+ belongs to (= vif_idx if no vlan in used) */
+ enum nl80211_band band; /* Band */
+ enum nl80211_chan_width width; /* Channel width */
+ u16 center_freq; /* Center frequency */
+ u32 center_freq1; /* Center frequency 1 */
+ u32 center_freq2; /* Center frequency 2 */
+ u8 ch_idx; /* Identifier of the channel
+ context the station belongs to */
+ bool qos; /* Flag indicating if the station
+ supports QoS */
+ u8 acm; /* Bitfield indicating which queues
+ have AC mandatory */
+ u16 uapsd_tids; /* Bitfield indicating which tids are subject to
+ UAPSD */
+ struct ecrnx_key key;
+ struct ecrnx_sta_ps ps; /* Information when STA is in PS (AP only) */
+#ifdef CONFIG_ECRNX_BFMER
+ struct ecrnx_bfmer_report *bfm_report; /* Beamforming report to be used for
+ VHT TX Beamforming */
+#ifdef CONFIG_ECRNX_MUMIMO_TX
+ struct ecrnx_sta_group_info group_info; /* MU grouping information for the STA */
+#endif /* CONFIG_ECRNX_MUMIMO_TX */
+#endif /* CONFIG_ECRNX_BFMER */
+
+ bool ht; /* Flag indicating if the station
+ supports HT */
+ bool vht; /* Flag indicating if the station
+ supports VHT */
+ u32 ac_param[AC_MAX]; /* EDCA parameters */
+ struct ecrnx_tdls tdls; /* TDLS station information */
+ struct ecrnx_sta_stats stats;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+ enum nl80211_mesh_power_mode mesh_pm; /* link-specific mesh power save mode */
+#endif
+ int listen_interval;
+ struct twt_setup_ind twt_ind; /*TWT Setup indication*/
+ uint64_t rx_pn[TID_MAX];
+ struct reord_cntrl reord_cntrl[TID_MAX];
+};
+
+#define ECRNX_INVALID_STA 0xFF
+static inline const u8 *ecrnx_sta_addr(struct ecrnx_sta *sta) {
+ return sta->mac_addr;
+}
+
+#ifdef CONFIG_ECRNX_SPLIT_TX_BUF
+struct ecrnx_amsdu_stats {
+ int done;
+ int failed;
+};
+#endif
+
+struct ecrnx_stats {
+ int cfm_balance[NX_TXQ_CNT];
+ unsigned long last_rx, last_tx; /* jiffies */
+ int ampdus_tx[IEEE80211_MAX_AMPDU_BUF];
+ int ampdus_rx[IEEE80211_MAX_AMPDU_BUF];
+ int ampdus_rx_map[4];
+ int ampdus_rx_miss;
+ int ampdus_rx_last;
+#ifdef CONFIG_ECRNX_SPLIT_TX_BUF
+ struct ecrnx_amsdu_stats amsdus[NX_TX_PAYLOAD_MAX];
+#endif
+ int amsdus_rx[64];
+};
+
+/**
+ * struct ecrnx_roc - Remain On Channel information
+ *
+
+ Structure that will contains all RoC information received from cfg80211 */
+struct ecrnx_roc {
+ struct ecrnx_vif *vif;
+ struct ieee80211_channel *chan;
+ unsigned int duration;
+ /* Used to avoid call of CFG80211 callback upon expiration of RoC */
+ bool internal;
+ /* Indicate if we have switch on the RoC channel */
+ bool on_chan;
+};
+
+/* Structure containing channel survey information received from MAC */
+struct ecrnx_survey_info {
+ // Filled
+ u32 filled;
+ // Amount of time in ms the radio spent on the channel
+ u32 chan_time_ms;
+ // Amount of time the primary channel was sensed busy
+ u32 chan_time_busy_ms;
+ // Noise in dbm
+ s8 noise_dbm;
+};
+
+
+/* Structure containing channel context information */
+struct ecrnx_chanctx {
+ struct cfg80211_chan_def chan_def; /* channel description */
+ u8 count; /* number of vif using this ctxt */
+};
+
+#define ECRNX_CH_NOT_SET 0xFF
+/**
+ * ecrnx_phy_info - Phy information
+ *
+ * @phy_cnt: Number of phy interface
+ * @cfg: Configuration send to firmware
+ * @sec_chan: Channel configuration of the second phy interface (if phy_cnt > 1)
+ * @limit_bw: Set to true to limit BW on requested channel. Only set to use
+ * VHT with old radio that don't support 80MHz (deprecated)
+ */
+struct ecrnx_phy_info {
+ u8 cnt;
+ struct phy_cfg_tag cfg;
+ struct mac_chan_op sec_chan;
+ bool limit_bw;
+};
+
+struct ecrnx_hw {
+ struct device *dev;
+ // Hardware info
+
+#ifdef CONFIG_ECRNX_ESWIN
+ void *plat;
+#else
+ struct ecrnx_plat *plat;
+#endif
+
+ struct ecrnx_phy_info phy;
+ struct mm_version_cfm version_cfm;
+ int machw_type;
+ struct ecrnx_mod_params *mod_params;
+ unsigned long flags;
+ struct wiphy *wiphy;
+ u8 ext_capa[10];
+ struct list_head vifs;
+ struct ecrnx_vif *vif_table[NX_VIRT_DEV_MAX + NX_REMOTE_STA_MAX]; /* indexed with fw id */
+ u8 vif_started;
+ u8 avail_idx_map;
+ u8 monitor_vif;
+ struct ecrnx_sta sta_table[NX_REMOTE_STA_MAX + NX_VIRT_DEV_MAX];
+
+ // Channels
+ struct ecrnx_chanctx chanctx_table[NX_CHAN_CTXT_CNT];
+ u8 cur_chanctx;
+ struct ecrnx_survey_info survey[SCAN_CHANNEL_MAX];
+
+ /* RoC Management */
+ struct ecrnx_roc *roc;
+ u32 roc_cookie;
+ struct cfg80211_scan_request *scan_request;
+ spinlock_t scan_req_lock;
+ spinlock_t connect_req_lock;
+ struct ecrnx_radar radar;
+
+#ifdef CONFIG_ECRNX_P2P
+ struct ecrnx_p2p_listen p2p_listen;
+#endif
+
+ // TX path
+ spinlock_t tx_lock;
+ struct ecrnx_txq txq[NX_NB_TXQ];
+ struct ecrnx_hwq hwq[NX_TXQ_CNT];
+ struct timer_list txq_cleanup;
+ struct kmem_cache *sw_txhdr_cache;
+ u32 tcp_pacing_shift;
+#ifdef CONFIG_ECRNX_MUMIMO_TX
+ struct ecrnx_mu_info mu;
+#endif
+
+ // RX path
+ struct ecrnx_defer_rx defer_rx;
+ spinlock_t rx_lock;
+
+ struct tasklet_struct task;
+
+
+ /* IPC */
+ struct ipc_host_env_tag *ipc_env;
+ struct ecrnx_cmd_mgr cmd_mgr;
+ spinlock_t cb_lock;
+ struct ecrnx_ipc_elem_pool e2amsgs_pool;
+ struct ecrnx_ipc_elem_pool dbgmsgs_pool;
+ struct ecrnx_ipc_elem_pool e2aradars_pool;
+ struct ecrnx_ipc_elem_var pattern_elem;
+ struct ecrnx_ipc_dbgdump_elem dbgdump_elem;
+ struct ecrnx_ipc_elem_pool e2arxdesc_pool;
+ struct ecrnx_ipc_skb_elem *e2aunsuprxvec_elems;
+ struct ecrnx_ipc_rxbuf_elems rxbuf_elems;
+ struct ecrnx_ipc_elem_var scan_ie;
+
+#ifdef CONFIG_ECRNX_DEBUGFS
+ struct ecrnx_debugfs debugfs;
+#endif
+
+ struct ecrnx_stats stats;
+ struct ecrnx_conf_file conf_param;
+
+#ifdef CONFIG_ECRNX_ESWIN
+ struct list_head agg_rx_list;
+ struct list_head defrag_rx_list;
+#endif
+#ifdef CONFIG_ESWIN_RX_REORDER
+ spinlock_t rx_msdu_free_lock;
+ struct list_head rx_msdu_free_list;
+ struct list_head rx_reord_list;
+ spinlock_t rx_reord_lock;
+ struct reord_msdu_info * rx_reord_buf;
+#endif
+
+ /* extended capabilities supported */
+#if defined(CONFIG_ECRNX_DEBUGFS_CUSTOM)
+ struct list_head debugfs_survey_info_tbl_ptr;
+#endif
+ u32 msg_tx;
+ u32 msg_tx_done;
+ u32 data_tx;
+ u32 data_tx_done;
+ u32 usb_rx;
+ u32 msg_rx;
+ u32 data_rx;
+};
+
+u8 *ecrnx_build_bcn(struct ecrnx_bcn *bcn, struct cfg80211_beacon_data *new);
+
+void ecrnx_chanctx_link(struct ecrnx_vif *vif, u8 idx,
+ struct cfg80211_chan_def *chandef);
+void ecrnx_chanctx_unlink(struct ecrnx_vif *vif);
+int ecrnx_chanctx_valid(struct ecrnx_hw *ecrnx_hw, u8 idx);
+
+static inline bool is_multicast_sta(int sta_idx)
+{
+ return (sta_idx >= NX_REMOTE_STA_MAX);
+}
+struct ecrnx_sta *ecrnx_get_sta(struct ecrnx_hw *ecrnx_hw, const u8 *mac_addr);
+
+static inline uint8_t master_vif_idx(struct ecrnx_vif *vif)
+{
+ if (unlikely(vif->wdev.iftype == NL80211_IFTYPE_AP_VLAN)) {
+ return vif->ap_vlan.master->vif_index;
+ } else {
+ return vif->vif_index;
+ }
+}
+
+static inline void *ecrnx_get_shared_trace_buf(struct ecrnx_hw *ecrnx_hw)
+{
+#ifdef CONFIG_ECRNX_DEBUGFS
+ return (void *)&(ecrnx_hw->debugfs.fw_trace.buf);
+#else
+ return NULL;
+#endif
+}
+
+void ecrnx_external_auth_enable(struct ecrnx_vif *vif);
+void ecrnx_external_auth_disable(struct ecrnx_vif *vif);
+
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 19, 0)
+/* 802.11ax HE MAC capabilities */
+#define IEEE80211_HE_MAC_CAP0_HTC_HE 0x01
+#define IEEE80211_HE_MAC_CAP0_TWT_REQ 0x02
+#define IEEE80211_HE_MAC_CAP0_TWT_RES 0x04
+#define IEEE80211_HE_MAC_CAP0_DYNAMIC_FRAG_NOT_SUPP 0x00
+#define IEEE80211_HE_MAC_CAP0_DYNAMIC_FRAG_LEVEL_1 0x08
+#define IEEE80211_HE_MAC_CAP0_DYNAMIC_FRAG_LEVEL_2 0x10
+#define IEEE80211_HE_MAC_CAP0_DYNAMIC_FRAG_LEVEL_3 0x18
+#define IEEE80211_HE_MAC_CAP0_DYNAMIC_FRAG_MASK 0x18
+#define IEEE80211_HE_MAC_CAP0_MAX_NUM_FRAG_MSDU_1 0x00
+#define IEEE80211_HE_MAC_CAP0_MAX_NUM_FRAG_MSDU_2 0x20
+#define IEEE80211_HE_MAC_CAP0_MAX_NUM_FRAG_MSDU_4 0x40
+#define IEEE80211_HE_MAC_CAP0_MAX_NUM_FRAG_MSDU_8 0x60
+#define IEEE80211_HE_MAC_CAP0_MAX_NUM_FRAG_MSDU_16 0x80
+#define IEEE80211_HE_MAC_CAP0_MAX_NUM_FRAG_MSDU_32 0xa0
+#define IEEE80211_HE_MAC_CAP0_MAX_NUM_FRAG_MSDU_64 0xc0
+#define IEEE80211_HE_MAC_CAP0_MAX_NUM_FRAG_MSDU_UNLIMITED 0xe0
+#define IEEE80211_HE_MAC_CAP0_MAX_NUM_FRAG_MSDU_MASK 0xe0
+
+#define IEEE80211_HE_MAC_CAP1_MIN_FRAG_SIZE_UNLIMITED 0x00
+#define IEEE80211_HE_MAC_CAP1_MIN_FRAG_SIZE_128 0x01
+#define IEEE80211_HE_MAC_CAP1_MIN_FRAG_SIZE_256 0x02
+#define IEEE80211_HE_MAC_CAP1_MIN_FRAG_SIZE_512 0x03
+#define IEEE80211_HE_MAC_CAP1_MIN_FRAG_SIZE_MASK 0x03
+#define IEEE80211_HE_MAC_CAP1_TF_MAC_PAD_DUR_0US 0x00
+#define IEEE80211_HE_MAC_CAP1_TF_MAC_PAD_DUR_8US 0x04
+#define IEEE80211_HE_MAC_CAP1_TF_MAC_PAD_DUR_16US 0x08
+#define IEEE80211_HE_MAC_CAP1_TF_MAC_PAD_DUR_MASK 0x0c
+#define IEEE80211_HE_MAC_CAP1_MULTI_TID_AGG_RX_QOS_1 0x00
+#define IEEE80211_HE_MAC_CAP1_MULTI_TID_AGG_RX_QOS_2 0x10
+#define IEEE80211_HE_MAC_CAP1_MULTI_TID_AGG_RX_QOS_3 0x20
+#define IEEE80211_HE_MAC_CAP1_MULTI_TID_AGG_RX_QOS_4 0x30
+#define IEEE80211_HE_MAC_CAP1_MULTI_TID_AGG_RX_QOS_5 0x40
+#define IEEE80211_HE_MAC_CAP1_MULTI_TID_AGG_RX_QOS_6 0x50
+#define IEEE80211_HE_MAC_CAP1_MULTI_TID_AGG_RX_QOS_7 0x60
+#define IEEE80211_HE_MAC_CAP1_MULTI_TID_AGG_RX_QOS_8 0x70
+#define IEEE80211_HE_MAC_CAP1_MULTI_TID_AGG_RX_QOS_MASK 0x70
+
+/* Link adaptation is split between byte HE_MAC_CAP1 and
+ * HE_MAC_CAP2. It should be set only if IEEE80211_HE_MAC_CAP0_HTC_HE
+ * in which case the following values apply:
+ * 0 = No feedback.
+ * 1 = reserved.
+ * 2 = Unsolicited feedback.
+ * 3 = both
+ */
+#define IEEE80211_HE_MAC_CAP1_LINK_ADAPTATION 0x80
+
+#define IEEE80211_HE_MAC_CAP2_LINK_ADAPTATION 0x01
+#define IEEE80211_HE_MAC_CAP2_ALL_ACK 0x02
+#define IEEE80211_HE_MAC_CAP2_TRS 0x04
+#define IEEE80211_HE_MAC_CAP2_BSR 0x08
+#define IEEE80211_HE_MAC_CAP2_BCAST_TWT 0x10
+#define IEEE80211_HE_MAC_CAP2_32BIT_BA_BITMAP 0x20
+#define IEEE80211_HE_MAC_CAP2_MU_CASCADING 0x40
+#define IEEE80211_HE_MAC_CAP2_ACK_EN 0x80
+
+#define IEEE80211_HE_MAC_CAP3_OMI_CONTROL 0x02
+#define IEEE80211_HE_MAC_CAP3_OFDMA_RA 0x04
+
+/* The maximum length of an A-MDPU is defined by the combination of the Maximum
+ * A-MDPU Length Exponent field in the HT capabilities, VHT capabilities and the
+ * same field in the HE capabilities.
+ */
+#define IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_USE_VHT 0x00
+#define IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_VHT_1 0x08
+#define IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_VHT_2 0x10
+#define IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_RESERVED 0x18
+#define IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_MASK 0x18
+#define IEEE80211_HE_MAC_CAP3_AMSDU_FRAG 0x20
+#define IEEE80211_HE_MAC_CAP3_FLEX_TWT_SCHED 0x40
+#define IEEE80211_HE_MAC_CAP3_RX_CTRL_FRAME_TO_MULTIBSS 0x80
+
+#define IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_SHIFT 3
+
+#define IEEE80211_HE_MAC_CAP4_BSRP_BQRP_A_MPDU_AGG 0x01
+#define IEEE80211_HE_MAC_CAP4_QTP 0x02
+#define IEEE80211_HE_MAC_CAP4_BQR 0x04
+#define IEEE80211_HE_MAC_CAP4_SRP_RESP 0x08
+#define IEEE80211_HE_MAC_CAP4_NDP_FB_REP 0x10
+#define IEEE80211_HE_MAC_CAP4_OPS 0x20
+#define IEEE80211_HE_MAC_CAP4_AMDSU_IN_AMPDU 0x40
+/* Multi TID agg TX is split between byte #4 and #5
+ * The value is a combination of B39,B40,B41
+ */
+#define IEEE80211_HE_MAC_CAP4_MULTI_TID_AGG_TX_QOS_B39 0x80
+
+#define IEEE80211_HE_MAC_CAP5_MULTI_TID_AGG_TX_QOS_B40 0x01
+#define IEEE80211_HE_MAC_CAP5_MULTI_TID_AGG_TX_QOS_B41 0x02
+#define IEEE80211_HE_MAC_CAP5_SUBCHAN_SELECVITE_TRANSMISSION 0x04
+#define IEEE80211_HE_MAC_CAP5_UL_2x996_TONE_RU 0x08
+#define IEEE80211_HE_MAC_CAP5_OM_CTRL_UL_MU_DATA_DIS_RX 0x10
+#define IEEE80211_HE_MAC_CAP5_HE_DYNAMIC_SM_PS 0x20
+#define IEEE80211_HE_MAC_CAP5_PUNCTURED_SOUNDING 0x40
+#define IEEE80211_HE_MAC_CAP5_HT_VHT_TRIG_FRAME_RX 0x80
+
+#define IEEE80211_HE_VHT_MAX_AMPDU_FACTOR 20
+#define IEEE80211_HE_HT_MAX_AMPDU_FACTOR 16
+
+/* 802.11ax HE PHY capabilities */
+#define IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G 0x02
+#define IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G 0x04
+#define IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G 0x08
+#define IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G 0x10
+#define IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_RU_MAPPING_IN_2G 0x20
+#define IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_RU_MAPPING_IN_5G 0x40
+#define IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_MASK 0xfe
+
+#define IEEE80211_HE_PHY_CAP1_PREAMBLE_PUNC_RX_80MHZ_ONLY_SECOND_20MHZ 0x01
+#define IEEE80211_HE_PHY_CAP1_PREAMBLE_PUNC_RX_80MHZ_ONLY_SECOND_40MHZ 0x02
+#define IEEE80211_HE_PHY_CAP1_PREAMBLE_PUNC_RX_160MHZ_ONLY_SECOND_20MHZ 0x04
+#define IEEE80211_HE_PHY_CAP1_PREAMBLE_PUNC_RX_160MHZ_ONLY_SECOND_40MHZ 0x08
+#define IEEE80211_HE_PHY_CAP1_PREAMBLE_PUNC_RX_MASK 0x0f
+#define IEEE80211_HE_PHY_CAP1_DEVICE_CLASS_A 0x10
+#define IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD 0x20
+#define IEEE80211_HE_PHY_CAP1_HE_LTF_AND_GI_FOR_HE_PPDUS_0_8US 0x40
+/* Midamble RX/TX Max NSTS is split between byte #2 and byte #3 */
+#define IEEE80211_HE_PHY_CAP1_MIDAMBLE_RX_TX_MAX_NSTS 0x80
+
+#define IEEE80211_HE_PHY_CAP2_MIDAMBLE_RX_TX_MAX_NSTS 0x01
+#define IEEE80211_HE_PHY_CAP2_NDP_4x_LTF_AND_3_2US 0x02
+#define IEEE80211_HE_PHY_CAP2_STBC_TX_UNDER_80MHZ 0x04
+#define IEEE80211_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ 0x08
+#define IEEE80211_HE_PHY_CAP2_DOPPLER_TX 0x10
+#define IEEE80211_HE_PHY_CAP2_DOPPLER_RX 0x20
+
+/* Note that the meaning of UL MU below is different between an AP and a non-AP
+ * sta, where in the AP case it indicates support for Rx and in the non-AP sta
+ * case it indicates support for Tx.
+ */
+#define IEEE80211_HE_PHY_CAP2_UL_MU_FULL_MU_MIMO 0x40
+#define IEEE80211_HE_PHY_CAP2_UL_MU_PARTIAL_MU_MIMO 0x80
+
+#define IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_TX_NO_DCM 0x00
+#define IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_TX_BPSK 0x01
+#define IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_TX_QPSK 0x02
+#define IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_TX_16_QAM 0x03
+#define IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_TX_MASK 0x03
+#define IEEE80211_HE_PHY_CAP3_DCM_MAX_TX_NSS_1 0x00
+#define IEEE80211_HE_PHY_CAP3_DCM_MAX_TX_NSS_2 0x04
+#define IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_NO_DCM 0x00
+#define IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_BPSK 0x08
+#define IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_QPSK 0x10
+#define IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_16_QAM 0x18
+#define IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_MASK 0x18
+#define IEEE80211_HE_PHY_CAP3_DCM_MAX_RX_NSS_1 0x00
+#define IEEE80211_HE_PHY_CAP3_DCM_MAX_RX_NSS_2 0x20
+#define IEEE80211_HE_PHY_CAP3_RX_HE_MU_PPDU_FROM_NON_AP_STA 0x40
+#define IEEE80211_HE_PHY_CAP3_SU_BEAMFORMER 0x80
+
+#define IEEE80211_HE_PHY_CAP4_SU_BEAMFORMEE 0x01
+#define IEEE80211_HE_PHY_CAP4_MU_BEAMFORMER 0x02
+
+/* Minimal allowed value of Max STS under 80MHz is 3 */
+#define IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_UNDER_80MHZ_4 0x0c
+#define IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_UNDER_80MHZ_5 0x10
+#define IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_UNDER_80MHZ_6 0x14
+#define IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_UNDER_80MHZ_7 0x18
+#define IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_UNDER_80MHZ_8 0x1c
+#define IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_UNDER_80MHZ_MASK 0x1c
+
+/* Minimal allowed value of Max STS above 80MHz is 3 */
+#define IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_ABOVE_80MHZ_4 0x60
+#define IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_ABOVE_80MHZ_5 0x80
+#define IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_ABOVE_80MHZ_6 0xa0
+#define IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_ABOVE_80MHZ_7 0xc0
+#define IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_ABOVE_80MHZ_8 0xe0
+#define IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_ABOVE_80MHZ_MASK 0xe0
+
+#define IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_UNDER_80MHZ_1 0x00
+#define IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_UNDER_80MHZ_2 0x01
+#define IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_UNDER_80MHZ_3 0x02
+#define IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_UNDER_80MHZ_4 0x03
+#define IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_UNDER_80MHZ_5 0x04
+#define IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_UNDER_80MHZ_6 0x05
+#define IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_UNDER_80MHZ_7 0x06
+#define IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_UNDER_80MHZ_8 0x07
+#define IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_UNDER_80MHZ_MASK 0x07
+
+#define IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_ABOVE_80MHZ_1 0x00
+#define IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_ABOVE_80MHZ_2 0x08
+#define IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_ABOVE_80MHZ_3 0x10
+#define IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_ABOVE_80MHZ_4 0x18
+#define IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_ABOVE_80MHZ_5 0x20
+#define IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_ABOVE_80MHZ_6 0x28
+#define IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_ABOVE_80MHZ_7 0x30
+#define IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_ABOVE_80MHZ_8 0x38
+#define IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_ABOVE_80MHZ_MASK 0x38
+
+#define IEEE80211_HE_PHY_CAP5_NG16_SU_FEEDBACK 0x40
+#define IEEE80211_HE_PHY_CAP5_NG16_MU_FEEDBACK 0x80
+
+#define IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_42_SU 0x01
+#define IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_75_MU 0x02
+#define IEEE80211_HE_PHY_CAP6_TRIG_SU_BEAMFORMER_FB 0x04
+#define IEEE80211_HE_PHY_CAP6_TRIG_MU_BEAMFORMER_FB 0x08
+#define IEEE80211_HE_PHY_CAP6_TRIG_CQI_FB 0x10
+#define IEEE80211_HE_PHY_CAP6_PARTIAL_BW_EXT_RANGE 0x20
+#define IEEE80211_HE_PHY_CAP6_PARTIAL_BANDWIDTH_DL_MUMIMO 0x40
+#define IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT 0x80
+
+#define IEEE80211_HE_PHY_CAP7_SRP_BASED_SR 0x01
+#define IEEE80211_HE_PHY_CAP7_POWER_BOOST_FACTOR_AR 0x02
+#define IEEE80211_HE_PHY_CAP7_HE_SU_MU_PPDU_4XLTF_AND_08_US_GI 0x04
+#define IEEE80211_HE_PHY_CAP7_MAX_NC_1 0x08
+#define IEEE80211_HE_PHY_CAP7_MAX_NC_2 0x10
+#define IEEE80211_HE_PHY_CAP7_MAX_NC_3 0x18
+#define IEEE80211_HE_PHY_CAP7_MAX_NC_4 0x20
+#define IEEE80211_HE_PHY_CAP7_MAX_NC_5 0x28
+#define IEEE80211_HE_PHY_CAP7_MAX_NC_6 0x30
+#define IEEE80211_HE_PHY_CAP7_MAX_NC_7 0x38
+#define IEEE80211_HE_PHY_CAP7_MAX_NC_MASK 0x38
+#define IEEE80211_HE_PHY_CAP7_STBC_TX_ABOVE_80MHZ 0x40
+#define IEEE80211_HE_PHY_CAP7_STBC_RX_ABOVE_80MHZ 0x80
+
+#define IEEE80211_HE_PHY_CAP8_HE_ER_SU_PPDU_4XLTF_AND_08_US_GI 0x01
+#define IEEE80211_HE_PHY_CAP8_20MHZ_IN_40MHZ_HE_PPDU_IN_2G 0x02
+#define IEEE80211_HE_PHY_CAP8_20MHZ_IN_160MHZ_HE_PPDU 0x04
+#define IEEE80211_HE_PHY_CAP8_80MHZ_IN_160MHZ_HE_PPDU 0x08
+#define IEEE80211_HE_PHY_CAP8_HE_ER_SU_1XLTF_AND_08_US_GI 0x10
+#define IEEE80211_HE_PHY_CAP8_MIDAMBLE_RX_TX_2X_AND_1XLTF 0x20
+#define IEEE80211_HE_PHY_CAP8_DCM_MAX_RU_242 0x00
+#define IEEE80211_HE_PHY_CAP8_DCM_MAX_RU_484 0x40
+#define IEEE80211_HE_PHY_CAP8_DCM_MAX_RU_996 0x80
+#define IEEE80211_HE_PHY_CAP8_DCM_MAX_RU_2x996 0xc0
+#define IEEE80211_HE_PHY_CAP8_DCM_MAX_RU_MASK 0xc0
+
+#define IEEE80211_HE_PHY_CAP9_LONGER_THAN_16_SIGB_OFDM_SYM 0x01
+#define IEEE80211_HE_PHY_CAP9_NON_TRIGGERED_CQI_FEEDBACK 0x02
+#define IEEE80211_HE_PHY_CAP9_TX_1024_QAM_LESS_THAN_242_TONE_RU 0x04
+#define IEEE80211_HE_PHY_CAP9_RX_1024_QAM_LESS_THAN_242_TONE_RU 0x08
+
+/* 802.11ax HE TX/RX MCS NSS Support */
+#define IEEE80211_TX_RX_MCS_NSS_SUPP_HIGHEST_MCS_POS (3)
+#define IEEE80211_TX_RX_MCS_NSS_SUPP_TX_BITMAP_POS (6)
+#define IEEE80211_TX_RX_MCS_NSS_SUPP_RX_BITMAP_POS (11)
+#define IEEE80211_TX_RX_MCS_NSS_SUPP_TX_BITMAP_MASK 0x07c0
+#define IEEE80211_TX_RX_MCS_NSS_SUPP_RX_BITMAP_MASK 0xf800
+
+
+/* TX/RX HE MCS Support field Highest MCS subfield encoding */
+enum ieee80211_he_highest_mcs_supported_subfield_enc {
+ HIGHEST_MCS_SUPPORTED_MCS7 = 0,
+ HIGHEST_MCS_SUPPORTED_MCS8,
+ HIGHEST_MCS_SUPPORTED_MCS9,
+ HIGHEST_MCS_SUPPORTED_MCS10,
+ HIGHEST_MCS_SUPPORTED_MCS11,
+};
+
+#define IEEE80211_HE_PPE_THRES_MAX_LEN 25
+
+/**
+ * struct ieee80211_he_mcs_nss_supp - HE Tx/Rx HE MCS NSS Support Field
+ *
+ * This structure holds the data required for the Tx/Rx HE MCS NSS Support Field
+ * described in P802.11ax_D2.0 section 9.4.2.237.4
+ *
+ * @rx_mcs_80: Rx MCS map 2 bits for each stream, total 8 streams, for channel
+ * widths less than 80MHz.
+ * @tx_mcs_80: Tx MCS map 2 bits for each stream, total 8 streams, for channel
+ * widths less than 80MHz.
+ * @rx_mcs_160: Rx MCS map 2 bits for each stream, total 8 streams, for channel
+ * width 160MHz.
+ * @tx_mcs_160: Tx MCS map 2 bits for each stream, total 8 streams, for channel
+ * width 160MHz.
+ * @rx_mcs_80p80: Rx MCS map 2 bits for each stream, total 8 streams, for
+ * channel width 80p80MHz.
+ * @tx_mcs_80p80: Tx MCS map 2 bits for each stream, total 8 streams, for
+ * channel width 80p80MHz.
+ */
+struct ieee80211_he_mcs_nss_supp {
+ __le16 rx_mcs_80;
+ __le16 tx_mcs_80;
+ __le16 rx_mcs_160;
+ __le16 tx_mcs_160;
+ __le16 rx_mcs_80p80;
+ __le16 tx_mcs_80p80;
+} __packed;
+
+/**
+ * struct ieee80211_he_cap_elem - HE capabilities element
+ *
+ * This structure is the "HE capabilities element" fixed fields as
+ * described in P802.11ax_D2.0 section 9.4.2.237.2 and 9.4.2.237.3
+ */
+struct ieee80211_he_cap_elem {
+ u8 mac_cap_info[6];
+ u8 phy_cap_info[11];
+} __packed;
+
+/**
+ * struct ieee80211_sta_he_cap - STA's HE capabilities
+ *
+ * This structure describes most essential parameters needed
+ * to describe 802.11ax HE capabilities for a STA.
+ *
+ * @has_he: true iff HE data is valid.
+ * @he_cap_elem: Fixed portion of the HE capabilities element.
+ * @he_mcs_nss_supp: The supported NSS/MCS combinations.
+ * @ppe_thres: Holds the PPE Thresholds data.
+ */
+struct ieee80211_sta_he_cap {
+ bool has_he;
+ struct ieee80211_he_cap_elem he_cap_elem;
+ struct ieee80211_he_mcs_nss_supp he_mcs_nss_supp;
+ u8 ppe_thres[IEEE80211_HE_PPE_THRES_MAX_LEN];
+};
+
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 20, 0)
+#define IEEE80211_HE_PHY_CAP9_RX_FULL_BW_SU_USING_MU_WITH_COMP_SIGB 0x10
+#define IEEE80211_HE_PHY_CAP9_RX_FULL_BW_SU_USING_MU_WITH_NON_COMP_SIGB 0x20
+#define IEEE80211_HE_PHY_CAP9_NOMIMAL_PKT_PADDING_0US 0x00
+#define IEEE80211_HE_PHY_CAP9_NOMIMAL_PKT_PADDING_8US 0x40
+#define IEEE80211_HE_PHY_CAP9_NOMIMAL_PKT_PADDING_16US 0x80
+#define IEEE80211_HE_PHY_CAP9_NOMIMAL_PKT_PADDING_RESERVED 0xc0
+#define IEEE80211_HE_PHY_CAP9_NOMIMAL_PKT_PADDING_MASK 0xc0
+#else
+#define IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_0US 0x0
+#define IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_8US 0x1
+#define IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_16US 0x2
+#define IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_RESERVED 0x3
+#endif
+
+
+#if defined(CONFIG_ECRNX_HE)
+extern struct ieee80211_sta_he_cap ecrnx_he_cap;
+#endif
+
+#define RW_DRV_DESCRIPTION "ESWIN 11nac driver for Linux cfg80211"
+#define RW_DRV_COPYRIGHT "Copyright(c) 2015-2017 ESWIN"
+#define RW_DRV_AUTHOR "ESWIN S.A.S"
+
+#define ECRNX_PRINT_CFM_ERR(req) \
+ printk(KERN_CRIT "%s: Status Error(%d)\n", #req, (&req##_cfm)->status)
+
+#define ECRNX_HT_CAPABILITIES \
+{ \
+ .ht_supported = true, \
+ .cap = 0, \
+ .ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K, \
+ .ampdu_density = IEEE80211_HT_MPDU_DENSITY_16, \
+ .mcs = { \
+ .rx_mask = { 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, \
+ .rx_highest = cpu_to_le16(65), \
+ .tx_params = IEEE80211_HT_MCS_TX_DEFINED, \
+ }, \
+}
+
+#define ECRNX_VHT_CAPABILITIES \
+{ \
+ .vht_supported = false, \
+ .cap = \
+ (7 << IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT),\
+ .vht_mcs = { \
+ .rx_mcs_map = cpu_to_le16( \
+ IEEE80211_VHT_MCS_SUPPORT_0_9 << 0 | \
+ IEEE80211_VHT_MCS_NOT_SUPPORTED << 2 | \
+ IEEE80211_VHT_MCS_NOT_SUPPORTED << 4 | \
+ IEEE80211_VHT_MCS_NOT_SUPPORTED << 6 | \
+ IEEE80211_VHT_MCS_NOT_SUPPORTED << 8 | \
+ IEEE80211_VHT_MCS_NOT_SUPPORTED << 10 | \
+ IEEE80211_VHT_MCS_NOT_SUPPORTED << 12 | \
+ IEEE80211_VHT_MCS_NOT_SUPPORTED << 14), \
+ .tx_mcs_map = cpu_to_le16( \
+ IEEE80211_VHT_MCS_SUPPORT_0_9 << 0 | \
+ IEEE80211_VHT_MCS_NOT_SUPPORTED << 2 | \
+ IEEE80211_VHT_MCS_NOT_SUPPORTED << 4 | \
+ IEEE80211_VHT_MCS_NOT_SUPPORTED << 6 | \
+ IEEE80211_VHT_MCS_NOT_SUPPORTED << 8 | \
+ IEEE80211_VHT_MCS_NOT_SUPPORTED << 10 | \
+ IEEE80211_VHT_MCS_NOT_SUPPORTED << 12 | \
+ IEEE80211_VHT_MCS_NOT_SUPPORTED << 14), \
+ } \
+}
+
+#if CONFIG_ECRNX_HE
+#define ECRNX_HE_CAPABILITIES \
+{ \
+ .has_he = false, \
+ .he_cap_elem = { \
+ .mac_cap_info[0] = 0, \
+ .mac_cap_info[1] = 0, \
+ .mac_cap_info[2] = 0, \
+ .mac_cap_info[3] = 0, \
+ .mac_cap_info[4] = 0, \
+ .mac_cap_info[5] = 0, \
+ .phy_cap_info[0] = 0, \
+ .phy_cap_info[1] = 0, \
+ .phy_cap_info[2] = 0, \
+ .phy_cap_info[3] = 0, \
+ .phy_cap_info[4] = 0, \
+ .phy_cap_info[5] = 0, \
+ .phy_cap_info[6] = 0, \
+ .phy_cap_info[7] = 0, \
+ .phy_cap_info[8] = 0, \
+ .phy_cap_info[9] = 0, \
+ .phy_cap_info[10] = 0, \
+ }, \
+ .he_mcs_nss_supp = { \
+ .rx_mcs_80 = cpu_to_le16(0xfffa), \
+ .tx_mcs_80 = cpu_to_le16(0xfffa), \
+ .rx_mcs_160 = cpu_to_le16(0xffff), \
+ .tx_mcs_160 = cpu_to_le16(0xffff), \
+ .rx_mcs_80p80 = cpu_to_le16(0xffff), \
+ .tx_mcs_80p80 = cpu_to_le16(0xffff), \
+ }, \
+ .ppe_thres = {0x00}, \
+}
+#endif
+
+#define RATE(_bitrate, _hw_rate, _flags) { \
+ .bitrate = (_bitrate), \
+ .flags = (_flags), \
+ .hw_value = (_hw_rate), \
+}
+
+#define CHAN(_freq) { \
+ .center_freq = (_freq), \
+ .max_power = 30, /* FIXME */ \
+}
+
+#endif /* _ECRNX_DEFS_H_*/
diff --git a/drivers/net/wireless/eswin/fullmac/ecrnx_main.c b/drivers/net/wireless/eswin/fullmac/ecrnx_main.c
new file mode 100644
index 000000000000..dcb8d9d3b50a
--- /dev/null
+++ b/drivers/net/wireless/eswin/fullmac/ecrnx_main.c
@@ -0,0 +1,4492 @@
+/**
+ ******************************************************************************
+ *
+ * @file ecrnx_main.c
+ *
+ * @brief Entry point of the ECRNX driver
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/inetdevice.h>
+#include <net/cfg80211.h>
+#include <net/ip.h>
+#include <linux/etherdevice.h>
+
+#include "ecrnx_defs.h"
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0))
+#include <linux/if_arp.h>
+#include <linux/ieee80211.h>
+#endif
+#include "ecrnx_msg_tx.h"
+#include "ecrnx_tx.h"
+#include "reg_access.h"
+#include "hal_desc.h"
+#include "ecrnx_debugfs.h"
+#include "ecrnx_cfgfile.h"
+#include "ecrnx_radar.h"
+#include "ecrnx_version.h"
+#ifdef CONFIG_ECRNX_BFMER
+#include "ecrnx_bfmer.h"
+#endif //(CONFIG_ECRNX_BFMER)
+#include "ecrnx_tdls.h"
+#include "ecrnx_events.h"
+#include "ecrnx_compat.h"
+#include "ecrnx_rx.h"
+
+#include "ecrnx_p2p.h"
+#include "ecrnx_debugfs_custom.h"
+#include "ecrnx_calibration_data.h"
+#include "eswin_utils.h"
+#include "ecrnx_debugfs_func.h"
+
+
+static struct ieee80211_rate ecrnx_ratetable[] = {
+ RATE(10, 0x00, 0),
+ RATE(20, 0x01, IEEE80211_RATE_SHORT_PREAMBLE),
+ RATE(55, 0x02, IEEE80211_RATE_SHORT_PREAMBLE),
+ RATE(110, 0x03, IEEE80211_RATE_SHORT_PREAMBLE),
+ RATE(60, 0x04, 0),
+ RATE(90, 0x05, 0),
+ RATE(120, 0x06, 0),
+ RATE(180, 0x07, 0),
+ RATE(240, 0x08, 0),
+ RATE(360, 0x09, 0),
+ RATE(480, 0x0A, 0),
+ RATE(540, 0x0B, 0),
+};
+
+/* The channels indexes here are not used anymore */
+static struct ieee80211_channel ecrnx_2ghz_channels[] = {
+ CHAN(2412),
+ CHAN(2417),
+ CHAN(2422),
+ CHAN(2427),
+ CHAN(2432),
+ CHAN(2437),
+ CHAN(2442),
+ CHAN(2447),
+ CHAN(2452),
+ CHAN(2457),
+ CHAN(2462),
+ CHAN(2467),
+ CHAN(2472),
+ CHAN(2484),
+ // Extra channels defined only to be used for PHY measures.
+ // Enabled only if custregd and custchan parameters are set
+ CHAN(2390),
+ CHAN(2400),
+ CHAN(2410),
+ CHAN(2420),
+ CHAN(2430),
+ CHAN(2440),
+ CHAN(2450),
+ CHAN(2460),
+ CHAN(2470),
+ CHAN(2480),
+ CHAN(2490),
+ CHAN(2500),
+ CHAN(2510),
+};
+
+#ifdef CONFIG_ECRNX_5G
+static struct ieee80211_channel ecrnx_5ghz_channels[] = {
+ CHAN(5180), // 36 - 20MHz
+ CHAN(5200), // 40 - 20MHz
+ CHAN(5220), // 44 - 20MHz
+ CHAN(5240), // 48 - 20MHz
+ CHAN(5260), // 52 - 20MHz
+ CHAN(5280), // 56 - 20MHz
+ CHAN(5300), // 60 - 20MHz
+ CHAN(5320), // 64 - 20MHz
+ CHAN(5500), // 100 - 20MHz
+ CHAN(5520), // 104 - 20MHz
+ CHAN(5540), // 108 - 20MHz
+ CHAN(5560), // 112 - 20MHz
+ CHAN(5580), // 116 - 20MHz
+ CHAN(5600), // 120 - 20MHz
+ CHAN(5620), // 124 - 20MHz
+ CHAN(5640), // 128 - 20MHz
+ CHAN(5660), // 132 - 20MHz
+ CHAN(5680), // 136 - 20MHz
+ CHAN(5700), // 140 - 20MHz
+ CHAN(5720), // 144 - 20MHz
+ CHAN(5745), // 149 - 20MHz
+ CHAN(5765), // 153 - 20MHz
+ CHAN(5785), // 157 - 20MHz
+ CHAN(5805), // 161 - 20MHz
+ CHAN(5825), // 165 - 20MHz
+ // Extra channels defined only to be used for PHY measures.
+ // Enabled only if custregd and custchan parameters are set
+ CHAN(5190),
+ CHAN(5210),
+ CHAN(5230),
+ CHAN(5250),
+ CHAN(5270),
+ CHAN(5290),
+ CHAN(5310),
+ CHAN(5330),
+ CHAN(5340),
+ CHAN(5350),
+ CHAN(5360),
+ CHAN(5370),
+ CHAN(5380),
+ CHAN(5390),
+ CHAN(5400),
+ CHAN(5410),
+ CHAN(5420),
+ CHAN(5430),
+ CHAN(5440),
+ CHAN(5450),
+ CHAN(5460),
+ CHAN(5470),
+ CHAN(5480),
+ CHAN(5490),
+ CHAN(5510),
+ CHAN(5530),
+ CHAN(5550),
+ CHAN(5570),
+ CHAN(5590),
+ CHAN(5610),
+ CHAN(5630),
+ CHAN(5650),
+ CHAN(5670),
+ CHAN(5690),
+ CHAN(5710),
+ CHAN(5730),
+ CHAN(5750),
+ CHAN(5760),
+ CHAN(5770),
+ CHAN(5780),
+ CHAN(5790),
+ CHAN(5800),
+ CHAN(5810),
+ CHAN(5820),
+ CHAN(5830),
+ CHAN(5840),
+ CHAN(5850),
+ CHAN(5860),
+ CHAN(5870),
+ CHAN(5880),
+ CHAN(5890),
+ CHAN(5900),
+ CHAN(5910),
+ CHAN(5920),
+ CHAN(5930),
+ CHAN(5940),
+ CHAN(5950),
+ CHAN(5960),
+ CHAN(5970),
+};
+#endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0)
+#if CONFIG_ECRNX_HE
+static struct ieee80211_sband_iftype_data ecrnx_he_capa = {
+ .types_mask = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_AP),
+ .he_cap = ECRNX_HE_CAPABILITIES,
+};
+#endif
+#endif
+
+static struct ieee80211_supported_band ecrnx_band_2GHz = {
+ .channels = ecrnx_2ghz_channels,
+ .n_channels = ARRAY_SIZE(ecrnx_2ghz_channels) - 13, // -13 to exclude extra channels
+ .bitrates = ecrnx_ratetable,
+ .n_bitrates = ARRAY_SIZE(ecrnx_ratetable),
+ .ht_cap = ECRNX_HT_CAPABILITIES,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0)
+#if CONFIG_ECRNX_HE
+ .iftype_data = &ecrnx_he_capa,
+ .n_iftype_data = 1,
+#endif
+#endif
+};
+
+#ifdef CONFIG_ECRNX_5G
+static struct ieee80211_supported_band ecrnx_band_5GHz = {
+ .channels = ecrnx_5ghz_channels,
+ .n_channels = ARRAY_SIZE(ecrnx_5ghz_channels) - 59, // -59 to exclude extra channels
+ .bitrates = &ecrnx_ratetable[4],
+ .n_bitrates = ARRAY_SIZE(ecrnx_ratetable) - 4,
+ .ht_cap = ECRNX_HT_CAPABILITIES,
+ .vht_cap = ECRNX_VHT_CAPABILITIES,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0)
+#if CONFIG_ECRNX_HE
+ .iftype_data = &ecrnx_he_capa,
+ .n_iftype_data = 1,
+#endif
+#endif
+};
+#endif
+
+static struct ieee80211_iface_limit ecrnx_limits[] = {
+ { .max = NX_VIRT_DEV_MAX, .types = BIT(NL80211_IFTYPE_AP) |
+ BIT(NL80211_IFTYPE_STATION)}
+};
+
+static struct ieee80211_iface_limit ecrnx_limits_dfs[] = {
+ { .max = NX_VIRT_DEV_MAX, .types = BIT(NL80211_IFTYPE_AP)}
+};
+
+static const struct ieee80211_iface_combination ecrnx_combinations[] = {
+ {
+ .limits = ecrnx_limits,
+ .n_limits = ARRAY_SIZE(ecrnx_limits),
+ .num_different_channels = NX_CHAN_CTXT_CNT,
+ .max_interfaces = NX_VIRT_DEV_MAX,
+ },
+ /* Keep this combination as the last one */
+ {
+ .limits = ecrnx_limits_dfs,
+ .n_limits = ARRAY_SIZE(ecrnx_limits_dfs),
+ .num_different_channels = 1,
+ .max_interfaces = NX_VIRT_DEV_MAX,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+ .radar_detect_widths = (BIT(NL80211_CHAN_WIDTH_20_NOHT) |
+ BIT(NL80211_CHAN_WIDTH_20) |
+ BIT(NL80211_CHAN_WIDTH_40) |
+ BIT(NL80211_CHAN_WIDTH_80)),
+#endif
+ }
+};
+
+/* There isn't a lot of sense in it, but you can transmit anything you like */
+static struct ieee80211_txrx_stypes
+ecrnx_default_mgmt_stypes[NUM_NL80211_IFTYPES] = {
+ [NL80211_IFTYPE_STATION] = {
+ .tx = 0xffff,
+ .rx = (BIT(IEEE80211_STYPE_ACTION >> 4) |
+ BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
+ BIT(IEEE80211_STYPE_AUTH >> 4)),
+ },
+ [NL80211_IFTYPE_AP] = {
+ .tx = 0xffff,
+ .rx = (BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |
+ BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) |
+ BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
+ BIT(IEEE80211_STYPE_DISASSOC >> 4) |
+ BIT(IEEE80211_STYPE_AUTH >> 4) |
+ BIT(IEEE80211_STYPE_DEAUTH >> 4) |
+ BIT(IEEE80211_STYPE_ACTION >> 4)),
+ },
+ [NL80211_IFTYPE_AP_VLAN] = {
+ /* copy AP */
+ .tx = 0xffff,
+ .rx = (BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |
+ BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) |
+ BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
+ BIT(IEEE80211_STYPE_DISASSOC >> 4) |
+ BIT(IEEE80211_STYPE_AUTH >> 4) |
+ BIT(IEEE80211_STYPE_DEAUTH >> 4) |
+ BIT(IEEE80211_STYPE_ACTION >> 4)),
+ },
+ [NL80211_IFTYPE_P2P_CLIENT] = {
+ .tx = 0xffff,
+ .rx = (BIT(IEEE80211_STYPE_ACTION >> 4) |
+ BIT(IEEE80211_STYPE_PROBE_REQ >> 4)),
+ },
+ [NL80211_IFTYPE_P2P_GO] = {
+ .tx = 0xffff,
+ .rx = (BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |
+ BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) |
+ BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
+ BIT(IEEE80211_STYPE_DISASSOC >> 4) |
+ BIT(IEEE80211_STYPE_AUTH >> 4) |
+ BIT(IEEE80211_STYPE_DEAUTH >> 4) |
+ BIT(IEEE80211_STYPE_ACTION >> 4)),
+ },
+ [NL80211_IFTYPE_P2P_DEVICE] = {
+ .tx = 0xffff,
+ .rx = (BIT(IEEE80211_STYPE_ACTION >> 4) |
+ BIT(IEEE80211_STYPE_PROBE_REQ >> 4)),
+ },
+ [NL80211_IFTYPE_MESH_POINT] = {
+ .tx = 0xffff,
+ .rx = (BIT(IEEE80211_STYPE_ACTION >> 4) |
+ BIT(IEEE80211_STYPE_AUTH >> 4) |
+ BIT(IEEE80211_STYPE_DEAUTH >> 4)),
+ },
+};
+
+
+static u32 cipher_suites[] = {
+ WLAN_CIPHER_SUITE_WEP40,
+ WLAN_CIPHER_SUITE_WEP104,
+ WLAN_CIPHER_SUITE_TKIP,
+ WLAN_CIPHER_SUITE_CCMP,
+ 0, // reserved entries to enable AES-CMAC and/or SMS4
+ 0,
+ 0,
+ 0,
+ 0,
+};
+#define NB_RESERVED_CIPHER 5;
+
+static const int ecrnx_ac2hwq[1][NL80211_NUM_ACS] = {
+ {
+ [NL80211_TXQ_Q_VO] = ECRNX_HWQ_VO,
+ [NL80211_TXQ_Q_VI] = ECRNX_HWQ_VI,
+ [NL80211_TXQ_Q_BE] = ECRNX_HWQ_BE,
+ [NL80211_TXQ_Q_BK] = ECRNX_HWQ_BK
+ }
+};
+
+const int ecrnx_tid2hwq[IEEE80211_NUM_TIDS] = {
+ ECRNX_HWQ_BE,
+ ECRNX_HWQ_BK,
+ ECRNX_HWQ_BK,
+ ECRNX_HWQ_BE,
+ ECRNX_HWQ_VI,
+ ECRNX_HWQ_VI,
+ ECRNX_HWQ_VO,
+ ECRNX_HWQ_VO,
+ /* TID_8 is used for management frames */
+ ECRNX_HWQ_VO,
+ /* At the moment, all others TID are mapped to BE */
+ ECRNX_HWQ_BE,
+ ECRNX_HWQ_BE,
+ ECRNX_HWQ_BE,
+ ECRNX_HWQ_BE,
+ ECRNX_HWQ_BE,
+ ECRNX_HWQ_BE,
+ ECRNX_HWQ_BE,
+};
+
+static const int ecrnx_hwq2uapsd[NL80211_NUM_ACS] = {
+ [ECRNX_HWQ_VO] = IEEE80211_WMM_IE_STA_QOSINFO_AC_VO,
+ [ECRNX_HWQ_VI] = IEEE80211_WMM_IE_STA_QOSINFO_AC_VI,
+ [ECRNX_HWQ_BE] = IEEE80211_WMM_IE_STA_QOSINFO_AC_BE,
+ [ECRNX_HWQ_BK] = IEEE80211_WMM_IE_STA_QOSINFO_AC_BK,
+};
+
+/*********************************************************************
+ * helper
+ *********************************************************************/
+struct ecrnx_sta *ecrnx_get_sta(struct ecrnx_hw *ecrnx_hw, const u8 *mac_addr)
+{
+ int i;
+
+ for (i = 0; i < NX_REMOTE_STA_MAX; i++) {
+ struct ecrnx_sta *sta = &ecrnx_hw->sta_table[i];
+ if (sta->valid && (memcmp(mac_addr, &sta->mac_addr, 6) == 0))
+ return sta;
+ }
+
+ return NULL;
+}
+
+void ecrnx_enable_wapi(struct ecrnx_hw *ecrnx_hw)
+{
+ cipher_suites[ecrnx_hw->wiphy->n_cipher_suites] = WLAN_CIPHER_SUITE_SMS4;
+ ecrnx_hw->wiphy->n_cipher_suites ++;
+ ecrnx_hw->wiphy->flags |= WIPHY_FLAG_CONTROL_PORT_PROTOCOL;
+}
+
+void ecrnx_enable_mfp(struct ecrnx_hw *ecrnx_hw)
+{
+ cipher_suites[ecrnx_hw->wiphy->n_cipher_suites] = WLAN_CIPHER_SUITE_AES_CMAC;
+ ecrnx_hw->wiphy->n_cipher_suites ++;
+}
+
+void ecrnx_enable_gcmp(struct ecrnx_hw *ecrnx_hw)
+{
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0))
+ cipher_suites[ecrnx_hw->wiphy->n_cipher_suites++] = WLAN_CIPHER_SUITE_CCMP_256;
+ cipher_suites[ecrnx_hw->wiphy->n_cipher_suites++] = WLAN_CIPHER_SUITE_GCMP;
+ cipher_suites[ecrnx_hw->wiphy->n_cipher_suites++] = WLAN_CIPHER_SUITE_GCMP_256;
+#endif
+}
+u8 *ecrnx_build_bcn(struct ecrnx_bcn *bcn, struct cfg80211_beacon_data *new)
+{
+ u8 *buf, *pos;
+
+ if (new->head) {
+ u8 *head = kmalloc(new->head_len, GFP_KERNEL);
+
+ if (!head)
+ return NULL;
+
+ if (bcn->head)
+ kfree(bcn->head);
+
+ bcn->head = head;
+ bcn->head_len = new->head_len;
+ memcpy(bcn->head, new->head, new->head_len);
+ }
+ if (new->tail) {
+ u8 *tail = kmalloc(new->tail_len, GFP_KERNEL);
+
+ if (!tail)
+ return NULL;
+
+ if (bcn->tail)
+ kfree(bcn->tail);
+
+ bcn->tail = tail;
+ bcn->tail_len = new->tail_len;
+ memcpy(bcn->tail, new->tail, new->tail_len);
+ }
+
+ if (!bcn->head)
+ return NULL;
+
+ bcn->tim_len = 6;
+ bcn->len = bcn->head_len + bcn->tail_len + bcn->ies_len + bcn->tim_len;
+
+ buf = kmalloc(bcn->len, GFP_KERNEL);
+ if (!buf)
+ return NULL;
+
+ // Build the beacon buffer
+ pos = buf;
+ memcpy(pos, bcn->head, bcn->head_len);
+ pos += bcn->head_len;
+ *pos++ = WLAN_EID_TIM;
+ *pos++ = 4;
+ *pos++ = 0;
+ *pos++ = bcn->dtim;
+ *pos++ = 0;
+ *pos++ = 0;
+ if (bcn->tail) {
+ memcpy(pos, bcn->tail, bcn->tail_len);
+ pos += bcn->tail_len;
+ }
+ if (bcn->ies) {
+ memcpy(pos, bcn->ies, bcn->ies_len);
+ }
+
+ return buf;
+}
+
+
+static void ecrnx_del_bcn(struct ecrnx_bcn *bcn)
+{
+ if (bcn->head) {
+ kfree(bcn->head);
+ bcn->head = NULL;
+ }
+ bcn->head_len = 0;
+
+ if (bcn->tail) {
+ kfree(bcn->tail);
+ bcn->tail = NULL;
+ }
+ bcn->tail_len = 0;
+
+ if (bcn->ies) {
+ kfree(bcn->ies);
+ bcn->ies = NULL;
+ }
+ bcn->ies_len = 0;
+ bcn->tim_len = 0;
+ bcn->dtim = 0;
+ bcn->len = 0;
+}
+
+/**
+ * Link channel ctxt to a vif and thus increments count for this context.
+ */
+void ecrnx_chanctx_link(struct ecrnx_vif *vif, u8 ch_idx,
+ struct cfg80211_chan_def *chandef)
+{
+ struct ecrnx_chanctx *ctxt;
+
+ if (ch_idx >= NX_CHAN_CTXT_CNT) {
+ WARN(1, "Invalid channel ctxt id %d", ch_idx);
+ return;
+ }
+
+ vif->ch_index = ch_idx;
+ ctxt = &vif->ecrnx_hw->chanctx_table[ch_idx];
+ ctxt->count++;
+
+ // For now chandef is NULL for STATION interface
+ if (chandef) {
+ if (!ctxt->chan_def.chan)
+ ctxt->chan_def = *chandef;
+ else {
+ // TODO. check that chandef is the same as the one already
+ // set for this ctxt
+ }
+ }
+}
+
+/**
+ * Unlink channel ctxt from a vif and thus decrements count for this context
+ */
+void ecrnx_chanctx_unlink(struct ecrnx_vif *vif)
+{
+ struct ecrnx_chanctx *ctxt;
+
+ if (vif->ch_index == ECRNX_CH_NOT_SET)
+ return;
+
+ ctxt = &vif->ecrnx_hw->chanctx_table[vif->ch_index];
+
+ if (ctxt->count == 0) {
+ WARN(1, "Chan ctxt ref count is already 0");
+ } else {
+ ctxt->count--;
+ }
+
+ if (ctxt->count == 0) {
+ if (vif->ch_index == vif->ecrnx_hw->cur_chanctx) {
+ /* If current chan ctxt is no longer linked to a vif
+ disable radar detection (no need to check if it was activated) */
+ ecrnx_radar_detection_enable(&vif->ecrnx_hw->radar,
+ ECRNX_RADAR_DETECT_DISABLE,
+ ECRNX_RADAR_RIU);
+ }
+ /* set chan to null, so that if this ctxt is relinked to a vif that
+ don't have channel information, don't use wrong information */
+ ctxt->chan_def.chan = NULL;
+ }
+ vif->ch_index = ECRNX_CH_NOT_SET;
+}
+
+int ecrnx_chanctx_valid(struct ecrnx_hw *ecrnx_hw, u8 ch_idx)
+{
+ if (ch_idx >= NX_CHAN_CTXT_CNT ||
+ ecrnx_hw->chanctx_table[ch_idx].chan_def.chan == NULL) {
+ return 0;
+ }
+
+ return 1;
+}
+
+static void ecrnx_del_csa(struct ecrnx_vif *vif)
+{
+ struct ecrnx_hw *ecrnx_hw = vif->ecrnx_hw;
+ struct ecrnx_csa *csa = vif->ap.csa;
+
+ if (!csa)
+ return;
+
+ ecrnx_ipc_elem_var_deallocs(ecrnx_hw, &csa->elem);
+ ecrnx_del_bcn(&csa->bcn);
+ kfree(csa);
+ vif->ap.csa = NULL;
+}
+
+static void ecrnx_csa_finish(struct work_struct *ws)
+{
+ struct ecrnx_csa *csa = container_of(ws, struct ecrnx_csa, work);
+ struct ecrnx_vif *vif = csa->vif;
+ struct ecrnx_hw *ecrnx_hw = vif->ecrnx_hw;
+ int error = csa->status;
+
+ if (!error)
+ error = ecrnx_send_bcn_change(ecrnx_hw, vif->vif_index, csa->elem.dma_addr,
+ csa->bcn.len, csa->bcn.head_len,
+ csa->bcn.tim_len, NULL);
+
+ if (error){
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0))
+ cfg80211_stop_iface(ecrnx_hw->wiphy, &vif->wdev, GFP_KERNEL);
+#else
+ cfg80211_disconnected(vif->ndev, 0, NULL, 0, 0, GFP_KERNEL);
+#endif
+ }
+ else {
+ mutex_lock(&vif->wdev.mtx);
+ __acquire(&vif->wdev.mtx);
+ spin_lock_bh(&ecrnx_hw->cb_lock);
+ ecrnx_chanctx_unlink(vif);
+ ecrnx_chanctx_link(vif, csa->ch_idx, &csa->chandef);
+ if (ecrnx_hw->cur_chanctx == csa->ch_idx) {
+ ecrnx_radar_detection_enable_on_cur_channel(ecrnx_hw);
+ ecrnx_txq_vif_start(vif, ECRNX_TXQ_STOP_CHAN, ecrnx_hw);
+ } else
+ ecrnx_txq_vif_stop(vif, ECRNX_TXQ_STOP_CHAN, ecrnx_hw);
+ spin_unlock_bh(&ecrnx_hw->cb_lock);
+ cfg80211_ch_switch_notify(vif->ndev, &csa->chandef, 0, 0);
+ mutex_unlock(&vif->wdev.mtx);
+ __release(&vif->wdev.mtx);
+ }
+ ecrnx_del_csa(vif);
+}
+
+/**
+ * ecrnx_external_auth_enable - Enable external authentication on a vif
+ *
+ * @vif: VIF on which external authentication must be enabled
+ *
+ * External authentication requires to start TXQ for unknown STA in
+ * order to send auth frame pusehd by user space.
+ * Note: It is assumed that fw is on the correct channel.
+ */
+void ecrnx_external_auth_enable(struct ecrnx_vif *vif)
+{
+ vif->sta.flags |= ECRNX_STA_EXT_AUTH;
+ ecrnx_txq_unk_vif_init(vif);
+ ecrnx_txq_start(ecrnx_txq_vif_get(vif, NX_UNK_TXQ_TYPE), 0);
+}
+
+/**
+ * ecrnx_external_auth_disable - Disable external authentication on a vif
+ *
+ * @vif: VIF on which external authentication must be disabled
+ */
+void ecrnx_external_auth_disable(struct ecrnx_vif *vif)
+{
+ if (!(vif->sta.flags & ECRNX_STA_EXT_AUTH))
+ return;
+
+ vif->sta.flags &= ~ECRNX_STA_EXT_AUTH;
+ ecrnx_txq_unk_vif_deinit(vif);
+}
+
+/**
+ * ecrnx_update_mesh_power_mode -
+ *
+ * @vif: mesh VIF for which power mode is updated
+ *
+ * Does nothing if vif is not a mesh point interface.
+ * Since firmware doesn't support one power save mode per link select the
+ * most "active" power mode among all mesh links.
+ * Indeed as soon as we have to be active on one link we might as well be
+ * active on all links.
+ *
+ * If there is no link then the power mode for next peer is used;
+ */
+void ecrnx_update_mesh_power_mode(struct ecrnx_vif *vif)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+ enum nl80211_mesh_power_mode mesh_pm;
+ struct ecrnx_sta *sta;
+ struct mesh_config mesh_conf;
+ struct mesh_update_cfm cfm;
+ u32 mask;
+
+ if (ECRNX_VIF_TYPE(vif) != NL80211_IFTYPE_MESH_POINT)
+ return;
+
+ if (list_empty(&vif->ap.sta_list)) {
+ mesh_pm = vif->ap.next_mesh_pm;
+ } else {
+ mesh_pm = NL80211_MESH_POWER_DEEP_SLEEP;
+ list_for_each_entry(sta, &vif->ap.sta_list, list) {
+ if (sta->valid && (sta->mesh_pm < mesh_pm)) {
+ mesh_pm = sta->mesh_pm;
+ }
+ }
+ }
+
+ if (mesh_pm == vif->ap.mesh_pm)
+ return;
+
+ mask = BIT(NL80211_MESHCONF_POWER_MODE - 1);
+ mesh_conf.power_mode = mesh_pm;
+ if (ecrnx_send_mesh_update_req(vif->ecrnx_hw, vif, mask, &mesh_conf, &cfm) ||
+ cfm.status)
+ return;
+
+ vif->ap.mesh_pm = mesh_pm;
+#endif
+}
+
+void ecrnx_save_assoc_info_for_ft(struct ecrnx_vif *vif,
+ struct cfg80211_connect_params *sme)
+{
+ int ies_len = sme->ie_len + sme->ssid_len + 2;
+ u8 *pos;
+ if (!vif->sta.ft_assoc_ies) {
+ if (!cfg80211_find_ie(WLAN_EID_MOBILITY_DOMAIN, sme->ie, sme->ie_len))
+ return;
+ vif->sta.ft_assoc_ies_len = ies_len;
+ vif->sta.ft_assoc_ies = kmalloc(ies_len, GFP_KERNEL);
+ } else if (vif->sta.ft_assoc_ies_len < ies_len) {
+ kfree(vif->sta.ft_assoc_ies);
+ vif->sta.ft_assoc_ies = kmalloc(ies_len, GFP_KERNEL);
+ }
+ if (!vif->sta.ft_assoc_ies)
+ return;
+ pos = vif->sta.ft_assoc_ies;
+ *pos++ = WLAN_EID_SSID;
+ *pos++ = sme->ssid_len;
+ memcpy(pos, sme->ssid, sme->ssid_len);
+ pos += sme->ssid_len;
+ memcpy(pos, sme->ie, sme->ie_len);
+ vif->sta.ft_assoc_ies_len = ies_len;
+}
+/**
+ * ecrnx_rsne_to_connect_params - Initialise cfg80211_connect_params from
+ * RSN element.
+ *
+ * @rsne: RSN element
+ * @sme: Structure cfg80211_connect_params to initialize
+ *
+ * The goal is only to initialize enough for ecrnx_send_sm_connect_req
+ */
+int ecrnx_rsne_to_connect_params(const struct ecrnx_element *rsne,
+ struct cfg80211_connect_params *sme)
+{
+ int len = rsne->datalen;
+ int clen;
+ const u8 *pos = rsne->data ;
+ if (len < 8)
+ return 1;
+
+ sme->crypto.control_port_no_encrypt = false;
+ sme->crypto.control_port = true;
+ sme->crypto.control_port_ethertype = cpu_to_be16(ETH_P_PAE);
+
+ pos += 2;
+ sme->crypto.cipher_group = ntohl(*((u32 *)pos));
+ pos += 4;
+ clen = le16_to_cpu(*((u16 *)pos)) * 4;
+ pos += 2;
+ len -= 8;
+ if (len < clen + 2)
+ return 1;
+ // only need one cipher suite
+ sme->crypto.n_ciphers_pairwise = 1;
+ sme->crypto.ciphers_pairwise[0] = ntohl(*((u32 *)pos));
+ pos += clen;
+ len -= clen;
+
+ // no need for AKM
+ clen = le16_to_cpu(*((u16 *)pos)) * 4;
+ pos += 2;
+ len -= 2;
+ if (len < clen)
+ return 1;
+ pos += clen;
+ len -= clen;
+
+ if (len < 4)
+ return 0;
+
+ pos += 2;
+ clen = le16_to_cpu(*((u16 *)pos)) * 16;
+ len -= 4;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+ if (len > clen)
+ sme->mfp = NL80211_MFP_REQUIRED;
+#endif
+
+ return 0;
+}
+
+/*********************************************************************
+ * netdev callbacks
+ ********************************************************************/
+/**
+ * int (*ndo_open)(struct net_device *dev);
+ * This function is called when network device transistions to the up
+ * state.
+ *
+ * - Start FW if this is the first interface opened
+ * - Add interface at fw level
+ */
+static int ecrnx_open(struct net_device *dev)
+{
+ struct ecrnx_vif *ecrnx_vif = netdev_priv(dev);
+ struct ecrnx_hw *ecrnx_hw = ecrnx_vif->ecrnx_hw;
+ struct mm_add_if_cfm add_if_cfm;
+ int error = 0;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ // Check if it is the first opened VIF
+ if (ecrnx_hw->vif_started == 0)
+ {
+ // Start the FW
+ if ((error = ecrnx_send_start(ecrnx_hw)))
+ return error;
+
+ /* Device is now started */
+ set_bit(ECRNX_DEV_STARTED, &ecrnx_hw->flags);
+ }
+
+ if (ecrnx_vif->up) {
+ netdev_info(dev, "Started repeatedly");
+ return error;
+ }
+
+ if (ECRNX_VIF_TYPE(ecrnx_vif) == NL80211_IFTYPE_AP_VLAN) {
+ /* For AP_vlan use same fw and drv indexes. We ensure that this index
+ will not be used by fw for another vif by taking index >= NX_VIRT_DEV_MAX */
+ add_if_cfm.inst_nbr = ecrnx_vif->drv_vif_index;
+ netif_tx_stop_all_queues(dev);
+ } else {
+ /* Forward the information to the LMAC,
+ * p2p value not used in FMAC configuration, iftype is sufficient */
+ if ((error = ecrnx_send_add_if(ecrnx_hw, dev->dev_addr,
+ ECRNX_VIF_TYPE(ecrnx_vif), false, &add_if_cfm)))
+ return error;
+
+ if (add_if_cfm.status != 0) {
+ ECRNX_PRINT_CFM_ERR(add_if);
+ return -EIO;
+ }
+ }
+
+ /* Save the index retrieved from LMAC */
+ spin_lock_bh(&ecrnx_hw->cb_lock);
+ ecrnx_vif->vif_index = add_if_cfm.inst_nbr;
+ ecrnx_vif->up = true;
+ ecrnx_hw->vif_started++;
+ ecrnx_hw->vif_table[add_if_cfm.inst_nbr] = ecrnx_vif;
+ memset(ecrnx_hw->vif_table[add_if_cfm.inst_nbr]->rx_pn, 0, TID_MAX * sizeof(uint64_t));
+ spin_unlock_bh(&ecrnx_hw->cb_lock);
+
+ if (ECRNX_VIF_TYPE(ecrnx_vif) == NL80211_IFTYPE_MONITOR) {
+ ecrnx_hw->monitor_vif = ecrnx_vif->vif_index;
+ if (ecrnx_vif->ch_index != ECRNX_CH_NOT_SET) {
+ //Configure the monitor channel
+ error = ecrnx_send_config_monitor_req(ecrnx_hw,
+ &ecrnx_hw->chanctx_table[ecrnx_vif->ch_index].chan_def,
+ NULL);
+ }
+ }
+
+ netif_carrier_off(dev);
+
+ return error;
+}
+
+/**
+ * int (*ndo_stop)(struct net_device *dev);
+ * This function is called when network device transistions to the down
+ * state.
+ *
+ * - Remove interface at fw level
+ * - Reset FW if this is the last interface opened
+ */
+static int ecrnx_close(struct net_device *dev)
+{
+ struct ecrnx_vif *ecrnx_vif = netdev_priv(dev);
+ struct ecrnx_hw *ecrnx_hw = ecrnx_vif->ecrnx_hw;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ netdev_info(dev, "CLOSE");
+
+ ecrnx_radar_cancel_cac(&ecrnx_hw->radar);
+
+ spin_lock_bh(&ecrnx_hw->scan_req_lock);
+ /* Abort scan request on the vif */
+ if (ecrnx_hw->scan_request &&
+ ecrnx_hw->scan_request->wdev == &ecrnx_vif->wdev) {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0)
+ struct cfg80211_scan_info info = {
+ .aborted = true,
+ };
+
+ cfg80211_scan_done(ecrnx_hw->scan_request, &info);
+#else
+ cfg80211_scan_done(ecrnx_hw->scan_request, true);
+#endif
+ ecrnx_hw->scan_request = NULL;
+ }
+
+ spin_unlock_bh(&ecrnx_hw->scan_req_lock);
+ ecrnx_send_remove_if(ecrnx_hw, ecrnx_vif->vif_index);
+
+ if (ecrnx_hw->roc && (ecrnx_hw->roc->vif == ecrnx_vif)) {
+ kfree(ecrnx_hw->roc);
+ /* Initialize RoC element pointer to NULL, indicate that RoC can be started */
+ ecrnx_hw->roc = NULL;
+ }
+
+ /* Ensure that we won't process disconnect ind */
+ spin_lock_bh(&ecrnx_hw->cb_lock);
+
+ ecrnx_vif->up = false;
+ if (netif_carrier_ok(dev)) {
+ if (ECRNX_VIF_TYPE(ecrnx_vif) == NL80211_IFTYPE_STATION ||
+ ECRNX_VIF_TYPE(ecrnx_vif) == NL80211_IFTYPE_P2P_CLIENT) {
+ if (ecrnx_vif->sta.ft_assoc_ies) {
+ kfree(ecrnx_vif->sta.ft_assoc_ies);
+ ecrnx_vif->sta.ft_assoc_ies = NULL;
+ ecrnx_vif->sta.ft_assoc_ies_len = 0;
+ }
+ cfg80211_disconnected(dev, WLAN_REASON_DEAUTH_LEAVING,
+ NULL, 0, true, GFP_ATOMIC);
+ if (ecrnx_vif->sta.ap) {
+ ecrnx_txq_sta_deinit(ecrnx_hw, ecrnx_vif->sta.ap);
+ ecrnx_txq_tdls_vif_deinit(ecrnx_vif);
+ }
+ netif_tx_stop_all_queues(dev);
+ netif_carrier_off(dev);
+ } else if (ECRNX_VIF_TYPE(ecrnx_vif) == NL80211_IFTYPE_AP_VLAN) {
+ netif_carrier_off(dev);
+ } else {
+ netdev_warn(dev, "AP not stopped when disabling interface");
+ }
+ }
+
+ ecrnx_hw->vif_table[ecrnx_vif->vif_index] = NULL;
+ spin_unlock_bh(&ecrnx_hw->cb_lock);
+
+ ecrnx_chanctx_unlink(ecrnx_vif);
+
+ if (ECRNX_VIF_TYPE(ecrnx_vif) == NL80211_IFTYPE_MONITOR)
+ ecrnx_hw->monitor_vif = ECRNX_INVALID_VIF;
+
+ ecrnx_hw->vif_started--;
+ if (ecrnx_hw->vif_started == 0) {
+#ifndef CONFIG_ECRNX_ESWIN
+ /* This also lets both ipc sides remain in sync before resetting */
+ ecrnx_ipc_tx_drain(ecrnx_hw);
+#endif
+ ecrnx_send_reset(ecrnx_hw);
+
+ // Set parameters to firmware
+ ecrnx_send_me_config_req(ecrnx_hw);
+
+ // Set channel parameters to firmware
+ ecrnx_send_me_chan_config_req(ecrnx_hw);
+
+ clear_bit(ECRNX_DEV_STARTED, &ecrnx_hw->flags);
+ }
+
+ return 0;
+}
+
+/**
+ * struct net_device_stats* (*ndo_get_stats)(struct net_device *dev);
+ * Called when a user wants to get the network device usage
+ * statistics. Drivers must do one of the following:
+ * 1. Define @ndo_get_stats64 to fill in a zero-initialised
+ * rtnl_link_stats64 structure passed by the caller.
+ * 2. Define @ndo_get_stats to update a net_device_stats structure
+ * (which should normally be dev->stats) and return a pointer to
+ * it. The structure may be changed asynchronously only if each
+ * field is written atomically.
+ * 3. Update dev->stats asynchronously and atomically, and define
+ * neither operation.
+ */
+static struct net_device_stats *ecrnx_get_stats(struct net_device *dev)
+{
+ struct ecrnx_vif *vif = netdev_priv(dev);
+
+ return &vif->net_stats;
+}
+
+/**
+ * u16 (*ndo_select_queue)(struct net_device *dev, struct sk_buff *skb,
+ * struct net_device *sb_dev);
+ * Called to decide which queue to when device supports multiple
+ * transmit queues.
+ */
+u16 ecrnx_select_queue(struct net_device *dev, struct sk_buff *skb,
+ struct net_device *sb_dev)
+{
+ struct ecrnx_vif *ecrnx_vif = netdev_priv(dev);
+ return ecrnx_select_txq(ecrnx_vif, skb);
+}
+
+/**
+ * int (*ndo_set_mac_address)(struct net_device *dev, void *addr);
+ * This function is called when the Media Access Control address
+ * needs to be changed. If this interface is not defined, the
+ * mac address can not be changed.
+ */
+static int ecrnx_set_mac_address(struct net_device *dev, void *addr)
+{
+ struct sockaddr *sa = addr;
+ int ret;
+
+ ret = eth_mac_addr(dev, sa);
+
+ return ret;
+}
+
+int ecrnx_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+ //struct iwreq *wrq = (struct iwreq *)rq;
+ int ret = 0;
+
+ switch (cmd) {
+ case (SIOCDEVPRIVATE+1):
+ //ret = ecrnx_android_priv_cmd(dev, rq, cmd);
+ break;
+ default:
+ ret = -EOPNOTSUPP;
+ break;
+ }
+
+ return ret;
+}
+
+static const struct net_device_ops ecrnx_netdev_ops = {
+ .ndo_open = ecrnx_open,
+ .ndo_stop = ecrnx_close,
+ .ndo_start_xmit = ecrnx_start_xmit,
+ .ndo_get_stats = ecrnx_get_stats,
+ .ndo_select_queue = ecrnx_select_queue,
+ .ndo_set_mac_address = ecrnx_set_mac_address,
+ .ndo_do_ioctl = ecrnx_ioctl,
+// .ndo_set_features = ecrnx_set_features,
+// .ndo_set_rx_mode = ecrnx_set_multicast_list,
+};
+
+static const struct net_device_ops ecrnx_netdev_monitor_ops = {
+ .ndo_open = ecrnx_open,
+ .ndo_stop = ecrnx_close,
+ .ndo_get_stats = ecrnx_get_stats,
+ .ndo_set_mac_address = ecrnx_set_mac_address,
+};
+
+#ifdef CONFIG_WIRELESS_EXT
+extern const struct iw_handler_def ecrnx_wext_handler_def;
+#endif
+
+static void ecrnx_netdev_setup(struct net_device *dev)
+{
+ ether_setup(dev);
+ dev->priv_flags &= ~IFF_TX_SKB_SHARING;
+ dev->netdev_ops = &ecrnx_netdev_ops;
+#ifdef CONFIG_WIRELESS_EXT
+ dev->wireless_handlers = &ecrnx_wext_handler_def;
+#endif
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0)
+ dev->destructor = free_netdev;
+#else
+ dev->needs_free_netdev = true;
+#endif
+ dev->watchdog_timeo = ECRNX_TX_LIFETIME_MS;
+ dev->needed_headroom = ECRNX_TX_MAX_HEADROOM;
+
+#ifdef CONFIG_ECRNX_AMSDUS_TX
+ dev->needed_headroom = max(dev->needed_headroom,
+ (unsigned short)(sizeof(struct ecrnx_amsdu_txhdr)
+ + sizeof(struct ethhdr) + 4
+ + sizeof(rfc1042_header) + 2));
+#endif /* CONFIG_ECRNX_AMSDUS_TX */
+
+ dev->hw_features = 0;
+}
+
+/*********************************************************************
+ * Cfg80211 callbacks (and helper)
+ *********************************************************************/
+static struct wireless_dev *ecrnx_interface_add(struct ecrnx_hw *ecrnx_hw,
+ const char *name,
+ unsigned char name_assign_type,
+ enum nl80211_iftype type,
+ struct vif_params *params)
+{
+ struct net_device *ndev;
+ struct ecrnx_vif *vif;
+ int min_idx, max_idx;
+ int vif_idx = -1;
+ int i;
+
+ // Look for an available VIF
+ if (type == NL80211_IFTYPE_AP_VLAN) {
+ min_idx = NX_VIRT_DEV_MAX;
+ max_idx = NX_VIRT_DEV_MAX + NX_REMOTE_STA_MAX;
+ } else {
+ min_idx = 0;
+ max_idx = NX_VIRT_DEV_MAX;
+ }
+
+ for (i = min_idx; i < max_idx; i++) {
+ if ((ecrnx_hw->avail_idx_map) & BIT(i)) {
+ vif_idx = i;
+ break;
+ }
+ }
+ if (vif_idx < 0)
+ return NULL;
+
+ #ifndef CONFIG_ECRNX_MON_DATA
+ list_for_each_entry(vif, &ecrnx_hw->vifs, list) {
+ // Check if monitor interface already exists or type is monitor
+ if ((ECRNX_VIF_TYPE(vif) == NL80211_IFTYPE_MONITOR) ||
+ (type == NL80211_IFTYPE_MONITOR)) {
+ wiphy_err(ecrnx_hw->wiphy,
+ "Monitor+Data interface support (MON_DATA) disabled\n");
+ return NULL;
+ }
+ }
+ #endif
+
+ ndev = alloc_netdev_mqs(sizeof(*vif), name, name_assign_type,
+ ecrnx_netdev_setup, NX_NB_NDEV_TXQ, 1);
+ if (!ndev)
+ return NULL;
+
+ vif = netdev_priv(ndev);
+ ndev->ieee80211_ptr = &vif->wdev;
+ vif->wdev.wiphy = ecrnx_hw->wiphy;
+ vif->ecrnx_hw = ecrnx_hw;
+ vif->ndev = ndev;
+ vif->drv_vif_index = vif_idx;
+ SET_NETDEV_DEV(ndev, wiphy_dev(vif->wdev.wiphy));
+ vif->wdev.netdev = ndev;
+ vif->wdev.iftype = type;
+ vif->up = false;
+ vif->ch_index = ECRNX_CH_NOT_SET;
+ vif->generation = 0;
+ memset(&vif->net_stats, 0, sizeof(vif->net_stats));
+ memset(vif->rx_pn, 0, TID_MAX * sizeof(uint64_t));
+
+ switch (type) {
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_P2P_CLIENT:
+ vif->sta.flags = 0;
+ vif->sta.ap = NULL;
+ vif->sta.tdls_sta = NULL;
+ vif->sta.ft_assoc_ies = NULL;
+ vif->sta.ft_assoc_ies_len = 0;
+ break;
+ case NL80211_IFTYPE_MESH_POINT:
+ INIT_LIST_HEAD(&vif->ap.mpath_list);
+ INIT_LIST_HEAD(&vif->ap.proxy_list);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+ vif->ap.mesh_pm = NL80211_MESH_POWER_ACTIVE;
+ vif->ap.next_mesh_pm = NL80211_MESH_POWER_ACTIVE;
+#endif
+ break;
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_P2P_GO:
+ INIT_LIST_HEAD(&vif->ap.sta_list);
+ memset(&vif->ap.bcn, 0, sizeof(vif->ap.bcn));
+ vif->ap.flags = 0;
+ break;
+ case NL80211_IFTYPE_AP_VLAN:
+ {
+ struct ecrnx_vif *master_vif;
+ bool found = false;
+ list_for_each_entry(master_vif, &ecrnx_hw->vifs, list) {
+ if ((ECRNX_VIF_TYPE(master_vif) == NL80211_IFTYPE_AP)
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+ && !(!memcmp(master_vif->ndev->dev_addr, params->macaddr,
+ ETH_ALEN))
+#endif
+ ) {
+ found=true;
+ break;
+ }
+ }
+
+ if (!found)
+ goto err;
+
+ vif->ap_vlan.master = master_vif;
+ vif->ap_vlan.sta_4a = NULL;
+ break;
+ }
+ case NL80211_IFTYPE_MONITOR:
+ ndev->type = ARPHRD_IEEE80211_RADIOTAP;
+ ndev->netdev_ops = &ecrnx_netdev_monitor_ops;
+ break;
+ default:
+ break;
+ }
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+ if (type == NL80211_IFTYPE_AP_VLAN)
+ memcpy(ndev->dev_addr, params->macaddr, ETH_ALEN);
+ else
+#endif
+ {
+ unsigned char mac_addr[6];
+ memcpy(mac_addr, ecrnx_hw->wiphy->perm_addr, ETH_ALEN);
+ mac_addr[5] ^= vif_idx;
+ eth_hw_addr_set(ndev, mac_addr);
+ memcpy(vif->wdev.address, ndev->dev_addr, ETH_ALEN);
+ }
+
+ if (params) {
+ vif->use_4addr = params->use_4addr;
+ ndev->ieee80211_ptr->use_4addr = params->use_4addr;
+ } else
+ vif->use_4addr = false;
+
+
+ if (register_netdevice(ndev))
+ goto err;
+
+ spin_lock_bh(&ecrnx_hw->cb_lock);
+ list_add_tail(&vif->list, &ecrnx_hw->vifs);
+ spin_unlock_bh(&ecrnx_hw->cb_lock);
+ ecrnx_hw->avail_idx_map &= ~BIT(vif_idx);
+
+//#if defined(CONFIG_ECRNX_ESWIN_SDIO) || defined(CONFIG_ECRNX_ESWIN_USB)
+ init_waitqueue_head(&vif->rxdataq);
+//#endif
+
+ return &vif->wdev;
+
+err:
+ free_netdev(ndev);
+ return NULL;
+}
+
+
+/*
+ * @brief Retrieve the ecrnx_sta object allocated for a given MAC address
+ * and a given role.
+ */
+static struct ecrnx_sta *ecrnx_retrieve_sta(struct ecrnx_hw *ecrnx_hw,
+ struct ecrnx_vif *ecrnx_vif, u8 *addr,
+ __le16 fc, bool ap)
+{
+ if (ap) {
+ /* only deauth, disassoc and action are bufferable MMPDUs */
+ bool bufferable = ieee80211_is_deauth(fc) ||
+ ieee80211_is_disassoc(fc) ||
+ ieee80211_is_action(fc);
+
+ /* Check if the packet is bufferable or not */
+ if (bufferable)
+ {
+ /* Check if address is a broadcast or a multicast address */
+ if (is_broadcast_ether_addr(addr) || is_multicast_ether_addr(addr)) {
+ /* Returned STA pointer */
+ struct ecrnx_sta *ecrnx_sta = &ecrnx_hw->sta_table[ecrnx_vif->ap.bcmc_index];
+
+ if (ecrnx_sta->valid)
+ return ecrnx_sta;
+ } else {
+ /* Returned STA pointer */
+ struct ecrnx_sta *ecrnx_sta;
+
+ /* Go through list of STAs linked with the provided VIF */
+ list_for_each_entry(ecrnx_sta, &ecrnx_vif->ap.sta_list, list) {
+ if (ecrnx_sta->valid &&
+ ether_addr_equal(ecrnx_sta->mac_addr, addr)) {
+ /* Return the found STA */
+ return ecrnx_sta;
+ }
+ }
+ }
+ }
+ } else {
+ return ecrnx_vif->sta.ap;
+ }
+
+ return NULL;
+}
+
+/**
+ * @add_virtual_intf: create a new virtual interface with the given name,
+ * must set the struct wireless_dev's iftype. Beware: You must create
+ * the new netdev in the wiphy's network namespace! Returns the struct
+ * wireless_dev, or an ERR_PTR. For P2P device wdevs, the driver must
+ * also set the address member in the wdev.
+ */
+static struct wireless_dev *ecrnx_cfg80211_add_iface(struct wiphy *wiphy,
+ const char *name,
+ unsigned char name_assign_type,
+ enum nl80211_iftype type,
+ struct vif_params *params)
+{
+ struct ecrnx_hw *ecrnx_hw = wiphy_priv(wiphy);
+ struct wireless_dev *wdev;
+
+ wdev = ecrnx_interface_add(ecrnx_hw, name, name_assign_type, type, params);
+
+ if (!wdev)
+ return ERR_PTR(-EINVAL);
+
+ return wdev;
+}
+
+/**
+ * @del_virtual_intf: remove the virtual interface
+ */
+static int ecrnx_cfg80211_del_iface(struct wiphy *wiphy, struct wireless_dev *wdev)
+{
+ struct net_device *dev = wdev->netdev;
+ struct ecrnx_vif *ecrnx_vif = netdev_priv(dev);
+ struct ecrnx_hw *ecrnx_hw = wiphy_priv(wiphy);
+
+ netdev_info(dev, "Remove Interface");
+
+ if (dev->reg_state == NETREG_REGISTERED) {
+ ECRNX_DBG("%s-%d:unregister_netdevice \n", __func__, __LINE__);
+ /* Will call ecrnx_close if interface is UP */
+ unregister_netdevice(dev);
+ }
+
+ spin_lock_bh(&ecrnx_hw->cb_lock);
+ list_del(&ecrnx_vif->list);
+ spin_unlock_bh(&ecrnx_hw->cb_lock);
+ ecrnx_hw->avail_idx_map |= BIT(ecrnx_vif->drv_vif_index);
+ ecrnx_vif->ndev = NULL;
+
+ /* Clear the priv in adapter */
+ dev->ieee80211_ptr = NULL;
+
+ return 0;
+}
+
+static int ecrnx_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *dev, unsigned int link_id);
+static int ecrnx_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *dev,
+ u16 reason_code);
+
+/**
+ * @change_virtual_intf: change type/configuration of virtual interface,
+ * keep the struct wireless_dev's iftype updated.
+ */
+static int ecrnx_cfg80211_change_iface(struct wiphy *wiphy,
+ struct net_device *dev,
+ enum nl80211_iftype type,
+ struct vif_params *params)
+{
+#ifndef CONFIG_ECRNX_MON_DATA
+ struct ecrnx_hw *ecrnx_hw = wiphy_priv(wiphy);
+#endif
+ struct ecrnx_vif *vif = netdev_priv(dev);
+
+
+ ECRNX_PRINT("%s:dev:0x%p, type:%d, vif->up:%d \n", __func__, dev, type, vif->up);
+
+ if (vif->up)
+ {
+ if((ECRNX_VIF_TYPE(vif) == NL80211_IFTYPE_AP) || (ECRNX_VIF_TYPE(vif) == NL80211_IFTYPE_P2P_GO))
+ {
+ ecrnx_cfg80211_stop_ap(wiphy, dev, NULL);
+ }
+ else if((ECRNX_VIF_TYPE(vif) == NL80211_IFTYPE_STATION) || (ECRNX_VIF_TYPE(vif) == NL80211_IFTYPE_P2P_CLIENT))
+ {
+ ecrnx_cfg80211_disconnect(wiphy, dev, WLAN_REASON_DEAUTH_LEAVING);
+ }
+ ECRNX_ERR("ecrnx_cfg80211_change_iface: -EBUSY \n");
+ ecrnx_close(dev);
+ //return (-EBUSY);
+ }
+
+#ifndef CONFIG_ECRNX_MON_DATA
+ if ((type == NL80211_IFTYPE_MONITOR) &&
+ (ECRNX_VIF_TYPE(vif) != NL80211_IFTYPE_MONITOR)) {
+ struct ecrnx_vif *vif_el;
+ list_for_each_entry(vif_el, &ecrnx_hw->vifs, list) {
+ // Check if data interface already exists
+ if ((vif_el != vif) &&
+ (ECRNX_VIF_TYPE(vif) != NL80211_IFTYPE_MONITOR)) {
+ wiphy_err(ecrnx_hw->wiphy,
+ "Monitor+Data interface support (MON_DATA) disabled\n");
+ return -EIO;
+ }
+ }
+ }
+#endif
+
+ // Reset to default case (i.e. not monitor)
+ dev->type = ARPHRD_ETHER;
+ dev->netdev_ops = &ecrnx_netdev_ops;
+
+ switch (type) {
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_P2P_CLIENT:
+ vif->sta.flags = 0;
+ vif->sta.ap = NULL;
+ vif->sta.tdls_sta = NULL;
+ vif->sta.ft_assoc_ies = NULL;
+ vif->sta.ft_assoc_ies_len = 0;
+ break;
+ case NL80211_IFTYPE_MESH_POINT:
+ INIT_LIST_HEAD(&vif->ap.mpath_list);
+ INIT_LIST_HEAD(&vif->ap.proxy_list);
+ break;
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_P2P_GO:
+ INIT_LIST_HEAD(&vif->ap.sta_list);
+ memset(&vif->ap.bcn, 0, sizeof(vif->ap.bcn));
+ vif->ap.flags = 0;
+ break;
+ case NL80211_IFTYPE_AP_VLAN:
+ return -EPERM;
+ case NL80211_IFTYPE_MONITOR:
+ dev->type = ARPHRD_IEEE80211_RADIOTAP;
+ dev->netdev_ops = &ecrnx_netdev_monitor_ops;
+ break;
+ default:
+ break;
+ }
+
+ vif->generation = 0;
+ vif->wdev.iftype = type;
+ if (params->use_4addr != -1)
+ vif->use_4addr = params->use_4addr;
+
+ if (!vif->up)
+ {
+ ecrnx_open(dev);
+ }
+
+ return 0;
+}
+
+/*
+ * GavinGao
+ * Used as P2P_DEVICE mode
+ */
+static int ecrnx_cfg80211_start_p2p_device(struct wiphy *wiphy,
+ struct wireless_dev *wdev)
+{
+ ECRNX_PRINT("rwnx_cfg80211_start_p2p_device\n");
+ return 0;
+}
+
+static void ecrnx_cfg80211_stop_p2p_device(struct wiphy *wiphy,
+ struct wireless_dev *wdev)
+{
+ //TODO
+}
+/* Used as P2P_DEVICE mode*/
+
+
+/**
+ * @scan: Request to do a scan. If returning zero, the scan request is given
+ * the driver, and will be valid until passed to cfg80211_scan_done().
+ * For scan results, call cfg80211_inform_bss(); you can call this outside
+ * the scan/scan_done bracket too.
+ */
+static int ecrnx_cfg80211_scan(struct wiphy *wiphy,
+ struct cfg80211_scan_request *request)
+{
+ struct ecrnx_hw *ecrnx_hw = wiphy_priv(wiphy);
+ struct ecrnx_vif *ecrnx_vif = container_of(request->wdev, struct ecrnx_vif,
+ wdev);
+ int error;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ if(!ecrnx_hw->scan_request){
+ ecrnx_hw->scan_request = request;
+ ECRNX_PRINT("%s:scan_request:0x%p \n", __func__, request);
+ if ((error = ecrnx_send_scanu_req(ecrnx_hw, ecrnx_vif, request))){
+ ECRNX_PRINT("scan message send error!!\n");
+ ecrnx_hw->scan_request = NULL;
+ return error;
+ }
+ }else{
+ ECRNX_PRINT("scan is already running!!\n");
+ }
+ ECRNX_DBG("send finish:ecrnx_cfg80211_scan \n");
+
+ return 0;
+}
+
+static void ecrnx_cfg80211_abort_scan(struct wiphy *wiphy,
+ struct wireless_dev *wdev)
+{
+ u8_l ret = 0;
+ struct ecrnx_hw *ecrnx_hw = wiphy_priv(wiphy);
+ struct ecrnx_vif *ecrnx_vif = container_of(wdev, struct ecrnx_vif,
+ wdev);
+
+ //mutex_lock(&ecrnx_hw->mutex);
+ ECRNX_PRINT("%s:ecrnx_hw->scan_request:0x%p \n", __func__, ecrnx_hw->scan_request);
+
+ if(!ecrnx_hw->scan_request){
+ ECRNX_ERR("no scan is running, don't need abort! \n");
+ goto out;
+ }
+
+ if(wdev->iftype != NL80211_IFTYPE_STATION){
+ ECRNX_ERR("abort scan ignored, iftype(%d)\n", wdev->iftype);
+ goto out;
+ }
+
+ if(wdev != ecrnx_hw->scan_request->wdev){
+ ECRNX_ERR("abort scan was called on the wrong iface\n");
+ goto out;
+ }
+
+ ret = ecrnx_send_scanu_cancel_req(ecrnx_hw, ecrnx_vif);
+
+out:
+ //mutex_unlock(&ecrnx_hw->mutex);
+ return;
+}
+
+/**
+ * @add_key: add a key with the given parameters. @mac_addr will be %NULL
+ * when adding a group key.
+ */
+static int ecrnx_cfg80211_add_key(struct wiphy *wiphy, struct net_device *netdev,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0)
+ int link_id,
+#endif
+
+ u8 key_index, bool pairwise, const u8 *mac_addr,
+ struct key_params *params)
+{
+ struct ecrnx_hw *ecrnx_hw = wiphy_priv(wiphy);
+ struct ecrnx_vif *vif = netdev_priv(netdev);
+ int i, error = 0;
+ struct mm_key_add_cfm key_add_cfm;
+ u8_l cipher = 0;
+ struct ecrnx_sta *sta = NULL;
+ struct ecrnx_key *ecrnx_key;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ if (mac_addr) {
+ sta = ecrnx_get_sta(ecrnx_hw, mac_addr);
+ if (!sta)
+ return -EINVAL;
+ ecrnx_key = &sta->key;
+ }
+ else
+ ecrnx_key = &vif->key[key_index];
+
+ /* Retrieve the cipher suite selector */
+ switch (params->cipher) {
+ case WLAN_CIPHER_SUITE_WEP40:
+ cipher = MAC_CIPHER_WEP40;
+ break;
+ case WLAN_CIPHER_SUITE_WEP104:
+ cipher = MAC_CIPHER_WEP104;
+ break;
+ case WLAN_CIPHER_SUITE_TKIP:
+ cipher = MAC_CIPHER_TKIP;
+ break;
+ case WLAN_CIPHER_SUITE_CCMP:
+ cipher = MAC_CIPHER_CCMP;
+ break;
+ case WLAN_CIPHER_SUITE_AES_CMAC:
+ cipher = MAC_CIPHER_BIP_CMAC_128;
+ break;
+ case WLAN_CIPHER_SUITE_SMS4:
+ {
+ // Need to reverse key order
+ u8 tmp, *key = (u8 *)params->key;
+ cipher = MAC_CIPHER_WPI_SMS4;
+ for (i = 0; i < WPI_SUBKEY_LEN/2; i++) {
+ tmp = key[i];
+ key[i] = key[WPI_SUBKEY_LEN - 1 - i];
+ key[WPI_SUBKEY_LEN - 1 - i] = tmp;
+ }
+ for (i = 0; i < WPI_SUBKEY_LEN/2; i++) {
+ tmp = key[i + WPI_SUBKEY_LEN];
+ key[i + WPI_SUBKEY_LEN] = key[WPI_KEY_LEN - 1 - i];
+ key[WPI_KEY_LEN - 1 - i] = tmp;
+ }
+ break;
+ }
+ case WLAN_CIPHER_SUITE_GCMP:
+ cipher = MAC_CIPHER_GCMP_128;
+ break;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)
+ case WLAN_CIPHER_SUITE_GCMP_256:
+ cipher = MAC_CIPHER_GCMP_256;
+ break;
+ case WLAN_CIPHER_SUITE_CCMP_256:
+ cipher = MAC_CIPHER_CCMP_256;
+ break;
+#endif
+ default:
+ return -EINVAL;
+ }
+
+ if ((error = ecrnx_send_key_add(ecrnx_hw, vif->vif_index,
+ (sta ? sta->sta_idx : 0xFF), pairwise,
+ (u8 *)params->key, params->key_len,
+ key_index, cipher, &key_add_cfm)))
+ return error;
+
+ if (key_add_cfm.status != 0) {
+ ECRNX_PRINT_CFM_ERR(key_add);
+ return -EIO;
+ }
+
+ /* Save the index retrieved from LMAC */
+ ecrnx_key->hw_idx = key_add_cfm.hw_key_idx;
+
+ return 0;
+}
+
+/**
+ * @get_key: get information about the key with the given parameters.
+ * @mac_addr will be %NULL when requesting information for a group
+ * key. All pointers given to the @callback function need not be valid
+ * after it returns. This function should return an error if it is
+ * not possible to retrieve the key, -ENOENT if it doesn't exist.
+ *
+ */
+static int ecrnx_cfg80211_get_key(struct wiphy *wiphy, struct net_device *netdev,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0)
+ int link_id,
+#endif
+
+ u8 key_index, bool pairwise, const u8 *mac_addr,
+ void *cookie,
+ void (*callback)(void *cookie, struct key_params*))
+{
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ return -1;
+}
+
+
+/**
+ * @del_key: remove a key given the @mac_addr (%NULL for a group key)
+ * and @key_index, return -ENOENT if the key doesn't exist.
+ */
+static int ecrnx_cfg80211_del_key(struct wiphy *wiphy, struct net_device *netdev,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0)
+ int link_id,
+#endif
+
+ u8 key_index, bool pairwise, const u8 *mac_addr)
+{
+ struct ecrnx_hw *ecrnx_hw = wiphy_priv(wiphy);
+ struct ecrnx_vif *vif = netdev_priv(netdev);
+ int error;
+ struct ecrnx_sta *sta = NULL;
+ struct ecrnx_key *ecrnx_key;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+ if (mac_addr) {
+ sta = ecrnx_get_sta(ecrnx_hw, mac_addr);
+ if (!sta)
+ return -EINVAL;
+ ecrnx_key = &sta->key;
+ }
+ else
+ ecrnx_key = &vif->key[key_index];
+
+ error = ecrnx_send_key_del(ecrnx_hw, ecrnx_key->hw_idx);
+
+ return error;
+}
+
+/**
+ * @set_default_key: set the default key on an interface
+ */
+static int ecrnx_cfg80211_set_default_key(struct wiphy *wiphy,
+ struct net_device *netdev,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0)
+ int link_id,
+#endif
+
+ u8 key_index, bool unicast, bool multicast)
+{
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ return 0;
+}
+
+/**
+ * @set_default_mgmt_key: set the default management frame key on an interface
+ */
+static int ecrnx_cfg80211_set_default_mgmt_key(struct wiphy *wiphy,
+ struct net_device *netdev,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0)
+ int link_id,
+#endif
+
+ u8 key_index)
+{
+ return 0;
+}
+
+/**
+ * @connect: Connect to the ESS with the specified parameters. When connected,
+ * call cfg80211_connect_result() with status code %WLAN_STATUS_SUCCESS.
+ * If the connection fails for some reason, call cfg80211_connect_result()
+ * with the status from the AP.
+ * (invoked with the wireless_dev mutex held)
+ */
+static int ecrnx_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
+ struct cfg80211_connect_params *sme)
+{
+ struct ecrnx_hw *ecrnx_hw = wiphy_priv(wiphy);
+ struct ecrnx_vif *ecrnx_vif = netdev_priv(dev);
+ struct sm_connect_cfm sm_connect_cfm;
+ int error = 0;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ /* For SHARED-KEY authentication, must install key first */
+ if (sme->auth_type == NL80211_AUTHTYPE_SHARED_KEY && sme->key)
+ {
+ struct key_params key_params;
+ key_params.key = sme->key;
+ key_params.seq = NULL;
+ key_params.key_len = sme->key_len;
+ key_params.seq_len = 0;
+ key_params.cipher = sme->crypto.cipher_group;
+ ecrnx_cfg80211_add_key(wiphy, dev, NULL, sme->key_idx, false, NULL, &key_params);
+ }
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0)
+ else if ((sme->auth_type == NL80211_AUTHTYPE_SAE) &&
+ !(sme->flags & CONNECT_REQ_EXTERNAL_AUTH_SUPPORT)) {
+ netdev_err(dev, "Doesn't support SAE without external authentication\n");
+ return -EINVAL;
+ }
+#endif
+
+ /* Forward the information to the LMAC */
+ if ((error = ecrnx_send_sm_connect_req(ecrnx_hw, ecrnx_vif, sme, &sm_connect_cfm)))
+ return error;
+ ECRNX_PRINT("%s:bssid:%pM, send status:%d\n", __func__, sme->bssid, sm_connect_cfm.status);
+
+ // Check the status
+ switch (sm_connect_cfm.status)
+ {
+ case CO_OK:
+ ecrnx_save_assoc_info_for_ft(ecrnx_vif, sme);
+ error = 0;
+ break;
+ case CO_BUSY:
+ error = -EINPROGRESS;
+ break;
+ case CO_BAD_PARAM:
+ error = -EINVAL;
+ break;
+ case CO_OP_IN_PROGRESS:
+ error = -EALREADY;
+ break;
+ default:
+ error = -EIO;
+ break;
+ }
+
+ return error;
+}
+
+/**
+ * @disconnect: Disconnect from the BSS/ESS.
+ * (invoked with the wireless_dev mutex held)
+ */
+static int ecrnx_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *dev,
+ u16 reason_code)
+{
+ struct ecrnx_hw *ecrnx_hw = wiphy_priv(wiphy);
+ struct ecrnx_vif *ecrnx_vif = netdev_priv(dev);
+
+ ECRNX_PRINT("%s:dev:0x%p, vif_index:%d, reason_code:%d \n", __func__, dev, ecrnx_vif->vif_index, reason_code);
+
+ return(ecrnx_send_sm_disconnect_req(ecrnx_hw, ecrnx_vif, reason_code));
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0)
+/**
+ * @external_auth: indicates result of offloaded authentication processing from
+ * user space
+ */
+static int ecrnx_cfg80211_external_auth(struct wiphy *wiphy, struct net_device *dev,
+ struct cfg80211_external_auth_params *params)
+{
+ struct ecrnx_hw *ecrnx_hw = wiphy_priv(wiphy);
+ struct ecrnx_vif *ecrnx_vif = netdev_priv(dev);
+
+ if (!(ecrnx_vif->sta.flags & ECRNX_STA_EXT_AUTH))
+ return -EINVAL;
+
+ ecrnx_external_auth_disable(ecrnx_vif);
+ return ecrnx_send_sm_external_auth_required_rsp(ecrnx_hw, ecrnx_vif,
+ params->status);
+}
+#endif
+
+/**
+ * @add_station: Add a new station.
+ */
+static int ecrnx_cfg80211_add_station(struct wiphy *wiphy, struct net_device *dev,
+ const u8 *mac, struct station_parameters *params)
+{
+ struct ecrnx_hw *ecrnx_hw = wiphy_priv(wiphy);
+ struct ecrnx_vif *ecrnx_vif = netdev_priv(dev);
+ struct me_sta_add_cfm me_sta_add_cfm;
+ int error = 0;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ WARN_ON(ECRNX_VIF_TYPE(ecrnx_vif) == NL80211_IFTYPE_AP_VLAN);
+
+ /* Do not add TDLS station */
+ if (params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))
+ return 0;
+
+ /* Indicate we are in a STA addition process - This will allow handling
+ * potential PS mode change indications correctly
+ */
+ set_bit(ECRNX_DEV_ADDING_STA, &ecrnx_hw->flags);
+
+ /* Forward the information to the LMAC */
+ if ((error = ecrnx_send_me_sta_add(ecrnx_hw, params, mac, ecrnx_vif->vif_index,
+ &me_sta_add_cfm)))
+ return error;
+
+ // Check the status
+ switch (me_sta_add_cfm.status)
+ {
+ case CO_OK:
+ {
+ struct ecrnx_sta *sta = &ecrnx_hw->sta_table[me_sta_add_cfm.sta_idx];
+
+ ECRNX_PRINT("%s-%d:sta:0x%p, sta_idx:%d \n", __func__, __LINE__, sta, me_sta_add_cfm.sta_idx);
+ int tid;
+ sta->aid = params->aid;
+ memset(sta->rx_pn, 0, TID_MAX * sizeof(uint64_t));
+ sta->sta_idx = me_sta_add_cfm.sta_idx;
+ sta->ch_idx = ecrnx_vif->ch_index;
+ sta->vif_idx = ecrnx_vif->vif_index;
+ sta->vlan_idx = sta->vif_idx;
+ sta->qos = (params->sta_flags_set & BIT(NL80211_STA_FLAG_WME)) != 0;
+ sta->ht = params->link_sta_params.ht_capa ? 1 : 0;
+ sta->vht = params->link_sta_params.vht_capa ? 1 : 0;
+ sta->acm = 0;
+ sta->listen_interval = params->listen_interval;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+ if (params->local_pm != NL80211_MESH_POWER_UNKNOWN)
+ sta->mesh_pm = params->local_pm;
+ else
+ sta->mesh_pm = ecrnx_vif->ap.next_mesh_pm;
+#endif
+ ecrnx_update_mesh_power_mode(ecrnx_vif);
+
+ for (tid = 0; tid < NX_NB_TXQ_PER_STA; tid++) {
+ int uapsd_bit = ecrnx_hwq2uapsd[ecrnx_tid2hwq[tid]];
+ if (params->uapsd_queues & uapsd_bit)
+ sta->uapsd_tids |= 1 << tid;
+ else
+ sta->uapsd_tids &= ~(1 << tid);
+ }
+ memcpy(sta->mac_addr, mac, ETH_ALEN);
+ ecrnx_dbgfs_register_sta(ecrnx_hw, sta);
+
+ /* Ensure that we won't process PS change or channel switch ind*/
+ spin_lock_bh(&ecrnx_hw->cb_lock);
+ ecrnx_txq_sta_init(ecrnx_hw, sta, ecrnx_txq_vif_get_status(ecrnx_vif));
+ ecrnx_rx_reord_sta_init(ecrnx_hw, ecrnx_vif, sta->sta_idx);
+ list_add_tail(&sta->list, &ecrnx_vif->ap.sta_list);
+ ecrnx_vif->generation++;
+ sta->valid = true;
+ ecrnx_ps_bh_enable(ecrnx_hw, sta, sta->ps.active || me_sta_add_cfm.pm_state);
+ spin_unlock_bh(&ecrnx_hw->cb_lock);
+
+#ifdef CONFIG_ECRNX_ANDRIOD
+ if((ECRNX_VIF_TYPE(ecrnx_vif) == NL80211_IFTYPE_AP) || (ECRNX_VIF_TYPE(ecrnx_vif) == NL80211_IFTYPE_P2P_GO)) {
+ struct station_info sinfo;
+ u8 ie_offset;
+
+ if((!is_multicast_sta(sta->sta_idx)) && (sta->mac_addr)) {
+ memset(&sinfo, 0, sizeof(sinfo));
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0)
+ sinfo.filled = STATION_INFO_ASSOC_REQ_IES;
+#endif
+ sinfo.assoc_req_ies = NULL;
+ sinfo.assoc_req_ies_len = 0;
+ ECRNX_PRINT("%s-%d:sta:0x%x,sta->mac_addr:%pM \n", __func__, __LINE__, sta, sta->mac_addr);
+ cfg80211_new_sta(ecrnx_vif->ndev, sta->mac_addr, &sinfo, GFP_ATOMIC);
+ }
+ }
+#endif
+
+ error = 0;
+
+#ifdef CONFIG_ECRNX_BFMER
+ if (ecrnx_hw->mod_params->bfmer)
+ ecrnx_send_bfmer_enable(ecrnx_hw, sta, params->vht_capa);
+
+ ecrnx_mu_group_sta_init(sta, params->vht_capa);
+#endif /* CONFIG_ECRNX_BFMER */
+
+ #define PRINT_STA_FLAG(f) \
+ (params->sta_flags_set & BIT(NL80211_STA_FLAG_##f) ? "["#f"]" : "")
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+ netdev_info(dev, "Add sta %d (%pM) flags=%s%s%s%s%s%s%s",
+ sta->sta_idx, mac,
+ PRINT_STA_FLAG(AUTHORIZED),
+ PRINT_STA_FLAG(SHORT_PREAMBLE),
+ PRINT_STA_FLAG(WME),
+ PRINT_STA_FLAG(MFP),
+ PRINT_STA_FLAG(AUTHENTICATED),
+ PRINT_STA_FLAG(TDLS_PEER),
+ PRINT_STA_FLAG(ASSOCIATED));
+#else
+ netdev_info(dev, "Add sta %d (%pM) flags=%s%s%s%s%s%s",
+ sta->sta_idx, mac,
+ PRINT_STA_FLAG(AUTHORIZED),
+ PRINT_STA_FLAG(SHORT_PREAMBLE),
+ PRINT_STA_FLAG(WME),
+ PRINT_STA_FLAG(MFP),
+ PRINT_STA_FLAG(AUTHENTICATED),
+ PRINT_STA_FLAG(TDLS_PEER));
+#endif
+
+ #undef PRINT_STA_FLAG
+#if defined(CONFIG_ECRNX_DEBUGFS_CUSTOM)
+ ecrnx_debugfs_add_station_in_ap_mode(ecrnx_hw, sta, params);
+#endif
+ break;
+ }
+ default:
+ error = -EBUSY;
+ break;
+ }
+
+ clear_bit(ECRNX_DEV_ADDING_STA, &ecrnx_hw->flags);
+
+ return error;
+}
+
+/**
+ * @del_station: Remove a station
+ */
+static int ecrnx_cfg80211_del_station(struct wiphy *wiphy,
+ struct net_device *dev,
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 16, 0))
+ u8 *mac
+#elif (LINUX_VERSION_CODE < KERNEL_VERSION(3, 19, 0))
+ const u8 *mac
+#else
+ struct station_del_parameters *params
+#endif
+)
+{
+ struct ecrnx_hw *ecrnx_hw = wiphy_priv(wiphy);
+ struct ecrnx_vif *ecrnx_vif = netdev_priv(dev);
+ struct ecrnx_sta *cur, *tmp;
+ int error = 0, found = 0;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)
+ const u8 *mac = NULL;
+ if (params)
+ mac = params->mac;
+#endif
+
+ if(list_empty(&ecrnx_vif->ap.sta_list)) {
+ goto end;
+ }
+
+ list_for_each_entry_safe(cur, tmp, &ecrnx_vif->ap.sta_list, list) {
+ if ((!mac) || (!memcmp(cur->mac_addr, mac, ETH_ALEN))) {
+ netdev_info(dev, "Del sta %d (%pM)", cur->sta_idx, cur->mac_addr);
+ ECRNX_PRINT("%s-%d:cur_list:0x%p, vif_list:0x%p, mac:%pM\n", __func__, __LINE__, &cur->list, &ecrnx_vif->ap.sta_list, mac);
+ /* Ensure that we won't process PS change ind */
+ spin_lock_bh(&ecrnx_hw->cb_lock);
+ cur->ps.active = false;
+ cur->valid = false;
+ spin_unlock_bh(&ecrnx_hw->cb_lock);
+
+ if (cur->vif_idx != cur->vlan_idx) {
+ struct ecrnx_vif *vlan_vif;
+ vlan_vif = ecrnx_hw->vif_table[cur->vlan_idx];
+ if (vlan_vif->up) {
+ if ((ECRNX_VIF_TYPE(vlan_vif) == NL80211_IFTYPE_AP_VLAN) &&
+ (vlan_vif->use_4addr)) {
+ vlan_vif->ap_vlan.sta_4a = NULL;
+ } else {
+ WARN(1, "Deleting sta belonging to VLAN other than AP_VLAN 4A");
+ }
+ }
+ }
+
+ ecrnx_txq_sta_deinit(ecrnx_hw, cur);
+ ecrnx_rx_reord_sta_deinit(ecrnx_hw, cur->sta_idx, true);
+
+ error = ecrnx_send_me_sta_del(ecrnx_hw, cur->sta_idx, false);
+ if ((error != 0) && (error != -EPIPE)){
+ ECRNX_WARN("del sta msg send fail, error code:%d \n", error);
+ }
+
+#ifdef CONFIG_ECRNX_ANDRIOD
+ if((ECRNX_VIF_TYPE(ecrnx_vif) == NL80211_IFTYPE_AP) || (ECRNX_VIF_TYPE(ecrnx_vif) == NL80211_IFTYPE_P2P_GO)) {
+ if((!is_multicast_sta(cur->sta_idx)) && params) {
+ if(params->mac){
+ ECRNX_PRINT("%s-%d:vif:%d, mac:%pM \n", __func__, __LINE__, ECRNX_VIF_TYPE(ecrnx_vif), params->mac);
+ cfg80211_del_sta(ecrnx_vif->ndev, params->mac, GFP_ATOMIC);
+ }
+ }
+ }
+#endif
+
+
+#ifdef CONFIG_ECRNX_BFMER
+ // Disable Beamformer if supported
+ ecrnx_bfmer_report_del(ecrnx_hw, cur);
+ ecrnx_mu_group_sta_del(ecrnx_hw, cur);
+#endif /* CONFIG_ECRNX_BFMER */
+
+ list_del(&cur->list);
+ ecrnx_vif->generation++;
+ ecrnx_dbgfs_unregister_sta(ecrnx_hw, cur);
+#if defined(CONFIG_ECRNX_DEBUGFS_CUSTOM)
+ ecrnx_debugfs_sta_in_ap_del(cur->sta_idx);
+#endif
+ found ++;
+ break;
+ }
+ }
+
+end:
+ if ((!found) && (mac))
+ return -ENOENT;
+
+ ecrnx_update_mesh_power_mode(ecrnx_vif);
+
+ return 0;
+}
+
+/**
+ * @change_station: Modify a given station. Note that flags changes are not much
+ * validated in cfg80211, in particular the auth/assoc/authorized flags
+ * might come to the driver in invalid combinations -- make sure to check
+ * them, also against the existing state! Drivers must call
+ * cfg80211_check_station_change() to validate the information.
+ */
+static int ecrnx_cfg80211_change_station(struct wiphy *wiphy, struct net_device *dev,
+ const u8 *mac, struct station_parameters *params)
+{
+ struct ecrnx_hw *ecrnx_hw = wiphy_priv(wiphy);
+ struct ecrnx_vif *vif = netdev_priv(dev);
+ struct ecrnx_sta *sta;
+
+ sta = ecrnx_get_sta(ecrnx_hw, mac);
+ if (!sta)
+ {
+ /* Add the TDLS station */
+ if (params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))
+ {
+ struct ecrnx_vif *ecrnx_vif = netdev_priv(dev);
+ struct me_sta_add_cfm me_sta_add_cfm;
+ int error = 0;
+
+ /* Indicate we are in a STA addition process - This will allow handling
+ * potential PS mode change indications correctly
+ */
+ set_bit(ECRNX_DEV_ADDING_STA, &ecrnx_hw->flags);
+
+ /* Forward the information to the LMAC */
+ if ((error = ecrnx_send_me_sta_add(ecrnx_hw, params, mac, ecrnx_vif->vif_index,
+ &me_sta_add_cfm)))
+ return error;
+
+ // Check the status
+ switch (me_sta_add_cfm.status)
+ {
+ case CO_OK:
+ {
+ int tid;
+ sta = &ecrnx_hw->sta_table[me_sta_add_cfm.sta_idx];
+ memset(&sta->rx_pn, 0, TID_MAX * sizeof(uint64_t));
+ sta->aid = params->aid;
+ sta->sta_idx = me_sta_add_cfm.sta_idx;
+ sta->ch_idx = ecrnx_vif->ch_index;
+ sta->vif_idx = ecrnx_vif->vif_index;
+ sta->vlan_idx = sta->vif_idx;
+ sta->qos = (params->sta_flags_set & BIT(NL80211_STA_FLAG_WME)) != 0;
+ sta->ht = params->link_sta_params.ht_capa ? 1 : 0;
+ sta->vht = params->link_sta_params.vht_capa ? 1 : 0;
+ sta->acm = 0;
+ for (tid = 0; tid < NX_NB_TXQ_PER_STA; tid++) {
+ int uapsd_bit = ecrnx_hwq2uapsd[ecrnx_tid2hwq[tid]];
+ if (params->uapsd_queues & uapsd_bit)
+ sta->uapsd_tids |= 1 << tid;
+ else
+ sta->uapsd_tids &= ~(1 << tid);
+ }
+ memcpy(sta->mac_addr, mac, ETH_ALEN);
+ ecrnx_dbgfs_register_sta(ecrnx_hw, sta);
+
+ /* Ensure that we won't process PS change or channel switch ind*/
+ spin_lock_bh(&ecrnx_hw->cb_lock);
+ ecrnx_txq_sta_init(ecrnx_hw, sta, ecrnx_txq_vif_get_status(ecrnx_vif));
+ ecrnx_rx_reord_sta_init(ecrnx_hw, ecrnx_vif, sta->sta_idx);
+ if (ecrnx_vif->tdls_status == TDLS_SETUP_RSP_TX) {
+ ecrnx_vif->tdls_status = TDLS_LINK_ACTIVE;
+ sta->tdls.initiator = true;
+ sta->tdls.active = true;
+ }
+ /* Set TDLS channel switch capability */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+ if ((params->ext_capab[3] & WLAN_EXT_CAPA4_TDLS_CHAN_SWITCH) &&
+ !ecrnx_vif->tdls_chsw_prohibited)
+#else
+ if (!ecrnx_vif->tdls_chsw_prohibited)
+#endif
+ sta->tdls.chsw_allowed = true;
+ ecrnx_vif->sta.tdls_sta = sta;
+ sta->valid = true;
+ spin_unlock_bh(&ecrnx_hw->cb_lock);
+#ifdef CONFIG_ECRNX_BFMER
+ if (ecrnx_hw->mod_params->bfmer)
+ ecrnx_send_bfmer_enable(ecrnx_hw, sta, params->vht_capa);
+
+ ecrnx_mu_group_sta_init(sta, NULL);
+#endif /* CONFIG_ECRNX_BFMER */
+
+ #define PRINT_STA_FLAG(f) \
+ (params->sta_flags_set & BIT(NL80211_STA_FLAG_##f) ? "["#f"]" : "")
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+ netdev_info(dev, "Add %s TDLS sta %d (%pM) flags=%s%s%s%s%s%s%s",
+ sta->tdls.initiator ? "initiator" : "responder",
+ sta->sta_idx, mac,
+ PRINT_STA_FLAG(AUTHORIZED),
+ PRINT_STA_FLAG(SHORT_PREAMBLE),
+ PRINT_STA_FLAG(WME),
+ PRINT_STA_FLAG(MFP),
+ PRINT_STA_FLAG(AUTHENTICATED),
+ PRINT_STA_FLAG(TDLS_PEER),
+ PRINT_STA_FLAG(ASSOCIATED));
+#else
+ netdev_info(dev, "Add %s TDLS sta %d (%pM) flags=%s%s%s%s%s%s",
+ sta->tdls.initiator ? "initiator" : "responder",
+ sta->sta_idx, mac,
+ PRINT_STA_FLAG(AUTHORIZED),
+ PRINT_STA_FLAG(SHORT_PREAMBLE),
+ PRINT_STA_FLAG(WME),
+ PRINT_STA_FLAG(MFP),
+ PRINT_STA_FLAG(AUTHENTICATED),
+ PRINT_STA_FLAG(TDLS_PEER));
+#endif
+ #undef PRINT_STA_FLAG
+
+ break;
+ }
+ default:
+ error = -EBUSY;
+ break;
+ }
+
+ clear_bit(ECRNX_DEV_ADDING_STA, &ecrnx_hw->flags);
+ } else {
+ return -EINVAL;
+ }
+ }
+
+ if (params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED))
+ ecrnx_send_me_set_control_port_req(ecrnx_hw,
+ (params->sta_flags_set & BIT(NL80211_STA_FLAG_AUTHORIZED)) != 0,
+ sta->sta_idx);
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+ if (ECRNX_VIF_TYPE(vif) == NL80211_IFTYPE_MESH_POINT) {
+ if (params->sta_modify_mask & STATION_PARAM_APPLY_PLINK_STATE) {
+ if (params->plink_state < NUM_NL80211_PLINK_STATES) {
+ ecrnx_send_mesh_peer_update_ntf(ecrnx_hw, vif, sta->sta_idx, params->plink_state);
+ }
+ }
+
+ if (params->local_pm != NL80211_MESH_POWER_UNKNOWN) {
+ sta->mesh_pm = params->local_pm;
+ ecrnx_update_mesh_power_mode(vif);
+ }
+ }
+#endif
+
+ if (params->vlan) {
+ uint8_t vlan_idx;
+
+ vif = netdev_priv(params->vlan);
+ vlan_idx = vif->vif_index;
+
+ if (sta->vlan_idx != vlan_idx) {
+ struct ecrnx_vif *old_vif;
+ old_vif = ecrnx_hw->vif_table[sta->vlan_idx];
+ ecrnx_txq_sta_switch_vif(sta, old_vif, vif);
+ sta->vlan_idx = vlan_idx;
+
+ if ((ECRNX_VIF_TYPE(vif) == NL80211_IFTYPE_AP_VLAN) &&
+ (vif->use_4addr)) {
+ WARN((vif->ap_vlan.sta_4a),
+ "4A AP_VLAN interface with more than one sta");
+ vif->ap_vlan.sta_4a = sta;
+ }
+
+ if ((ECRNX_VIF_TYPE(old_vif) == NL80211_IFTYPE_AP_VLAN) &&
+ (old_vif->use_4addr)) {
+ old_vif->ap_vlan.sta_4a = NULL;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * @start_ap: Start acting in AP mode defined by the parameters.
+ */
+static int ecrnx_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *dev,
+ struct cfg80211_ap_settings *settings)
+{
+ struct ecrnx_hw *ecrnx_hw = wiphy_priv(wiphy);
+ struct ecrnx_vif *ecrnx_vif = netdev_priv(dev);
+ struct apm_start_cfm apm_start_cfm;
+ struct ecrnx_ipc_elem_var elem;
+ struct ecrnx_sta *sta;
+ int error = 0;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ /* Forward the information to the LMAC */
+ if ((error = ecrnx_send_apm_start_req(ecrnx_hw, ecrnx_vif, settings,
+ &apm_start_cfm, &elem)))
+ goto end;
+
+ // Check the status
+ switch (apm_start_cfm.status)
+ {
+ case CO_OK:
+ {
+ u8 txq_status = 0;
+ ecrnx_vif->ap.bcmc_index = apm_start_cfm.bcmc_idx;
+ ecrnx_vif->ap.flags = 0;
+ ecrnx_vif->ap.bcn_interval = settings->beacon_interval;
+ sta = &ecrnx_hw->sta_table[apm_start_cfm.bcmc_idx];
+ sta->valid = true;
+ sta->aid = 0;
+ sta->sta_idx = apm_start_cfm.bcmc_idx;
+ sta->ch_idx = apm_start_cfm.ch_idx;
+ sta->vif_idx = ecrnx_vif->vif_index;
+ sta->qos = false;
+ sta->acm = 0;
+ sta->ps.active = false;
+ sta->listen_interval = 5;
+ ecrnx_mu_group_sta_init(sta, NULL);
+ spin_lock_bh(&ecrnx_hw->cb_lock);
+ ecrnx_chanctx_link(ecrnx_vif, apm_start_cfm.ch_idx,
+ &settings->chandef);
+ if (ecrnx_hw->cur_chanctx != apm_start_cfm.ch_idx) {
+ txq_status = ECRNX_TXQ_STOP_CHAN;
+ }
+ ecrnx_txq_vif_init(ecrnx_hw, ecrnx_vif, txq_status);
+ spin_unlock_bh(&ecrnx_hw->cb_lock);
+
+ netif_tx_start_all_queues(dev);
+ netif_carrier_on(dev);
+ error = 0;
+ /* If the AP channel is already the active, we probably skip radar
+ activation on MM_CHANNEL_SWITCH_IND (unless another vif use this
+ ctxt). In anycase retest if radar detection must be activated
+ */
+ if (txq_status == 0) {
+ ecrnx_radar_detection_enable_on_cur_channel(ecrnx_hw);
+ }
+ break;
+ }
+ case CO_BUSY:
+ error = -EINPROGRESS;
+ break;
+ case CO_OP_IN_PROGRESS:
+ error = -EALREADY;
+ break;
+ default:
+ error = -EIO;
+ break;
+ }
+
+ if (error) {
+ netdev_info(dev, "Failed to start AP (%d)", error);
+ } else {
+ netdev_info(dev, "AP started: ch=%d, bcmc_idx=%d",
+ ecrnx_vif->ch_index, ecrnx_vif->ap.bcmc_index);
+ }
+
+ end:
+ ecrnx_ipc_elem_var_deallocs(ecrnx_hw, &elem);
+
+ return error;
+}
+
+
+/**
+ * @change_beacon: Change the beacon parameters for an access point mode
+ * interface. This should reject the call when AP mode wasn't started.
+ */
+static int ecrnx_cfg80211_change_beacon(struct wiphy *wiphy, struct net_device *dev,
+ struct cfg80211_beacon_data *info)
+{
+ struct ecrnx_hw *ecrnx_hw = wiphy_priv(wiphy);
+ struct ecrnx_vif *vif = netdev_priv(dev);
+ struct ecrnx_bcn *bcn = &vif->ap.bcn;
+ struct ecrnx_ipc_elem_var elem;
+ u8 *buf;
+ int error = 0;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ // Build the beacon
+ buf = ecrnx_build_bcn(bcn, info);
+ if (!buf)
+ return -ENOMEM;
+
+ // Sync buffer for FW
+ if ((error = ecrnx_ipc_elem_var_allocs(ecrnx_hw, &elem, bcn->len, DMA_TO_DEVICE,
+ buf, NULL, NULL)))
+ return error;
+
+ // Forward the information to the LMAC
+ error = ecrnx_send_bcn_change(ecrnx_hw, vif->vif_index, elem.dma_addr,
+ bcn->len, bcn->head_len, bcn->tim_len, NULL);
+
+ ecrnx_ipc_elem_var_deallocs(ecrnx_hw, &elem);
+
+ return error;
+}
+
+/**
+ * * @stop_ap: Stop being an AP, including stopping beaconing.
+ */
+static int ecrnx_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *dev, unsigned int link_id)
+{
+ struct ecrnx_hw *ecrnx_hw = wiphy_priv(wiphy);
+ struct ecrnx_vif *ecrnx_vif = netdev_priv(dev);
+ struct ecrnx_sta *sta;
+
+ ecrnx_radar_cancel_cac(&ecrnx_hw->radar);
+ ecrnx_send_apm_stop_req(ecrnx_hw, ecrnx_vif);
+ spin_lock_bh(&ecrnx_hw->cb_lock);
+ ecrnx_chanctx_unlink(ecrnx_vif);
+ spin_unlock_bh(&ecrnx_hw->cb_lock);
+
+ /* delete any remaining STA*/
+ while (!list_empty(&ecrnx_vif->ap.sta_list)) {
+ ecrnx_cfg80211_del_station(wiphy, dev, NULL);
+ }
+
+ /* delete BC/MC STA */
+ sta = &ecrnx_hw->sta_table[ecrnx_vif->ap.bcmc_index];
+ ecrnx_txq_vif_deinit(ecrnx_hw, ecrnx_vif);
+ ecrnx_del_bcn(&ecrnx_vif->ap.bcn);
+ ecrnx_del_csa(ecrnx_vif);
+
+ netif_tx_stop_all_queues(dev);
+ netif_carrier_off(dev);
+
+ netdev_info(dev, "AP Stopped");
+
+ return 0;
+}
+
+/**
+ * @set_monitor_channel: Set the monitor mode channel for the device. If other
+ * interfaces are active this callback should reject the configuration.
+ * If no interfaces are active or the device is down, the channel should
+ * be stored for when a monitor interface becomes active.
+ *
+ * Also called internaly with chandef set to NULL simply to retrieve the channel
+ * configured at firmware level.
+ */
+static int ecrnx_cfg80211_set_monitor_channel(struct wiphy *wiphy,
+ struct cfg80211_chan_def *chandef)
+{
+ struct ecrnx_hw *ecrnx_hw = wiphy_priv(wiphy);
+ struct ecrnx_vif *ecrnx_vif;
+ struct me_config_monitor_cfm cfm;
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ if (ecrnx_hw->monitor_vif == ECRNX_INVALID_VIF)
+ return -EINVAL;
+
+ ecrnx_vif = ecrnx_hw->vif_table[ecrnx_hw->monitor_vif];
+
+ // Do nothing if monitor interface is already configured with the requested channel
+ if (ecrnx_chanctx_valid(ecrnx_hw, ecrnx_vif->ch_index)) {
+ struct ecrnx_chanctx *ctxt;
+ ctxt = &ecrnx_vif->ecrnx_hw->chanctx_table[ecrnx_vif->ch_index];
+ if (chandef && cfg80211_chandef_identical(&ctxt->chan_def, chandef))
+ return 0;
+ }
+
+ // Always send command to firmware. It allows to retrieve channel context index
+ // and its configuration.
+ if (ecrnx_send_config_monitor_req(ecrnx_hw, chandef, &cfm))
+ return -EIO;
+
+ // Always re-set channel context info
+ ecrnx_chanctx_unlink(ecrnx_vif);
+
+
+
+ // If there is also a STA interface not yet connected then monitor interface
+ // will only have a channel context after the connection of the STA interface.
+ if (cfm.chan_index != ECRNX_CH_NOT_SET)
+ {
+ struct cfg80211_chan_def mon_chandef;
+
+ if (ecrnx_hw->vif_started > 1) {
+ // In this case we just want to update the channel context index not
+ // the channel configuration
+ ecrnx_chanctx_link(ecrnx_vif, cfm.chan_index, NULL);
+ return -EBUSY;
+ }
+
+ mon_chandef.chan = ieee80211_get_channel(wiphy, cfm.chan.prim20_freq);
+ mon_chandef.center_freq1 = cfm.chan.center1_freq;
+ mon_chandef.center_freq2 = cfm.chan.center2_freq;
+ mon_chandef.width = chnl2bw[cfm.chan.type];
+ ecrnx_chanctx_link(ecrnx_vif, cfm.chan_index, &mon_chandef);
+ }
+
+ return 0;
+}
+
+/**
+ * @probe_client: probe an associated client, must return a cookie that it
+ * later passes to cfg80211_probe_status().
+ */
+int ecrnx_cfg80211_probe_client(struct wiphy *wiphy, struct net_device *dev,
+ const u8 *peer, u64 *cookie)
+{
+ struct ecrnx_hw *ecrnx_hw = wiphy_priv(wiphy);
+ struct ecrnx_vif *vif = netdev_priv(dev);
+ struct ecrnx_sta *sta = NULL;
+ struct apm_probe_client_cfm cfm;
+ if ((ECRNX_VIF_TYPE(vif) != NL80211_IFTYPE_AP) &&
+ (ECRNX_VIF_TYPE(vif) != NL80211_IFTYPE_AP_VLAN) &&
+ (ECRNX_VIF_TYPE(vif) != NL80211_IFTYPE_P2P_GO) &&
+ (ECRNX_VIF_TYPE(vif) != NL80211_IFTYPE_MESH_POINT))
+ return -EINVAL;
+ list_for_each_entry(sta, &vif->ap.sta_list, list) {
+ if (sta->valid && ether_addr_equal(sta->mac_addr, peer))
+ break;
+}
+
+ if (!sta)
+ return -EINVAL;
+
+ ecrnx_send_apm_probe_req(ecrnx_hw, vif, sta, &cfm);
+
+ if (cfm.status != CO_OK)
+ return -EINVAL;
+
+ *cookie = (u64)cfm.probe_id;
+ return 0;
+}
+
+/**
+ * @set_wiphy_params: Notify that wiphy parameters have changed;
+ * @changed bitfield (see &enum wiphy_params_flags) describes which values
+ * have changed. The actual parameter values are available in
+ * struct wiphy. If returning an error, no value should be changed.
+ */
+static int ecrnx_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
+{
+ return 0;
+}
+
+
+/**
+ * @set_tx_power: set the transmit power according to the parameters,
+ * the power passed is in mBm, to get dBm use MBM_TO_DBM(). The
+ * wdev may be %NULL if power was set for the wiphy, and will
+ * always be %NULL unless the driver supports per-vif TX power
+ * (as advertised by the nl80211 feature flag.)
+ */
+static int ecrnx_cfg80211_set_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev,
+ enum nl80211_tx_power_setting type, int mbm)
+{
+ struct ecrnx_hw *ecrnx_hw = wiphy_priv(wiphy);
+ struct ecrnx_vif *vif;
+ s8 pwr;
+ int res = 0;
+
+ if (type == NL80211_TX_POWER_AUTOMATIC) {
+ pwr = 0x7f;
+ } else {
+ pwr = MBM_TO_DBM(mbm);
+ }
+
+ if (wdev) {
+ vif = container_of(wdev, struct ecrnx_vif, wdev);
+ res = ecrnx_send_set_power(ecrnx_hw, vif->vif_index, pwr, NULL);
+ } else {
+ list_for_each_entry(vif, &ecrnx_hw->vifs, list) {
+ res = ecrnx_send_set_power(ecrnx_hw, vif->vif_index, pwr, NULL);
+ if (res)
+ break;
+ }
+ }
+
+ return res;
+}
+
+/**
+ * @set_power_mgmt: set the power save to one of those two modes:
+ * Power-save off
+ * Power-save on - Dynamic mode
+ */
+static int ecrnx_cfg80211_set_power_mgmt(struct wiphy *wiphy,
+ struct net_device *dev,
+ bool enabled, int timeout)
+{
+ struct ecrnx_hw *ecrnx_hw = wiphy_priv(wiphy);
+ u8 ps_mode;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+ if (timeout >= 0)
+ netdev_info(dev, "Ignore timeout value %d", timeout);
+
+ if (!(ecrnx_hw->version_cfm.features & BIT(MM_FEAT_PS_BIT)))
+ enabled = false;
+
+ if (enabled) {
+ /* Switch to Dynamic Power Save */
+ ps_mode = MM_PS_MODE_ON_DYN;
+ } else {
+ /* Exit Power Save */
+ ps_mode = MM_PS_MODE_OFF;
+ }
+
+ return ecrnx_send_me_set_ps_mode(ecrnx_hw, ps_mode);
+}
+
+static int ecrnx_cfg80211_set_txq_params(struct wiphy *wiphy, struct net_device *dev,
+ struct ieee80211_txq_params *params)
+{
+ struct ecrnx_hw *ecrnx_hw = wiphy_priv(wiphy);
+ struct ecrnx_vif *ecrnx_vif = netdev_priv(dev);
+ u8 hw_queue, aifs, cwmin, cwmax;
+ u32 param;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ hw_queue = ecrnx_ac2hwq[0][params->ac];
+
+ aifs = params->aifs;
+ cwmin = fls(params->cwmin);
+ cwmax = fls(params->cwmax);
+
+ /* Store queue information in general structure */
+ param = (u32) (aifs << 0);
+ param |= (u32) (cwmin << 4);
+ param |= (u32) (cwmax << 8);
+ param |= (u32) (params->txop) << 12;
+
+ /* Send the MM_SET_EDCA_REQ message to the FW */
+ return ecrnx_send_set_edca(ecrnx_hw, hw_queue, param, false, ecrnx_vif->vif_index);
+}
+
+
+/**
+ * @remain_on_channel: Request the driver to remain awake on the specified
+ * channel for the specified duration to complete an off-channel
+ * operation (e.g., public action frame exchange). When the driver is
+ * ready on the requested channel, it must indicate this with an event
+ * notification by calling cfg80211_ready_on_channel().
+ */
+static int
+ecrnx_cfg80211_remain_on_channel(struct wiphy *wiphy, struct wireless_dev *wdev,
+ struct ieee80211_channel *chan,
+ unsigned int duration, u64 *cookie)
+{
+ struct ecrnx_hw *ecrnx_hw = wiphy_priv(wiphy);
+ struct ecrnx_vif *ecrnx_vif = netdev_priv(wdev->netdev);
+ struct ecrnx_roc *roc;
+ int error;
+ unsigned long timer = 0;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ /* For debug purpose (use ftrace kernel option) */
+ trace_roc(ecrnx_vif->vif_index, chan->center_freq, duration);
+
+ /* Check that no other RoC procedure has been launched */
+ if (ecrnx_hw->roc)
+ {
+ ECRNX_ERR("%s-%d,statu error!!!, duration:%d \n", __func__, __LINE__, duration);
+ //exp_report(ecrnx_hw);
+ //wait for slave confirm
+ //ecrnx_hw->p2p_listen.rxdatas = 0;
+#ifdef CONFIG_ECRNX_P2P
+ timer = wait_event_interruptible_timeout(ecrnx_hw->p2p_listen.rxdataq, (ecrnx_hw->roc == NULL), HZ/2);
+ if (timer)
+ ECRNX_PRINT("wait_event: wake up!!! timer:%ld \n", timer);
+ else
+ ECRNX_PRINT("wait_event: timout!!!\n");
+#endif
+ //return -EBUSY;
+ }
+
+ /* Allocate a temporary RoC element */
+ roc = kmalloc(sizeof(struct ecrnx_roc), GFP_KERNEL);
+
+ /* Verify that element has well been allocated */
+ if (!roc)
+ return -ENOMEM;
+
+ /* Initialize the RoC information element */
+ roc->vif = ecrnx_vif;
+ roc->chan = chan;
+ roc->duration = duration;
+ roc->internal = false;
+ roc->on_chan = false;
+
+ /* Initialize the OFFCHAN TX queue to allow off-channel transmissions */
+ ecrnx_txq_offchan_init(ecrnx_vif);
+
+ /* Forward the information to the FMAC */
+ ecrnx_hw->roc = roc;
+ error = ecrnx_send_roc(ecrnx_hw, ecrnx_vif, chan, duration);
+
+ /* If no error, keep all the information for handling of end of procedure */
+ if (error == 0) {
+
+ /* Set the cookie value */
+ *cookie = (u64)(ecrnx_hw->roc_cookie);
+
+#ifdef CONFIG_ECRNX_P2P
+ if(ecrnx_vif->mgmt_reg_stypes & BIT(IEEE80211_STYPE_PROBE_REQ >> 4))
+ {
+ if(ecrnx_send_p2p_start_listen_req(ecrnx_hw, ecrnx_vif, duration))
+ ECRNX_ERR("P2P: start_listen failed\n");
+ }
+#endif
+ } else {
+ kfree(roc);
+ ecrnx_hw->roc = NULL;
+ ecrnx_txq_offchan_deinit(ecrnx_vif);
+ }
+
+ return error;
+}
+
+/**
+ * @cancel_remain_on_channel: Cancel an on-going remain-on-channel operation.
+ * This allows the operation to be terminated prior to timeout based on
+ * the duration value.
+ */
+static int ecrnx_cfg80211_cancel_remain_on_channel(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ u64 cookie)
+{
+ struct ecrnx_hw *ecrnx_hw = wiphy_priv(wiphy);
+ struct ecrnx_vif *ecrnx_vif = netdev_priv(wdev->netdev);
+ int error;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ /* For debug purpose (use ftrace kernel option) */
+ trace_cancel_roc(ecrnx_vif->vif_index);
+
+ /* Check if a RoC procedure is pending */
+ if (!ecrnx_hw->roc)
+ return 0;
+#ifdef CONFIG_ECRNX_P2P
+ //if(ecrnx_vif->mgmt_reg_stypes & BIT(IEEE80211_STYPE_PROBE_REQ >> 4))
+ {
+ error = ecrnx_send_p2p_cancel_listen_req(ecrnx_hw, ecrnx_vif);
+ if(error == 0)
+ ECRNX_PRINT("P2P: cancel_listen OK!!!\n");
+ else
+ ECRNX_ERR("P2P: cancel_listen failed, error=%d\n", error);
+ }
+#endif
+ /* Forward the information to the FMAC */
+ return ecrnx_send_cancel_roc(ecrnx_hw);
+}
+
+/**
+ * @dump_survey: get site survey information.
+ */
+static int ecrnx_cfg80211_dump_survey(struct wiphy *wiphy, struct net_device *netdev,
+ int idx, struct survey_info *info)
+{
+ struct ecrnx_hw *ecrnx_hw = wiphy_priv(wiphy);
+ struct ieee80211_supported_band *sband;
+ struct ecrnx_survey_info *ecrnx_survey;
+
+ //ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ if (idx >= ARRAY_SIZE(ecrnx_hw->survey))
+ return -ENOENT;
+
+ ecrnx_survey = &ecrnx_hw->survey[idx];
+
+ // Check if provided index matches with a supported 2.4GHz channel
+ sband = wiphy->bands[NL80211_BAND_2GHZ];
+ if (sband && idx >= sband->n_channels) {
+ idx -= sband->n_channels;
+ sband = NULL;
+ }
+
+ if (!sband) {
+#ifdef CONFIG_ECRNX_5G
+ // Check if provided index matches with a supported 5GHz channel
+ sband = wiphy->bands[NL80211_BAND_5GHZ];
+#endif
+ if (!sband || idx >= sband->n_channels)
+ return -ENOENT;
+ }
+
+ // Fill the survey
+ info->channel = &sband->channels[idx];
+ info->filled = ecrnx_survey->filled;
+
+ if (ecrnx_survey->filled != 0) {
+ SURVEY_TIME(info) = (u64)ecrnx_survey->chan_time_ms;
+ SURVEY_TIME(info) = (u64)ecrnx_survey->chan_time_busy_ms;
+ info->noise = ecrnx_survey->noise_dbm;
+
+ // Set the survey report as not used
+ ecrnx_survey->filled = 0;
+ }
+
+ return 0;
+}
+
+/**
+ * @get_channel: Get the current operating channel for the virtual interface.
+ * For monitor interfaces, it should return %NULL unless there's a single
+ * current monitoring channel.
+ */
+int ecrnx_cfg80211_get_channel(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ unsigned int link_id,
+ struct cfg80211_chan_def *chandef) {
+ struct ecrnx_hw *ecrnx_hw = wiphy_priv(wiphy);
+ struct ecrnx_vif *ecrnx_vif = container_of(wdev, struct ecrnx_vif, wdev);
+ struct ecrnx_chanctx *ctxt;
+
+ if (!ecrnx_vif->up) {
+ return -ENODATA;
+ }
+
+ if (ecrnx_vif->vif_index == ecrnx_hw->monitor_vif)
+ {
+ //retrieve channel from firmware
+ ecrnx_cfg80211_set_monitor_channel(wiphy, NULL);
+ }
+
+ //Check if channel context is valid
+ if(!ecrnx_chanctx_valid(ecrnx_hw, ecrnx_vif->ch_index)){
+ return -ENODATA;
+ }
+
+ ctxt = &ecrnx_hw->chanctx_table[ecrnx_vif->ch_index];
+ *chandef = ctxt->chan_def;
+
+ return 0;
+}
+
+/**
+ * @mgmt_tx: Transmit a management frame.
+ */
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0))
+static int ecrnx_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
+ struct cfg80211_mgmt_tx_params *params,
+ u64 *cookie)
+#else
+static int ecrnx_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
+ struct ieee80211_channel *channel, bool offchan,
+ unsigned int wait, const u8* buf, size_t len,
+ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0))
+ bool no_cck,
+ #endif
+ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 3, 0))
+ bool dont_wait_for_ack,
+ #endif
+ u64 *cookie)
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) */
+{
+ struct ecrnx_hw *ecrnx_hw = wiphy_priv(wiphy);
+ struct ecrnx_vif *ecrnx_vif = netdev_priv(wdev->netdev);
+ struct ecrnx_sta *ecrnx_sta;
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0))
+ struct ieee80211_channel *channel = params->chan;
+ const u8 *buf = params->buf;
+ bool offchan = false;
+#endif
+ struct ieee80211_mgmt *mgmt = (void *)buf;
+ bool ap = false;
+
+ /* Check if provided VIF is an AP or a STA one */
+ switch (ECRNX_VIF_TYPE(ecrnx_vif)) {
+ case NL80211_IFTYPE_AP_VLAN:
+ ecrnx_vif = ecrnx_vif->ap_vlan.master;
+ break;
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_P2P_GO:
+ case NL80211_IFTYPE_MESH_POINT:
+ ap = true;
+ break;
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_P2P_CLIENT:
+ default:
+ break;
+ }
+
+ /* Get STA on which management frame has to be sent */
+ ecrnx_sta = ecrnx_retrieve_sta(ecrnx_hw, ecrnx_vif, mgmt->da,
+ mgmt->frame_control, ap);
+
+ trace_mgmt_tx((channel) ? channel->center_freq : 0,
+ ecrnx_vif->vif_index, (ecrnx_sta) ? ecrnx_sta->sta_idx : 0xFF,
+ mgmt);
+
+ if (ap || ecrnx_sta)
+ goto send_frame;
+
+ /* Not an AP interface sending frame to unknown STA:
+ * This is allowed for external authetication */
+ if ((ecrnx_vif->sta.flags & ECRNX_STA_EXT_AUTH) && ieee80211_is_auth(mgmt->frame_control))
+ goto send_frame;
+
+ if(ieee80211_is_probe_resp(mgmt->frame_control))
+ goto p2p_send_frame;
+
+ /* Otherwise ROC is needed */
+ if (!channel)
+ return -EINVAL;
+
+ /* Check that a RoC is already pending */
+ if (ecrnx_hw->roc) {
+ /* Get VIF used for current ROC */
+
+ /* Check if RoC channel is the same than the required one */
+ if ((ecrnx_hw->roc->vif != ecrnx_vif) ||
+ (ecrnx_hw->roc->chan->center_freq != channel->center_freq))
+ return -EINVAL;
+
+ } else {
+ u64 cookie;
+ int error;
+
+ /* Start a ROC procedure for 30ms */
+ error = ecrnx_cfg80211_remain_on_channel(wiphy, wdev, channel,
+ 30, &cookie);
+ if (error)
+ return error;
+
+ /* Need to keep in mind that RoC has been launched internally in order to
+ * avoid to call the cfg80211 callback once expired */
+ ecrnx_hw->roc->internal = true;
+#ifdef CONFIG_ECRNX_P2P
+ ecrnx_hw->p2p_listen.rxdatas = 0;
+ wait_event_interruptible_timeout(ecrnx_hw->p2p_listen.rxdataq, ecrnx_hw->p2p_listen.rxdatas, HZ);
+#endif
+ }
+
+p2p_send_frame:
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0))
+ offchan = true;
+#endif
+
+send_frame:
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0))
+ return ecrnx_start_mgmt_xmit(ecrnx_vif, ecrnx_sta, params, offchan, cookie);
+#else
+ return ecrnx_start_mgmt_xmit(ecrnx_vif, ecrnx_sta, channel, offchan, wait, buf, len, no_cck, dont_wait_for_ack, cookie);
+#endif
+}
+
+/**
+ * @start_radar_detection: Start radar detection in the driver.
+ */
+static int ecrnx_cfg80211_start_radar_detection(struct wiphy *wiphy,
+ struct net_device *dev,
+ struct cfg80211_chan_def *chandef,
+ u32 cac_time_ms)
+{
+ struct ecrnx_hw *ecrnx_hw = wiphy_priv(wiphy);
+ struct ecrnx_vif *ecrnx_vif = netdev_priv(dev);
+ struct apm_start_cac_cfm cfm;
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 15, 0))
+ ecrnx_radar_start_cac(&ecrnx_hw->radar, cac_time_ms, ecrnx_vif);
+#endif
+ ecrnx_send_apm_start_cac_req(ecrnx_hw, ecrnx_vif, chandef, &cfm);
+
+ if (cfm.status == CO_OK) {
+ spin_lock_bh(&ecrnx_hw->cb_lock);
+ ecrnx_chanctx_link(ecrnx_vif, cfm.ch_idx, chandef);
+ if (ecrnx_hw->cur_chanctx == ecrnx_vif->ch_index)
+ ecrnx_radar_detection_enable(&ecrnx_hw->radar,
+ ECRNX_RADAR_DETECT_REPORT,
+ ECRNX_RADAR_RIU);
+ spin_unlock_bh(&ecrnx_hw->cb_lock);
+ } else {
+ return -EIO;
+ }
+
+ return 0;
+}
+
+/**
+ * @update_ft_ies: Provide updated Fast BSS Transition information to the
+ * driver. If the SME is in the driver/firmware, this information can be
+ * used in building Authentication and Reassociation Request frames.
+ */
+static int ecrnx_cfg80211_update_ft_ies(struct wiphy *wiphy,
+ struct net_device *dev,
+ struct cfg80211_update_ft_ies_params *ftie)
+{
+ struct ecrnx_hw *ecrnx_hw = wiphy_priv(wiphy);
+ struct ecrnx_vif *vif = netdev_priv(dev);
+ const struct ecrnx_element *rsne = NULL, *mde = NULL, *fte = NULL, *elem;
+ bool ft_in_non_rsn = false;
+ int fties_len = 0;
+ u8 *ft_assoc_ies, *pos;
+ if ((ECRNX_VIF_TYPE(vif) != NL80211_IFTYPE_STATION) ||
+ (vif->sta.ft_assoc_ies == NULL))
+ return 0;
+ for_each_ecrnx_element(elem, ftie->ie, ftie->ie_len) {
+ if (elem->id == WLAN_EID_RSN)
+ rsne = elem;
+ else if (elem->id == WLAN_EID_MOBILITY_DOMAIN)
+ mde = elem;
+ else if (elem->id == WLAN_EID_FAST_BSS_TRANSITION)
+ fte = elem;
+ else
+ netdev_warn(dev, "Unexpected FT element %d\n", elem->id);
+ }
+ if (!mde) {
+ netdev_warn(dev, "Didn't find Mobility_Domain Element\n");
+ return 0;
+ } else if (!rsne && !fte) {
+ ft_in_non_rsn = true;
+ } else if (!rsne || !fte) {
+ netdev_warn(dev, "Didn't find RSN or Fast Transition Element\n");
+ return 0;
+ }
+ for_each_ecrnx_element(elem, vif->sta.ft_assoc_ies, vif->sta.ft_assoc_ies_len) {
+ if ((elem->id == WLAN_EID_RSN) ||
+ (elem->id == WLAN_EID_MOBILITY_DOMAIN) ||
+ (elem->id == WLAN_EID_FAST_BSS_TRANSITION))
+ fties_len += elem->datalen + sizeof(struct ecrnx_element);
+ }
+ ft_assoc_ies = kmalloc(vif->sta.ft_assoc_ies_len - fties_len + ftie->ie_len,
+ GFP_KERNEL);
+ if (!ft_assoc_ies) {
+ netdev_warn(dev, "Fail to allocate buffer for association elements");
+ }
+ pos = ft_assoc_ies;
+ for_each_ecrnx_element(elem, vif->sta.ft_assoc_ies, vif->sta.ft_assoc_ies_len) {
+ if (elem->id == WLAN_EID_RSN) {
+ if (ft_in_non_rsn) {
+ netdev_warn(dev, "Found RSN element in non RSN FT");
+ goto abort;
+ } else if (!rsne) {
+ netdev_warn(dev, "Found several RSN element");
+ goto abort;
+ } else {
+ memcpy(pos, rsne, sizeof(*rsne) + rsne->datalen);
+ pos += sizeof(*rsne) + rsne->datalen;
+ rsne = NULL;
+ }
+ } else if (elem->id == WLAN_EID_MOBILITY_DOMAIN) {
+ if (!mde) {
+ netdev_warn(dev, "Found several Mobility Domain element");
+ goto abort;
+ } else {
+ memcpy(pos, mde, sizeof(*mde) + mde->datalen);
+ pos += sizeof(*mde) + mde->datalen;
+ mde = NULL;
+ }
+ }
+ else if (elem->id == WLAN_EID_FAST_BSS_TRANSITION) {
+ if (ft_in_non_rsn) {
+ netdev_warn(dev, "Found Fast Transition element in non RSN FT");
+ goto abort;
+ } else if (!fte) {
+ netdev_warn(dev, "found several Fast Transition element");
+ goto abort;
+ } else {
+ memcpy(pos, fte, sizeof(*fte) + fte->datalen);
+ pos += sizeof(*fte) + fte->datalen;
+ fte = NULL;
+ }
+ }
+ else {
+ if (fte && !mde) {
+ memcpy(pos, fte, sizeof(*fte) + fte->datalen);
+ pos += sizeof(*fte) + fte->datalen;
+ fte = NULL;
+ }
+ memcpy(pos, elem, sizeof(*elem) + elem->datalen);
+ pos += sizeof(*elem) + elem->datalen;
+ }
+ }
+ if (fte) {
+ memcpy(pos, fte, sizeof(*fte) + fte->datalen);
+ pos += sizeof(*fte) + fte->datalen;
+ fte = NULL;
+ }
+ kfree(vif->sta.ft_assoc_ies);
+ vif->sta.ft_assoc_ies = ft_assoc_ies;
+ vif->sta.ft_assoc_ies_len = pos - ft_assoc_ies;
+ if (vif->sta.flags & ECRNX_STA_FT_OVER_DS) {
+ struct sm_connect_cfm sm_connect_cfm;
+ struct cfg80211_connect_params sme;
+ memset(&sme, 0, sizeof(sme));
+ rsne = cfg80211_find_ecrnx_elem(WLAN_EID_RSN, vif->sta.ft_assoc_ies,
+ vif->sta.ft_assoc_ies_len);
+ if (rsne && ecrnx_rsne_to_connect_params(rsne, &sme)) {
+ netdev_warn(dev, "FT RSN parsing failed\n");
+ return 0;
+ }
+ sme.ssid_len = vif->sta.ft_assoc_ies[1];
+ sme.ssid = &vif->sta.ft_assoc_ies[2];
+ sme.bssid = vif->sta.ft_target_ap;
+ sme.ie = &vif->sta.ft_assoc_ies[2 + sme.ssid_len];
+ sme.ie_len = vif->sta.ft_assoc_ies_len - (2 + sme.ssid_len);
+ sme.auth_type = NL80211_AUTHTYPE_FT;
+ ecrnx_send_sm_connect_req(ecrnx_hw, vif, &sme, &sm_connect_cfm);
+ vif->sta.flags &= ~ECRNX_STA_FT_OVER_DS;
+ } else if (vif->sta.flags & ECRNX_STA_FT_OVER_AIR) {
+ uint8_t ssid_len;
+ vif->sta.flags &= ~ECRNX_STA_FT_OVER_AIR;
+ ssid_len = vif->sta.ft_assoc_ies[1] + 2;
+ if (ecrnx_send_sm_ft_auth_rsp(ecrnx_hw, vif, &vif->sta.ft_assoc_ies[ssid_len],
+ vif->sta.ft_assoc_ies_len - ssid_len))
+ netdev_err(dev, "FT Over Air: Failed to send updated assoc elem\n");
+ }
+ return 0;
+abort:
+ kfree(ft_assoc_ies);
+ return 0;
+}
+
+/**
+ * @set_cqm_rssi_config: Configure connection quality monitor RSSI threshold.
+ */
+static int ecrnx_cfg80211_set_cqm_rssi_config(struct wiphy *wiphy,
+ struct net_device *dev,
+ int32_t rssi_thold, uint32_t rssi_hyst)
+{
+ struct ecrnx_hw *ecrnx_hw = wiphy_priv(wiphy);
+ struct ecrnx_vif *ecrnx_vif = netdev_priv(dev);
+
+ return ecrnx_send_cfg_rssi_req(ecrnx_hw, ecrnx_vif->vif_index, rssi_thold, rssi_hyst);
+}
+
+/**
+ *
+ * @channel_switch: initiate channel-switch procedure (with CSA). Driver is
+ * responsible for veryfing if the switch is possible. Since this is
+ * inherently tricky driver may decide to disconnect an interface later
+ * with cfg80211_stop_iface(). This doesn't mean driver can accept
+ * everything. It should do it's best to verify requests and reject them
+ * as soon as possible.
+ */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)
+static int ecrnx_cfg80211_channel_switch(struct wiphy *wiphy,
+ struct net_device *dev,
+ struct cfg80211_csa_settings *params)
+{
+ struct ecrnx_hw *ecrnx_hw = wiphy_priv(wiphy);
+ struct ecrnx_vif *vif = netdev_priv(dev);
+ struct ecrnx_ipc_elem_var elem;
+ struct ecrnx_bcn *bcn, *bcn_after;
+ struct ecrnx_csa *csa;
+ u16 csa_oft[BCN_MAX_CSA_CPT];
+ u8 *buf;
+ int i, error = 0;
+
+
+ if (vif->ap.csa)
+ return -EBUSY;
+
+ if (params->n_counter_offsets_beacon > BCN_MAX_CSA_CPT)
+ return -EINVAL;
+
+ /* Build the new beacon with CSA IE */
+ bcn = &vif->ap.bcn;
+ buf = ecrnx_build_bcn(bcn, &params->beacon_csa);
+ if (!buf)
+ return -ENOMEM;
+
+ memset(csa_oft, 0, sizeof(csa_oft));
+ for (i = 0; i < params->n_counter_offsets_beacon; i++)
+ {
+ csa_oft[i] = params->counter_offsets_beacon[i] + bcn->head_len +
+ bcn->tim_len;
+ }
+
+ /* If count is set to 0 (i.e anytime after this beacon) force it to 2 */
+ if (params->count == 0) {
+ params->count = 2;
+ for (i = 0; i < params->n_counter_offsets_beacon; i++)
+ {
+ buf[csa_oft[i]] = 2;
+ }
+ }
+
+ if ((error = ecrnx_ipc_elem_var_allocs(ecrnx_hw, &elem, bcn->len,
+ DMA_TO_DEVICE, buf, NULL, NULL))) {
+ goto end;
+ }
+
+ /* Build the beacon to use after CSA. It will only be sent to fw once
+ CSA is over, but do it before sending the beacon as it must be ready
+ when CSA is finished. */
+ csa = kzalloc(sizeof(struct ecrnx_csa), GFP_KERNEL);
+ if (!csa) {
+ error = -ENOMEM;
+ goto end;
+ }
+
+ bcn_after = &csa->bcn;
+ buf = ecrnx_build_bcn(bcn_after, &params->beacon_after);
+ if (!buf) {
+ error = -ENOMEM;
+ ecrnx_del_csa(vif);
+ goto end;
+ }
+
+ if ((error = ecrnx_ipc_elem_var_allocs(ecrnx_hw, &csa->elem, bcn_after->len,
+ DMA_TO_DEVICE, buf, NULL, NULL))) {
+ goto end;
+ }
+
+ vif->ap.csa = csa;
+ csa->vif = vif;
+ csa->chandef = params->chandef;
+
+ /* Send new Beacon. FW will extract channel and count from the beacon */
+ error = ecrnx_send_bcn_change(ecrnx_hw, vif->vif_index, elem.dma_addr,
+ bcn->len, bcn->head_len, bcn->tim_len, csa_oft);
+
+ if (error) {
+ ecrnx_del_csa(vif);
+ goto end;
+ } else {
+ INIT_WORK(&csa->work, ecrnx_csa_finish);
+ cfg80211_ch_switch_started_notify(dev, &csa->chandef, params->count);
+ }
+
+ end:
+ ecrnx_ipc_elem_var_deallocs(ecrnx_hw, &elem);
+ return error;
+}
+#endif
+
+/*
+ * @tdls_mgmt: prepare TDLS action frame packets and forward them to FW
+ */
+static int ecrnx_cfg80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
+ const u8 *peer, int link_id, u8 action_code,
+ u8 dialog_token, u16 status_code,
+ u32 peer_capability, bool initiator,
+ const u8 *buf, size_t len)
+{
+ struct ecrnx_hw *ecrnx_hw = wiphy_priv(wiphy);
+ struct ecrnx_vif *ecrnx_vif = netdev_priv(dev);
+ int ret = 0;
+
+ /* make sure we support TDLS */
+ if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS))
+ return -ENOTSUPP;
+
+ /* make sure we are in station mode (and connected) */
+ if ((ECRNX_VIF_TYPE(ecrnx_vif) != NL80211_IFTYPE_STATION) ||
+ (!ecrnx_vif->up) || (!ecrnx_vif->sta.ap))
+ return -ENOTSUPP;
+
+ /* only one TDLS link is supported */
+ if ((action_code == WLAN_TDLS_SETUP_REQUEST) &&
+ (ecrnx_vif->sta.tdls_sta) &&
+ (ecrnx_vif->tdls_status == TDLS_LINK_ACTIVE)) {
+ ECRNX_ERR("%s: only one TDLS link is supported!\n", __func__);
+ return -ENOTSUPP;
+ }
+
+ if ((action_code == WLAN_TDLS_DISCOVERY_REQUEST) &&
+ (ecrnx_hw->mod_params->ps_on)) {
+ ECRNX_ERR("%s: discovery request is not supported when "
+ "power-save is enabled!\n", __func__);
+ return -ENOTSUPP;
+ }
+
+ switch (action_code) {
+ case WLAN_TDLS_SETUP_RESPONSE:
+ /* only one TDLS link is supported */
+ if ((status_code == 0) &&
+ (ecrnx_vif->sta.tdls_sta) &&
+ (ecrnx_vif->tdls_status == TDLS_LINK_ACTIVE)) {
+ ECRNX_ERR("%s: only one TDLS link is supported!\n", __func__);
+ status_code = WLAN_STATUS_REQUEST_DECLINED;
+ }
+ /* fall-through */
+ case WLAN_TDLS_SETUP_REQUEST:
+ case WLAN_TDLS_TEARDOWN:
+ case WLAN_TDLS_DISCOVERY_REQUEST:
+ case WLAN_TDLS_SETUP_CONFIRM:
+ case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
+ ret = ecrnx_tdls_send_mgmt_packet_data(ecrnx_hw, ecrnx_vif, peer, action_code,
+ dialog_token, status_code, peer_capability, initiator, buf, len, 0, NULL);
+ break;
+
+ default:
+ ECRNX_ERR("%s: Unknown TDLS mgmt/action frame %pM\n",
+ __func__, peer);
+ ret = -EOPNOTSUPP;
+ break;
+ }
+
+ if (action_code == WLAN_TDLS_SETUP_REQUEST) {
+ ecrnx_vif->tdls_status = TDLS_SETUP_REQ_TX;
+ } else if (action_code == WLAN_TDLS_SETUP_RESPONSE) {
+ ecrnx_vif->tdls_status = TDLS_SETUP_RSP_TX;
+ } else if ((action_code == WLAN_TDLS_SETUP_CONFIRM) && (ret == CO_OK)) {
+ ecrnx_vif->tdls_status = TDLS_LINK_ACTIVE;
+ /* Set TDLS active */
+ ecrnx_vif->sta.tdls_sta->tdls.active = true;
+ }
+
+ return ret;
+}
+
+/*
+ * @tdls_oper: execute TDLS operation
+ */
+static int ecrnx_cfg80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
+ const u8 *peer, enum nl80211_tdls_operation oper)
+{
+ struct ecrnx_hw *ecrnx_hw = wiphy_priv(wiphy);
+ struct ecrnx_vif *ecrnx_vif = netdev_priv(dev);
+ int error;
+
+ if (oper != NL80211_TDLS_DISABLE_LINK)
+ return 0;
+
+ if (!ecrnx_vif->sta.tdls_sta) {
+ ECRNX_ERR("%s: TDLS station %pM does not exist\n", __func__, peer);
+ return -ENOLINK;
+ }
+
+ if (memcmp(ecrnx_vif->sta.tdls_sta->mac_addr, peer, ETH_ALEN) == 0) {
+ /* Disable Channel Switch */
+ if (!ecrnx_send_tdls_cancel_chan_switch_req(ecrnx_hw, ecrnx_vif,
+ ecrnx_vif->sta.tdls_sta,
+ NULL))
+ ecrnx_vif->sta.tdls_sta->tdls.chsw_en = false;
+
+ netdev_info(dev, "Del TDLS sta %d (%pM)",
+ ecrnx_vif->sta.tdls_sta->sta_idx,
+ ecrnx_vif->sta.tdls_sta->mac_addr);
+ /* Ensure that we won't process PS change ind */
+ spin_lock_bh(&ecrnx_hw->cb_lock);
+ ecrnx_vif->sta.tdls_sta->ps.active = false;
+ ecrnx_vif->sta.tdls_sta->valid = false;
+ spin_unlock_bh(&ecrnx_hw->cb_lock);
+ ecrnx_txq_sta_deinit(ecrnx_hw, ecrnx_vif->sta.tdls_sta);
+ error = ecrnx_send_me_sta_del(ecrnx_hw, ecrnx_vif->sta.tdls_sta->sta_idx, true);
+ if ((error != 0) && (error != -EPIPE))
+ return error;
+
+#ifdef CONFIG_ECRNX_BFMER
+ // Disable Beamformer if supported
+ ecrnx_bfmer_report_del(ecrnx_hw, ecrnx_vif->sta.tdls_sta);
+ ecrnx_mu_group_sta_del(ecrnx_hw, ecrnx_vif->sta.tdls_sta);
+#endif /* CONFIG_ECRNX_BFMER */
+
+ /* Set TDLS not active */
+ ecrnx_vif->sta.tdls_sta->tdls.active = false;
+ ecrnx_dbgfs_unregister_sta(ecrnx_hw, ecrnx_vif->sta.tdls_sta);
+ // Remove TDLS station
+ ecrnx_vif->tdls_status = TDLS_LINK_IDLE;
+ ecrnx_vif->sta.tdls_sta = NULL;
+ }
+
+ return 0;
+}
+
+/*
+ * @tdls_channel_switch: enable TDLS channel switch
+ */
+static int ecrnx_cfg80211_tdls_channel_switch(struct wiphy *wiphy,
+ struct net_device *dev,
+ const u8 *addr, u8 oper_class,
+ struct cfg80211_chan_def *chandef)
+{
+ struct ecrnx_vif *ecrnx_vif = netdev_priv(dev);
+ struct ecrnx_hw *ecrnx_hw = wiphy_priv(wiphy);
+ struct ecrnx_sta *ecrnx_sta = ecrnx_vif->sta.tdls_sta;
+ struct tdls_chan_switch_cfm cfm;
+ int error;
+
+ if ((!ecrnx_sta) || (memcmp(addr, ecrnx_sta->mac_addr, ETH_ALEN))) {
+ ECRNX_ERR("%s: TDLS station %pM doesn't exist\n", __func__, addr);
+ return -ENOLINK;
+ }
+
+ if (!ecrnx_sta->tdls.chsw_allowed) {
+ ECRNX_ERR("%s: TDLS station %pM does not support TDLS channel switch\n", __func__, addr);
+ return -ENOTSUPP;
+ }
+
+ error = ecrnx_send_tdls_chan_switch_req(ecrnx_hw, ecrnx_vif, ecrnx_sta,
+ ecrnx_sta->tdls.initiator,
+ oper_class, chandef, &cfm);
+ if (error)
+ return error;
+
+ if (!cfm.status) {
+ ecrnx_sta->tdls.chsw_en = true;
+ return 0;
+ } else {
+ ECRNX_ERR("%s: TDLS channel switch already enabled and only one is supported\n", __func__);
+ return -EALREADY;
+ }
+}
+
+/*
+ * @tdls_cancel_channel_switch: disable TDLS channel switch
+ */
+static void ecrnx_cfg80211_tdls_cancel_channel_switch(struct wiphy *wiphy,
+ struct net_device *dev,
+ const u8 *addr)
+{
+ struct ecrnx_vif *ecrnx_vif = netdev_priv(dev);
+ struct ecrnx_hw *ecrnx_hw = wiphy_priv(wiphy);
+ struct ecrnx_sta *ecrnx_sta = ecrnx_vif->sta.tdls_sta;
+ struct tdls_cancel_chan_switch_cfm cfm;
+
+ if (!ecrnx_sta)
+ return;
+
+ if (!ecrnx_send_tdls_cancel_chan_switch_req(ecrnx_hw, ecrnx_vif,
+ ecrnx_sta, &cfm))
+ ecrnx_sta->tdls.chsw_en = false;
+}
+
+/**
+ * @change_bss: Modify parameters for a given BSS (mainly for AP mode).
+ */
+static int ecrnx_cfg80211_change_bss(struct wiphy *wiphy, struct net_device *dev,
+ struct bss_parameters *params)
+{
+ struct ecrnx_vif *ecrnx_vif = netdev_priv(dev);
+ int res = -EOPNOTSUPP;
+
+ if (((ECRNX_VIF_TYPE(ecrnx_vif) == NL80211_IFTYPE_AP) ||
+ (ECRNX_VIF_TYPE(ecrnx_vif) == NL80211_IFTYPE_P2P_GO)) &&
+ (params->ap_isolate > -1)) {
+
+ if (params->ap_isolate)
+ ecrnx_vif->ap.flags |= ECRNX_AP_ISOLATE;
+ else
+ ecrnx_vif->ap.flags &= ~ECRNX_AP_ISOLATE;
+
+ res = 0;
+ }
+
+ return res;
+}
+
+
+/**
+ * @get_station: get station information for the station identified by @mac
+ */
+static int ecrnx_fill_station_info(struct ecrnx_sta *sta, struct ecrnx_vif *vif,
+ struct station_info *sinfo)
+{
+ struct ecrnx_sta_stats *stats = &sta->stats;
+ struct rx_vector_1 *rx_vect1 = &stats->last_rx.rx_vect1;
+
+ // Generic info
+ sinfo->generation = vif->generation;
+
+ //sinfo->inactive_time = jiffies_to_msecs(jiffies - stats->last_act);
+ sinfo->rx_bytes = stats->rx_bytes;
+ sinfo->tx_bytes = stats->tx_bytes;
+ sinfo->tx_packets = stats->tx_pkts;
+ sinfo->rx_packets = stats->rx_pkts;
+ sinfo->signal = rx_vect1->rssi1;
+ sinfo->tx_failed = 1;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)
+ switch (rx_vect1->ch_bw) {
+ case PHY_CHNL_BW_20:
+ sinfo->rxrate.bw = RATE_INFO_BW_20;
+ break;
+ case PHY_CHNL_BW_40:
+ sinfo->rxrate.bw = RATE_INFO_BW_40;
+ break;
+ case PHY_CHNL_BW_80:
+ sinfo->rxrate.bw = RATE_INFO_BW_80;
+ break;
+ case PHY_CHNL_BW_160:
+ sinfo->rxrate.bw = RATE_INFO_BW_160;
+ break;
+ default:
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0)
+ sinfo->rxrate.bw = RATE_INFO_BW_HE_RU;
+#else
+ sinfo->rxrate.bw = RATE_INFO_BW_160;
+#endif
+ break;
+ }
+#endif
+
+ switch (rx_vect1->format_mod) {
+ case FORMATMOD_NON_HT:
+ case FORMATMOD_NON_HT_DUP_OFDM:
+ sinfo->rxrate.flags = 0;
+ sinfo->rxrate.legacy = legrates_lut[rx_vect1->leg_rate].rate;
+ break;
+ case FORMATMOD_HT_MF:
+ case FORMATMOD_HT_GF:
+ sinfo->rxrate.flags = RATE_INFO_FLAGS_MCS;
+ if (rx_vect1->ht.short_gi)
+ sinfo->rxrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
+ sinfo->rxrate.mcs = rx_vect1->ht.mcs;
+ break;
+ case FORMATMOD_VHT:
+ sinfo->rxrate.flags = RATE_INFO_FLAGS_VHT_MCS;
+ if (rx_vect1->vht.short_gi)
+ sinfo->rxrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
+ sinfo->rxrate.mcs = rx_vect1->vht.mcs;
+ sinfo->rxrate.nss = rx_vect1->vht.nss;
+ break;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)
+#if CONFIG_ECRNX_HE
+ case FORMATMOD_HE_MU:
+ sinfo->rxrate.he_ru_alloc = rx_vect1->he.ru_size;
+ break;
+ case FORMATMOD_HE_SU:
+ case FORMATMOD_HE_ER:
+ case FORMATMOD_HE_TB:
+ sinfo->rxrate.flags = RATE_INFO_FLAGS_HE_MCS;
+ sinfo->rxrate.mcs = rx_vect1->he.mcs;
+ sinfo->rxrate.nss = rx_vect1->he.nss;
+ sinfo->rxrate.he_gi = rx_vect1->he.gi_type;
+ sinfo->rxrate.he_dcm = rx_vect1->he.dcm;
+ break;
+#endif
+#endif
+ default :
+ return -EINVAL;
+ }
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0)
+ sinfo->filled |= (STATION_INFO_INACTIVE_TIME |
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+ STATION_INFO_RX_BYTES64 |
+ STATION_INFO_TX_BYTES64 |
+#endif
+ STATION_INFO_RX_PACKETS |
+ STATION_INFO_TX_PACKETS |
+ STATION_INFO_SIGNAL |
+ STATION_INFO_RX_BITRATE);
+#else
+ sinfo->filled = (BIT(NL80211_STA_INFO_RX_BYTES64) |
+ BIT(NL80211_STA_INFO_TX_BYTES64) |
+ BIT(NL80211_STA_INFO_RX_PACKETS) |
+ BIT(NL80211_STA_INFO_TX_PACKETS) |
+ BIT(NL80211_STA_INFO_SIGNAL) |
+ BIT(NL80211_STA_INFO_TX_BITRATE) |
+ BIT(NL80211_STA_INFO_TX_FAILED) |
+ BIT(NL80211_STA_INFO_RX_BITRATE));
+#endif
+
+ // Mesh specific info
+ if (ECRNX_VIF_TYPE(vif) == NL80211_IFTYPE_MESH_POINT)
+ {
+ struct mesh_peer_info_cfm peer_info_cfm;
+ if (ecrnx_send_mesh_peer_info_req(vif->ecrnx_hw, vif, sta->sta_idx,
+ &peer_info_cfm))
+ return -ENOMEM;
+
+ peer_info_cfm.last_bcn_age = peer_info_cfm.last_bcn_age / 1000;
+ if (peer_info_cfm.last_bcn_age < sinfo->inactive_time)
+ sinfo->inactive_time = peer_info_cfm.last_bcn_age;
+
+ sinfo->llid = peer_info_cfm.local_link_id;
+ sinfo->plid = peer_info_cfm.peer_link_id;
+ sinfo->plink_state = peer_info_cfm.link_state;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+ sinfo->local_pm = peer_info_cfm.local_ps_mode;
+ sinfo->peer_pm = peer_info_cfm.peer_ps_mode;
+ sinfo->nonpeer_pm = peer_info_cfm.non_peer_ps_mode;
+#endif
+ sinfo->filled |= (BIT(NL80211_STA_INFO_LLID) |
+ BIT(NL80211_STA_INFO_PLID) |
+ BIT(NL80211_STA_INFO_PLINK_STATE) |
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+ BIT(NL80211_STA_INFO_LOCAL_PM) |
+ BIT(NL80211_STA_INFO_PEER_PM) |
+ BIT(NL80211_STA_INFO_NONPEER_PM)|
+#endif
+ 0);
+ }
+
+ sinfo->txrate.legacy = 0x6818;
+
+ return 0;
+}
+
+static int ecrnx_cfg80211_get_station(struct wiphy *wiphy, struct net_device *dev,
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 16, 0))
+ u8 *mac,
+#else
+ const u8 *mac,
+#endif
+ struct station_info *sinfo)
+{
+ struct ecrnx_vif *vif = netdev_priv(dev);
+ struct ecrnx_sta *sta = NULL;
+
+ if (ECRNX_VIF_TYPE(vif) == NL80211_IFTYPE_MONITOR)
+ return -EINVAL;
+ else if ((ECRNX_VIF_TYPE(vif) == NL80211_IFTYPE_STATION) ||
+ (ECRNX_VIF_TYPE(vif) == NL80211_IFTYPE_P2P_CLIENT)) {
+ if (vif->sta.ap && ether_addr_equal(vif->sta.ap->mac_addr, mac))
+ sta = vif->sta.ap;
+ }
+ else
+ {
+ struct ecrnx_sta *sta_iter;
+ list_for_each_entry(sta_iter, &vif->ap.sta_list, list) {
+ if (sta_iter->valid && ether_addr_equal(sta_iter->mac_addr, mac)) {
+ sta = sta_iter;
+ break;
+ }
+ }
+ }
+
+ if (sta)
+ return ecrnx_fill_station_info(sta, vif, sinfo);
+
+ return -EINVAL;
+}
+
+int ecrnx_cfg80211_dump_station(struct wiphy *wiphy, struct net_device *dev,
+ int idx, u8 *mac, struct station_info *sinfo)
+{
+ struct ecrnx_vif *vif = netdev_priv(dev);
+ struct ecrnx_sta *sta = NULL;
+
+ if (ECRNX_VIF_TYPE(vif) == NL80211_IFTYPE_MONITOR)
+ return -EINVAL;
+ else if ((ECRNX_VIF_TYPE(vif) == NL80211_IFTYPE_STATION) ||
+ (ECRNX_VIF_TYPE(vif) == NL80211_IFTYPE_P2P_CLIENT)) {
+ if ((idx == 0) && vif->sta.ap && vif->sta.ap->valid)
+ sta = vif->sta.ap;
+ } else {
+ struct ecrnx_sta *sta_iter;
+ int i = 0;
+ list_for_each_entry(sta_iter, &vif->ap.sta_list, list) {
+ if (i == idx) {
+ sta = sta_iter;
+ break;
+ }
+ i++;
+ }
+ }
+
+ if (sta == NULL)
+ return -ENOENT;
+
+
+ memcpy(mac, &sta->mac_addr, ETH_ALEN);
+
+ return ecrnx_fill_station_info(sta, vif, sinfo);
+}
+
+/**
+ * @add_mpath: add a fixed mesh path
+ */
+static int ecrnx_cfg80211_add_mpath(struct wiphy *wiphy, struct net_device *dev,
+ const u8 *dst, const u8 *next_hop)
+{
+ struct ecrnx_vif *ecrnx_vif = netdev_priv(dev);
+ struct ecrnx_hw *ecrnx_hw = wiphy_priv(wiphy);
+ struct mesh_path_update_cfm cfm;
+
+ if (ECRNX_VIF_TYPE(ecrnx_vif) != NL80211_IFTYPE_MESH_POINT)
+ return -ENOTSUPP;
+
+ return ecrnx_send_mesh_path_update_req(ecrnx_hw, ecrnx_vif, dst, next_hop, &cfm);
+}
+
+/**
+ * @del_mpath: delete a given mesh path
+ */
+static int ecrnx_cfg80211_del_mpath(struct wiphy *wiphy, struct net_device *dev,
+ const u8 *dst)
+{
+ struct ecrnx_vif *ecrnx_vif = netdev_priv(dev);
+ struct ecrnx_hw *ecrnx_hw = wiphy_priv(wiphy);
+ struct mesh_path_update_cfm cfm;
+
+ if (ECRNX_VIF_TYPE(ecrnx_vif) != NL80211_IFTYPE_MESH_POINT)
+ return -ENOTSUPP;
+
+ return ecrnx_send_mesh_path_update_req(ecrnx_hw, ecrnx_vif, dst, NULL, &cfm);
+}
+
+/**
+ * @change_mpath: change a given mesh path
+ */
+static int ecrnx_cfg80211_change_mpath(struct wiphy *wiphy, struct net_device *dev,
+ const u8 *dst, const u8 *next_hop)
+{
+ struct ecrnx_vif *ecrnx_vif = netdev_priv(dev);
+ struct ecrnx_hw *ecrnx_hw = wiphy_priv(wiphy);
+ struct mesh_path_update_cfm cfm;
+
+ if (ECRNX_VIF_TYPE(ecrnx_vif) != NL80211_IFTYPE_MESH_POINT)
+ return -ENOTSUPP;
+
+ return ecrnx_send_mesh_path_update_req(ecrnx_hw, ecrnx_vif, dst, next_hop, &cfm);
+}
+
+/**
+ * @get_mpath: get a mesh path for the given parameters
+ */
+static int ecrnx_cfg80211_get_mpath(struct wiphy *wiphy, struct net_device *dev,
+ u8 *dst, u8 *next_hop, struct mpath_info *pinfo)
+{
+ struct ecrnx_vif *ecrnx_vif = netdev_priv(dev);
+ struct ecrnx_mesh_path *mesh_path = NULL;
+ struct ecrnx_mesh_path *cur;
+
+ if (ECRNX_VIF_TYPE(ecrnx_vif) != NL80211_IFTYPE_MESH_POINT)
+ return -ENOTSUPP;
+
+ list_for_each_entry(cur, &ecrnx_vif->ap.mpath_list, list) {
+ /* Compare the path target address and the provided destination address */
+ if (memcmp(dst, &cur->tgt_mac_addr, ETH_ALEN)) {
+ continue;
+ }
+
+ mesh_path = cur;
+ break;
+ }
+
+ if (mesh_path == NULL)
+ return -ENOENT;
+
+ /* Copy next HOP MAC address */
+ if (mesh_path->nhop_sta)
+ memcpy(next_hop, &mesh_path->nhop_sta->mac_addr, ETH_ALEN);
+
+ /* Fill path information */
+ pinfo->filled = 0;
+ pinfo->generation = ecrnx_vif->generation;
+
+ return 0;
+}
+
+/**
+ * @dump_mpath: dump mesh path callback -- resume dump at index @idx
+ */
+static int ecrnx_cfg80211_dump_mpath(struct wiphy *wiphy, struct net_device *dev,
+ int idx, u8 *dst, u8 *next_hop,
+ struct mpath_info *pinfo)
+{
+ struct ecrnx_vif *ecrnx_vif = netdev_priv(dev);
+ struct ecrnx_mesh_path *mesh_path = NULL;
+ struct ecrnx_mesh_path *cur;
+ int i = 0;
+
+ if (ECRNX_VIF_TYPE(ecrnx_vif) != NL80211_IFTYPE_MESH_POINT)
+ return -ENOTSUPP;
+
+ list_for_each_entry(cur, &ecrnx_vif->ap.mpath_list, list) {
+ if (i < idx) {
+ i++;
+ continue;
+ }
+
+ mesh_path = cur;
+ break;
+ }
+
+ if (mesh_path == NULL)
+ return -ENOENT;
+
+ /* Copy target and next hop MAC address */
+ memcpy(dst, &mesh_path->tgt_mac_addr, ETH_ALEN);
+ if (mesh_path->nhop_sta)
+ memcpy(next_hop, &mesh_path->nhop_sta->mac_addr, ETH_ALEN);
+
+ /* Fill path information */
+ pinfo->filled = 0;
+ pinfo->generation = ecrnx_vif->generation;
+
+ return 0;
+}
+
+/**
+ * @get_mpp: get a mesh proxy path for the given parameters
+ */
+static int ecrnx_cfg80211_get_mpp(struct wiphy *wiphy, struct net_device *dev,
+ u8 *dst, u8 *mpp, struct mpath_info *pinfo)
+{
+ struct ecrnx_vif *ecrnx_vif = netdev_priv(dev);
+ struct ecrnx_mesh_proxy *mesh_proxy = NULL;
+ struct ecrnx_mesh_proxy *cur;
+
+ if (ECRNX_VIF_TYPE(ecrnx_vif) != NL80211_IFTYPE_MESH_POINT)
+ return -ENOTSUPP;
+
+ list_for_each_entry(cur, &ecrnx_vif->ap.proxy_list, list) {
+ if (cur->local) {
+ continue;
+ }
+
+ /* Compare the path target address and the provided destination address */
+ if (memcmp(dst, &cur->ext_sta_addr, ETH_ALEN)) {
+ continue;
+ }
+
+ mesh_proxy = cur;
+ break;
+ }
+
+ if (mesh_proxy == NULL)
+ return -ENOENT;
+
+ memcpy(mpp, &mesh_proxy->proxy_addr, ETH_ALEN);
+
+ /* Fill path information */
+ pinfo->filled = 0;
+ pinfo->generation = ecrnx_vif->generation;
+
+ return 0;
+}
+
+/**
+ * @dump_mpp: dump mesh proxy path callback -- resume dump at index @idx
+ */
+static int ecrnx_cfg80211_dump_mpp(struct wiphy *wiphy, struct net_device *dev,
+ int idx, u8 *dst, u8 *mpp, struct mpath_info *pinfo)
+{
+ struct ecrnx_vif *ecrnx_vif = netdev_priv(dev);
+ struct ecrnx_mesh_proxy *mesh_proxy = NULL;
+ struct ecrnx_mesh_proxy *cur;
+ int i = 0;
+
+ if (ECRNX_VIF_TYPE(ecrnx_vif) != NL80211_IFTYPE_MESH_POINT)
+ return -ENOTSUPP;
+
+ list_for_each_entry(cur, &ecrnx_vif->ap.proxy_list, list) {
+ if (cur->local) {
+ continue;
+ }
+
+ if (i < idx) {
+ i++;
+ continue;
+ }
+
+ mesh_proxy = cur;
+ break;
+ }
+
+ if (mesh_proxy == NULL)
+ return -ENOENT;
+
+ /* Copy target MAC address */
+ memcpy(dst, &mesh_proxy->ext_sta_addr, ETH_ALEN);
+ memcpy(mpp, &mesh_proxy->proxy_addr, ETH_ALEN);
+
+ /* Fill path information */
+ pinfo->filled = 0;
+ pinfo->generation = ecrnx_vif->generation;
+
+ return 0;
+}
+
+/**
+ * @get_mesh_config: Get the current mesh configuration
+ */
+static int ecrnx_cfg80211_get_mesh_config(struct wiphy *wiphy, struct net_device *dev,
+ struct mesh_config *conf)
+{
+ struct ecrnx_vif *ecrnx_vif = netdev_priv(dev);
+
+ if (ECRNX_VIF_TYPE(ecrnx_vif) != NL80211_IFTYPE_MESH_POINT)
+ return -ENOTSUPP;
+
+ return 0;
+}
+
+/**
+ * @update_mesh_config: Update mesh parameters on a running mesh.
+ */
+static int ecrnx_cfg80211_update_mesh_config(struct wiphy *wiphy, struct net_device *dev,
+ u32 mask, const struct mesh_config *nconf)
+{
+ struct ecrnx_vif *ecrnx_vif = netdev_priv(dev);
+ struct ecrnx_hw *ecrnx_hw = wiphy_priv(wiphy);
+ struct mesh_update_cfm cfm;
+ int status;
+
+ if (ECRNX_VIF_TYPE(ecrnx_vif) != NL80211_IFTYPE_MESH_POINT)
+ return -ENOTSUPP;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+ if (mask & CO_BIT(NL80211_MESHCONF_POWER_MODE - 1)) {
+ ecrnx_vif->ap.next_mesh_pm = nconf->power_mode;
+
+ if (!list_empty(&ecrnx_vif->ap.sta_list)) {
+ // If there are mesh links we don't want to update the power mode
+ // It will be updated with ecrnx_update_mesh_power_mode() when the
+ // ps mode of a link is updated or when a new link is added/removed
+ mask &= ~BIT(NL80211_MESHCONF_POWER_MODE - 1);
+
+ if (!mask)
+ return 0;
+ }
+ }
+#endif
+
+ status = ecrnx_send_mesh_update_req(ecrnx_hw, ecrnx_vif, mask, nconf, &cfm);
+
+ if (!status && (cfm.status != 0))
+ status = -EINVAL;
+
+ return status;
+}
+
+/**
+ * @join_mesh: join the mesh network with the specified parameters
+ * (invoked with the wireless_dev mutex held)
+ */
+static int ecrnx_cfg80211_join_mesh(struct wiphy *wiphy, struct net_device *dev,
+ const struct mesh_config *conf, const struct mesh_setup *setup)
+{
+ struct ecrnx_hw *ecrnx_hw = wiphy_priv(wiphy);
+ struct ecrnx_vif *ecrnx_vif = netdev_priv(dev);
+ struct mesh_start_cfm mesh_start_cfm;
+ int error = 0;
+ u8 txq_status = 0;
+ /* STA for BC/MC traffic */
+ struct ecrnx_sta *sta;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ if (ECRNX_VIF_TYPE(ecrnx_vif) != NL80211_IFTYPE_MESH_POINT)
+ return -ENOTSUPP;
+
+ /* Forward the information to the UMAC */
+ if ((error = ecrnx_send_mesh_start_req(ecrnx_hw, ecrnx_vif, conf, setup, &mesh_start_cfm))) {
+ return error;
+ }
+
+ /* Check the status */
+ switch (mesh_start_cfm.status) {
+ case CO_OK:
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+ ecrnx_vif->ap.bcmc_index = mesh_start_cfm.bcmc_idx;
+ ecrnx_vif->ap.bcn_interval = setup->beacon_interval;
+#endif
+ ecrnx_vif->ap.flags = 0;
+ ecrnx_vif->use_4addr = true;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+ if (setup->user_mpm)
+ ecrnx_vif->ap.flags |= ECRNX_AP_USER_MESH_PM;
+#endif
+
+ sta = &ecrnx_hw->sta_table[mesh_start_cfm.bcmc_idx];
+ sta->valid = true;
+ sta->aid = 0;
+ sta->sta_idx = mesh_start_cfm.bcmc_idx;
+ sta->ch_idx = mesh_start_cfm.ch_idx;
+ sta->vif_idx = ecrnx_vif->vif_index;
+ sta->qos = true;
+ sta->acm = 0;
+ sta->ps.active = false;
+ sta->listen_interval = 5;
+ ecrnx_mu_group_sta_init(sta, NULL);
+ spin_lock_bh(&ecrnx_hw->cb_lock);
+ ecrnx_chanctx_link(ecrnx_vif, mesh_start_cfm.ch_idx,
+ (struct cfg80211_chan_def *)(&setup->chandef));
+ if (ecrnx_hw->cur_chanctx != mesh_start_cfm.ch_idx) {
+ txq_status = ECRNX_TXQ_STOP_CHAN;
+ }
+ ecrnx_txq_vif_init(ecrnx_hw, ecrnx_vif, txq_status);
+ spin_unlock_bh(&ecrnx_hw->cb_lock);
+
+ netif_tx_start_all_queues(dev);
+ netif_carrier_on(dev);
+
+ /* If the AP channel is already the active, we probably skip radar
+ activation on MM_CHANNEL_SWITCH_IND (unless another vif use this
+ ctxt). In anycase retest if radar detection must be activated
+ */
+ if (ecrnx_hw->cur_chanctx == mesh_start_cfm.ch_idx) {
+ ecrnx_radar_detection_enable_on_cur_channel(ecrnx_hw);
+ }
+ break;
+
+ case CO_BUSY:
+ error = -EINPROGRESS;
+ break;
+
+ default:
+ error = -EIO;
+ break;
+ }
+
+ /* Print information about the operation */
+ if (error) {
+ netdev_info(dev, "Failed to start MP (%d)", error);
+ } else {
+ netdev_info(dev, "MP started: ch=%d, bcmc_idx=%d",
+ ecrnx_vif->ch_index, ecrnx_vif->ap.bcmc_index);
+ }
+
+ return error;
+}
+
+/**
+ * @leave_mesh: leave the current mesh network
+ * (invoked with the wireless_dev mutex held)
+ */
+static int ecrnx_cfg80211_leave_mesh(struct wiphy *wiphy, struct net_device *dev)
+{
+ struct ecrnx_hw *ecrnx_hw = wiphy_priv(wiphy);
+ struct ecrnx_vif *ecrnx_vif = netdev_priv(dev);
+ struct mesh_stop_cfm mesh_stop_cfm;
+ int error = 0;
+
+ error = ecrnx_send_mesh_stop_req(ecrnx_hw, ecrnx_vif, &mesh_stop_cfm);
+
+ if (error == 0) {
+ /* Check the status */
+ switch (mesh_stop_cfm.status) {
+ case CO_OK:
+ spin_lock_bh(&ecrnx_hw->cb_lock);
+ ecrnx_chanctx_unlink(ecrnx_vif);
+ ecrnx_radar_cancel_cac(&ecrnx_hw->radar);
+ spin_unlock_bh(&ecrnx_hw->cb_lock);
+ /* delete BC/MC STA */
+ ecrnx_txq_vif_deinit(ecrnx_hw, ecrnx_vif);
+ ecrnx_del_bcn(&ecrnx_vif->ap.bcn);
+
+ netif_tx_stop_all_queues(dev);
+ netif_carrier_off(dev);
+
+ break;
+
+ default:
+ error = -EIO;
+ break;
+ }
+ }
+
+ if (error) {
+ netdev_info(dev, "Failed to stop MP");
+ } else {
+ netdev_info(dev, "MP Stopped");
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_ECRNX_P2P
+#if LINUX_VERSION_CODE > KERNEL_VERSION(5, 8, 0)
+static void ecrnx_cfg80211_update_mgmt_frame_registrations(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ struct mgmt_frame_regs *upd)
+{
+ struct ecrnx_vif *ecrnx_vif = netdev_priv(wdev->netdev);
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ if (ECRNX_VIF_TYPE(ecrnx_vif) == NL80211_IFTYPE_STATION)
+ {
+ ecrnx_vif->mgmt_reg_stypes = upd->interface_stypes & BIT(IEEE80211_STYPE_PROBE_REQ >> 4);
+ }
+}
+#else
+static void ecrnx_cfg80211_mgmt_frame_register(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ u16 frame_type, bool reg)
+{
+ struct ecrnx_vif *ecrnx_vif = netdev_priv(wdev->netdev);
+
+ u16 mgmt_type;
+
+ mgmt_type = (frame_type & IEEE80211_FCTL_STYPE) >> 4;
+
+ if (reg)
+ ecrnx_vif->mgmt_reg_stypes |= BIT(mgmt_type);
+ else
+ ecrnx_vif->mgmt_reg_stypes &= ~BIT(mgmt_type);
+}
+#endif
+#endif
+
+static struct cfg80211_ops ecrnx_cfg80211_ops = {
+ .add_virtual_intf = ecrnx_cfg80211_add_iface,
+ .del_virtual_intf = ecrnx_cfg80211_del_iface,
+ .change_virtual_intf = ecrnx_cfg80211_change_iface,
+ .start_p2p_device = ecrnx_cfg80211_start_p2p_device,
+ .stop_p2p_device = ecrnx_cfg80211_stop_p2p_device,
+ .scan = ecrnx_cfg80211_scan,
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0))
+ .abort_scan = ecrnx_cfg80211_abort_scan,
+#endif
+ .connect = ecrnx_cfg80211_connect,
+ .disconnect = ecrnx_cfg80211_disconnect,
+ .add_key = ecrnx_cfg80211_add_key,
+ .get_key = ecrnx_cfg80211_get_key,
+ .del_key = ecrnx_cfg80211_del_key,
+ .set_default_key = ecrnx_cfg80211_set_default_key,
+ .set_default_mgmt_key = ecrnx_cfg80211_set_default_mgmt_key,
+ .add_station = ecrnx_cfg80211_add_station,
+ .del_station = ecrnx_cfg80211_del_station,
+ .change_station = ecrnx_cfg80211_change_station,
+ .mgmt_tx = ecrnx_cfg80211_mgmt_tx,
+ .start_ap = ecrnx_cfg80211_start_ap,
+ .change_beacon = ecrnx_cfg80211_change_beacon,
+ .stop_ap = ecrnx_cfg80211_stop_ap,
+ .set_monitor_channel = ecrnx_cfg80211_set_monitor_channel,
+ .probe_client = ecrnx_cfg80211_probe_client,
+ .set_wiphy_params = ecrnx_cfg80211_set_wiphy_params,
+ .set_txq_params = ecrnx_cfg80211_set_txq_params,
+ .set_tx_power = ecrnx_cfg80211_set_tx_power,
+// .get_tx_power = ecrnx_cfg80211_get_tx_power,
+ .set_power_mgmt = ecrnx_cfg80211_set_power_mgmt,
+ .get_station = ecrnx_cfg80211_get_station,
+ .remain_on_channel = ecrnx_cfg80211_remain_on_channel,
+ .cancel_remain_on_channel = ecrnx_cfg80211_cancel_remain_on_channel,
+ .dump_survey = ecrnx_cfg80211_dump_survey,
+ .get_channel = ecrnx_cfg80211_get_channel,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+ .start_radar_detection = ecrnx_cfg80211_start_radar_detection,
+ .update_ft_ies = ecrnx_cfg80211_update_ft_ies,
+#endif
+ .set_cqm_rssi_config = ecrnx_cfg80211_set_cqm_rssi_config,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)
+ .channel_switch = ecrnx_cfg80211_channel_switch,
+#endif
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)
+ .tdls_channel_switch = ecrnx_cfg80211_tdls_channel_switch,
+ .tdls_cancel_channel_switch = ecrnx_cfg80211_tdls_cancel_channel_switch,
+#endif
+ .tdls_mgmt = ecrnx_cfg80211_tdls_mgmt,
+ .tdls_oper = ecrnx_cfg80211_tdls_oper,
+ .change_bss = ecrnx_cfg80211_change_bss,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0)
+ .external_auth = ecrnx_cfg80211_external_auth,
+#endif
+
+#ifdef CONFIG_ECRNX_P2P
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0)
+ .update_mgmt_frame_registrations =
+ ecrnx_cfg80211_update_mgmt_frame_registrations,
+#else
+ .mgmt_frame_register = ecrnx_cfg80211_mgmt_frame_register,
+#endif
+#endif
+
+};
+
+
+/*********************************************************************
+ * Init/Exit functions
+ *********************************************************************/
+static void ecrnx_wdev_unregister(struct ecrnx_hw *ecrnx_hw)
+{
+ struct ecrnx_vif *ecrnx_vif, *tmp;
+
+ rtnl_lock();
+ list_for_each_entry_safe(ecrnx_vif, tmp, &ecrnx_hw->vifs, list) {
+ ecrnx_cfg80211_del_iface(ecrnx_hw->wiphy, &ecrnx_vif->wdev);
+ }
+ rtnl_unlock();
+}
+
+static void ecrnx_set_vers(struct ecrnx_hw *ecrnx_hw)
+{
+ u32 vers = ecrnx_hw->version_cfm.version_lmac;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ snprintf(ecrnx_hw->wiphy->fw_version,
+ sizeof(ecrnx_hw->wiphy->fw_version), "%d.%d.%d.%d",
+ (vers & (0xff << 24)) >> 24, (vers & (0xff << 16)) >> 16,
+ (vers & (0xff << 8)) >> 8, (vers & (0xff << 0)) >> 0);
+ ecrnx_hw->machw_type = ecrnx_machw_type(ecrnx_hw->version_cfm.version_machw_2);
+}
+
+static void ecrnx_reg_notifier(struct wiphy *wiphy,
+ struct regulatory_request *request)
+{
+ struct ecrnx_hw *ecrnx_hw = wiphy_priv(wiphy);
+
+ // For now trust all initiator
+ ecrnx_radar_set_domain(&ecrnx_hw->radar, request->dfs_region);
+ ecrnx_send_me_chan_config_req(ecrnx_hw);
+}
+
+static void ecrnx_enable_mesh(struct ecrnx_hw *ecrnx_hw)
+{
+ struct wiphy *wiphy = ecrnx_hw->wiphy;
+
+ if (!ecrnx_mod_params.mesh)
+ return;
+
+ ecrnx_cfg80211_ops.add_mpath = ecrnx_cfg80211_add_mpath;
+ ecrnx_cfg80211_ops.del_mpath = ecrnx_cfg80211_del_mpath;
+ ecrnx_cfg80211_ops.change_mpath = ecrnx_cfg80211_change_mpath;
+ ecrnx_cfg80211_ops.get_mpath = ecrnx_cfg80211_get_mpath;
+ ecrnx_cfg80211_ops.dump_mpath = ecrnx_cfg80211_dump_mpath;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)
+ ecrnx_cfg80211_ops.get_mpp = ecrnx_cfg80211_get_mpp;
+ ecrnx_cfg80211_ops.dump_mpp = ecrnx_cfg80211_dump_mpp;
+#endif
+ ecrnx_cfg80211_ops.get_mesh_config = ecrnx_cfg80211_get_mesh_config;
+ ecrnx_cfg80211_ops.update_mesh_config = ecrnx_cfg80211_update_mesh_config;
+ ecrnx_cfg80211_ops.join_mesh = ecrnx_cfg80211_join_mesh;
+ ecrnx_cfg80211_ops.leave_mesh = ecrnx_cfg80211_leave_mesh;
+
+ wiphy->flags |= (WIPHY_FLAG_MESH_AUTH | WIPHY_FLAG_IBSS_RSN);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+ wiphy->features |= NL80211_FEATURE_USERSPACE_MPM;
+#endif
+ wiphy->interface_modes |= BIT(NL80211_IFTYPE_MESH_POINT);
+
+ ecrnx_limits[0].types |= BIT(NL80211_IFTYPE_MESH_POINT);
+ ecrnx_limits_dfs[0].types |= BIT(NL80211_IFTYPE_MESH_POINT);
+}
+
+int ecrnx_get_cal_result(struct ecrnx_hw *ecrnx_hw)
+{
+ int ret;
+ wifi_cal_data_t *result = &cal_result;
+
+ ret = ecrnx_send_cal_result_get_req(ecrnx_hw, result);
+
+ return ret;
+}
+
+void ecrnx_he_init(void)
+{
+ ecrnx_he_cap.has_he = true;
+ memset(&ecrnx_he_cap.he_cap_elem, 0, sizeof(struct ieee80211_he_cap_elem));
+
+ ecrnx_he_cap.he_mcs_nss_supp.rx_mcs_80 = cpu_to_le16(0xfffa);
+ ecrnx_he_cap.he_mcs_nss_supp.tx_mcs_80 = cpu_to_le16(0xfffa);
+ ecrnx_he_cap.he_mcs_nss_supp.rx_mcs_160 = cpu_to_le16(0xffff);
+ ecrnx_he_cap.he_mcs_nss_supp.tx_mcs_160 = cpu_to_le16(0xffff);
+ ecrnx_he_cap.he_mcs_nss_supp.rx_mcs_80p80 = cpu_to_le16(0xffff);
+ ecrnx_he_cap.he_mcs_nss_supp.tx_mcs_80p80 = cpu_to_le16(0xffff);
+ memset(ecrnx_he_cap.ppe_thres, 0, sizeof(u8)*IEEE80211_HE_PPE_THRES_MAX_LEN);
+}
+
+/**
+ *
+ */
+bool register_drv_done = false;
+int ecrnx_cfg80211_init(void *ecrnx_plat, void **platform_data)
+{
+ struct ecrnx_hw *ecrnx_hw;
+ int ret = 0;
+ struct wiphy *wiphy;
+ struct wireless_dev *wdev;
+ int i;
+
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+ /* create a new wiphy for use with cfg80211 */
+ wiphy = wiphy_new(&ecrnx_cfg80211_ops, sizeof(struct ecrnx_hw));
+
+ if (!wiphy) {
+ dev_err(ecrnx_platform_get_dev(ecrnx_plat), "Failed to create new wiphy\n");
+ ret = -ENOMEM;
+ goto err_out;
+ }
+
+ ecrnx_hw = wiphy_priv(wiphy);
+ ecrnx_hw->wiphy = wiphy;
+ ecrnx_hw->plat = ecrnx_plat;
+ ecrnx_hw->dev = ecrnx_platform_get_dev(ecrnx_plat);
+ ecrnx_hw->mod_params = &ecrnx_mod_params;
+ ecrnx_hw->tcp_pacing_shift = 7;
+ *platform_data = ecrnx_hw;
+
+ /* set device pointer for wiphy */
+ set_wiphy_dev(wiphy, ecrnx_hw->dev);
+ /* Create cache to allocate sw_txhdr */
+ ecrnx_hw->sw_txhdr_cache = KMEM_CACHE(ecrnx_sw_txhdr, 0);
+ if (!ecrnx_hw->sw_txhdr_cache) {
+ wiphy_err(wiphy, "Cannot allocate cache for sw TX header\n");
+ ret = -ENOMEM;
+ goto err_cache;
+ }
+
+ if ((ret = ecrnx_parse_configfile(ecrnx_hw, ECRNX_CONFIG_FW_NAME))) {
+ wiphy_err(wiphy, "ecrnx_parse_configfile failed\n");
+ goto err_config;
+ }
+
+ ecrnx_hw->vif_started = 0;
+ ecrnx_hw->monitor_vif = ECRNX_INVALID_VIF;
+
+ ecrnx_hw->scan_ie.addr = NULL;
+
+ for (i = 0; i < NX_VIRT_DEV_MAX + NX_REMOTE_STA_MAX; i++)
+ ecrnx_hw->avail_idx_map |= BIT(i);
+
+ ecrnx_hwq_init(ecrnx_hw);
+ ecrnx_txq_prepare(ecrnx_hw);
+
+ ecrnx_mu_group_init(ecrnx_hw);
+
+ /* Initialize RoC element pointer to NULL, indicate that RoC can be started */
+ ecrnx_hw->roc = NULL;
+ /* Cookie can not be 0 */
+ ecrnx_hw->roc_cookie = 1;
+
+ wiphy->mgmt_stypes = ecrnx_default_mgmt_stypes;
+
+ wiphy->bands[NL80211_BAND_2GHZ] = &ecrnx_band_2GHz;
+#ifdef CONFIG_ECRNX_5G
+ wiphy->bands[NL80211_BAND_5GHZ] = &ecrnx_band_5GHz;
+#endif
+ wiphy->interface_modes =
+ BIT(NL80211_IFTYPE_STATION) |
+ BIT(NL80211_IFTYPE_AP) |
+ BIT(NL80211_IFTYPE_AP_VLAN) |
+ BIT(NL80211_IFTYPE_P2P_CLIENT) |
+ BIT(NL80211_IFTYPE_P2P_GO) |
+ BIT(NL80211_IFTYPE_MONITOR);
+ wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL |
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0))
+ WIPHY_FLAG_HAS_CHANNEL_SWITCH |
+#endif
+ WIPHY_FLAG_4ADDR_STATION |
+ WIPHY_FLAG_4ADDR_AP;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)
+ wiphy->max_num_csa_counters = BCN_MAX_CSA_CPT;
+#endif
+
+ wiphy->max_remain_on_channel_duration = ecrnx_hw->mod_params->roc_dur_max;
+
+#if 0 /* eswin:rm the feature of OBSS_SCAN which can cause the uplink stream shutdown */
+ wiphy->features |= NL80211_FEATURE_NEED_OBSS_SCAN |
+ NL80211_FEATURE_SK_TX_STATUS |
+#else
+ wiphy->features |= NL80211_FEATURE_SK_TX_STATUS |
+#endif
+ NL80211_FEATURE_VIF_TXPOWER |
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)
+ NL80211_FEATURE_ACTIVE_MONITOR |
+#endif
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)
+ NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE |
+#endif
+ 0;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0)
+ wiphy->features |= NL80211_FEATURE_SAE;
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 20, 0)
+ ecrnx_he_init();
+#endif
+
+ if (ecrnx_mod_params.tdls)
+ /* TDLS support */
+ wiphy->features |= NL80211_FEATURE_TDLS_CHANNEL_SWITCH;
+
+ wiphy->iface_combinations = ecrnx_combinations;
+ /* -1 not to include combination with radar detection, will be re-added in
+ ecrnx_handle_dynparams if supported */
+ wiphy->n_iface_combinations = ARRAY_SIZE(ecrnx_combinations) - 1;
+ wiphy->reg_notifier = ecrnx_reg_notifier;
+
+ wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
+
+ wiphy->cipher_suites = cipher_suites;
+ wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites) - NB_RESERVED_CIPHER;
+
+ ecrnx_hw->ext_capa[0] = WLAN_EXT_CAPA1_EXT_CHANNEL_SWITCHING;
+ ecrnx_hw->ext_capa[2] = WLAN_EXT_CAPA3_MULTI_BSSID_SUPPORT;
+ ecrnx_hw->ext_capa[4] = WLAN_EXT_CAPA5_QOS_MAP_SUPPORT;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+ ecrnx_hw->ext_capa[7] = WLAN_EXT_CAPA8_OPMODE_NOTIF;
+ wiphy->extended_capabilities = ecrnx_hw->ext_capa;
+ wiphy->extended_capabilities_mask = ecrnx_hw->ext_capa;
+ wiphy->extended_capabilities_len = ARRAY_SIZE(ecrnx_hw->ext_capa);
+#endif
+
+#ifndef CONFIG_ECRNX_ESWIN
+ tasklet_init(&ecrnx_hw->task, ecrnx_task, (unsigned long)ecrnx_hw);
+#endif
+
+ INIT_LIST_HEAD(&ecrnx_hw->vifs);
+#ifdef CONFIG_ECRNX_ESWIN
+ INIT_LIST_HEAD(&ecrnx_hw->agg_rx_list);
+ INIT_LIST_HEAD(&ecrnx_hw->defrag_rx_list);
+#endif
+
+ mutex_init(&ecrnx_hw->dbgdump_elem.mutex);
+ spin_lock_init(&ecrnx_hw->tx_lock);
+ spin_lock_init(&ecrnx_hw->cb_lock);
+ spin_lock_init(&ecrnx_hw->rx_lock);
+ spin_lock_init(&ecrnx_hw->scan_req_lock);
+ spin_lock_init(&ecrnx_hw->connect_req_lock);
+
+ if ((ret = ecrnx_platform_on(ecrnx_hw, NULL)))
+ goto err_platon;
+
+ if ((ret = ecrnx_get_cal_result(ecrnx_hw))) {
+ wiphy_err(wiphy, "get cal result failed\n");
+ goto err_lmac_reqs;
+ } else {
+ if ((0 == (cal_result.mac_addr[0] & 0x1)) && (cal_result.mac_addr[0] || cal_result.mac_addr[1]
+ || cal_result.mac_addr[2] || cal_result.mac_addr[3] || cal_result.mac_addr[4]
+ || cal_result.mac_addr[5])) {
+ memcpy(ecrnx_hw->conf_param.mac_addr, cal_result.mac_addr, ETH_ALEN);
+ }
+ }
+ memcpy(wiphy->perm_addr, ecrnx_hw->conf_param.mac_addr, ETH_ALEN);
+
+ /* Reset FW */
+ if ((ret = ecrnx_send_reset(ecrnx_hw)))
+ goto err_lmac_reqs;
+ if ((ret = ecrnx_send_version_req(ecrnx_hw, &ecrnx_hw->version_cfm)))
+ goto err_lmac_reqs;
+ ecrnx_set_vers(ecrnx_hw);
+
+ if ((ret = ecrnx_handle_dynparams(ecrnx_hw, ecrnx_hw->wiphy)))
+ goto err_lmac_reqs;
+
+ ecrnx_enable_mesh(ecrnx_hw);
+ ecrnx_radar_detection_init(&ecrnx_hw->radar);
+
+#ifdef CONFIG_ECRNX_P2P
+ ecrnx_p2p_listen_init(&ecrnx_hw->p2p_listen);
+#endif
+
+ /* Set parameters to firmware */
+ ecrnx_send_me_config_req(ecrnx_hw);
+
+ /* Only monitor mode supported when custom channels are enabled */
+ if (ecrnx_mod_params.custchan) {
+ ecrnx_limits[0].types = BIT(NL80211_IFTYPE_MONITOR);
+ ecrnx_limits_dfs[0].types = BIT(NL80211_IFTYPE_MONITOR);
+ }
+
+ if ((ret = wiphy_register(wiphy))) {
+ wiphy_err(wiphy, "Could not register wiphy device\n");
+ goto err_register_wiphy;
+ }
+
+ INIT_WORK(&ecrnx_hw->defer_rx.work, ecrnx_rx_deferred);
+ skb_queue_head_init(&ecrnx_hw->defer_rx.sk_list);
+ /* Update regulatory (if needed) and set channel parameters to firmware
+ (must be done after WiPHY registration) */
+ ecrnx_fw_log_level_set((u32)ecrnx_hw->conf_param.fw_log_level, (u32)ecrnx_hw->conf_param.fw_log_type);
+ ecrnx_custregd(ecrnx_hw, wiphy);
+ ecrnx_send_me_chan_config_req(ecrnx_hw);
+
+ /* config gain delta */
+ ecrnx_send_set_gain_delta_req(ecrnx_hw);
+
+#ifdef CONFIG_ECRNX_DEBUGFS
+ if ((ret = ecrnx_dbgfs_register(ecrnx_hw, "ecrnx"))) {
+ ECRNX_DBG(" ecrnx_dbgfs_register error \n");
+ wiphy_err(wiphy, "Failed to register debugfs entries");
+ goto err_debugfs;
+ }
+#endif
+
+ rtnl_lock();
+
+ /* Add an initial interface */
+ wdev = ecrnx_interface_add(ecrnx_hw, "wlan%d", NET_NAME_UNKNOWN,
+ ecrnx_mod_params.custchan ? NL80211_IFTYPE_MONITOR : NL80211_IFTYPE_STATION,
+ NULL);
+#if defined(CONFIG_ECRNX_P2P)
+ wdev = ecrnx_interface_add(ecrnx_hw, "p2p%d", NET_NAME_UNKNOWN, NL80211_IFTYPE_STATION,
+ NULL);
+#endif
+ rtnl_unlock();
+
+ if (!wdev) {
+ wiphy_err(wiphy, "Failed to instantiate a network device\n");
+ ret = -ENOMEM;
+ goto err_add_interface;
+ }
+
+ wiphy_info(wiphy, "New interface create %s", wdev->netdev->name);
+
+#if defined(CONFIG_ECRNX_DEBUGFS_CUSTOM)
+ ecrnx_debugfs_init(ecrnx_hw);
+#endif
+ register_drv_done = true;
+ return 0;
+
+err_add_interface:
+#ifdef CONFIG_ECRNX_DEBUGFS
+err_debugfs:
+#endif
+ wiphy_unregister(ecrnx_hw->wiphy);
+err_register_wiphy:
+err_lmac_reqs:
+ ecrnx_fw_trace_dump(ecrnx_hw);
+ ecrnx_platform_off(ecrnx_hw, NULL);
+err_platon:
+err_config:
+ kmem_cache_destroy(ecrnx_hw->sw_txhdr_cache);
+err_cache:
+ wiphy_free(wiphy);
+err_out:
+ ECRNX_DBG(" %s cfg80211 init failed %d!!", __func__, ret);
+ return ret;
+}
+
+/**
+ *
+ */
+void ecrnx_cfg80211_deinit(struct ecrnx_hw *ecrnx_hw)
+{
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+ if(!register_drv_done)
+ {
+ return;
+ }
+#if 0
+ ecrnx_dbgfs_unregister(ecrnx_hw);
+#endif
+ register_drv_done = false;
+
+ del_timer_sync(&ecrnx_hw->txq_cleanup);
+ ecrnx_wdev_unregister(ecrnx_hw);
+ if(ecrnx_hw->wiphy)
+ {
+ ECRNX_DBG("%s wiphy_unregister \n", __func__);
+ wiphy_unregister(ecrnx_hw->wiphy);
+ wiphy_free(ecrnx_hw->wiphy);
+ ecrnx_hw->wiphy = NULL;
+ }
+ ecrnx_radar_detection_deinit(&ecrnx_hw->radar);
+ ecrnx_platform_off(ecrnx_hw, NULL);
+ kmem_cache_destroy(ecrnx_hw->sw_txhdr_cache);
+}
+
+/**
+ *
+ */
+static int __init ecrnx_mod_init(void)
+{
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+ ecrnx_print_version();
+ return ecrnx_platform_register_drv();
+}
+
+/**
+ *
+ */
+static void __exit ecrnx_mod_exit(void)
+{
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+#if defined(CONFIG_ECRNX_DEBUGFS_CUSTOM)
+ ecrnx_debugfs_exit();
+#endif
+
+ ecrnx_platform_unregister_drv();
+}
+
+module_init(ecrnx_mod_init);
+module_exit(ecrnx_mod_exit);
+
+MODULE_FIRMWARE(ECRNX_CONFIG_FW_NAME);
+
+MODULE_DESCRIPTION(RW_DRV_DESCRIPTION);
+MODULE_VERSION(ECRNX_VERS_MOD);
+MODULE_AUTHOR(RW_DRV_COPYRIGHT " " RW_DRV_AUTHOR);
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/wireless/eswin/fullmac/ecrnx_main.h b/drivers/net/wireless/eswin/fullmac/ecrnx_main.h
new file mode 100644
index 000000000000..b1c81e94afb1
--- /dev/null
+++ b/drivers/net/wireless/eswin/fullmac/ecrnx_main.h
@@ -0,0 +1,19 @@
+/**
+ ******************************************************************************
+ *
+ * @file ecrnx_main.h
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+
+#ifndef _ECRNX_MAIN_H_
+#define _ECRNX_MAIN_H_
+
+#include "ecrnx_defs.h"
+
+int ecrnx_cfg80211_init(struct ecrnx_plat *ecrnx_plat, void **platform_data);
+void ecrnx_cfg80211_deinit(struct ecrnx_hw *ecrnx_hw);
+
+#endif /* _ECRNX_MAIN_H_ */
diff --git a/drivers/net/wireless/eswin/fullmac/ecrnx_mesh.c b/drivers/net/wireless/eswin/fullmac/ecrnx_mesh.c
new file mode 100644
index 000000000000..c027d6712993
--- /dev/null
+++ b/drivers/net/wireless/eswin/fullmac/ecrnx_mesh.c
@@ -0,0 +1,42 @@
+/**
+ ****************************************************************************************
+ *
+ * @file ecrnx_mesh.c
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ****************************************************************************************
+ */
+
+/**
+ * INCLUDE FILES
+ ****************************************************************************************
+ */
+
+#include "ecrnx_mesh.h"
+
+/**
+ * FUNCTION DEFINITIONS
+ ****************************************************************************************
+ */
+
+struct ecrnx_mesh_proxy *ecrnx_get_mesh_proxy_info(struct ecrnx_vif *p_ecrnx_vif, u8 *p_sta_addr, bool local)
+{
+ struct ecrnx_mesh_proxy *p_mesh_proxy = NULL;
+ struct ecrnx_mesh_proxy *p_cur_proxy;
+
+ /* Look for proxied devices with provided address */
+ list_for_each_entry(p_cur_proxy, &p_ecrnx_vif->ap.proxy_list, list) {
+ if (p_cur_proxy->local != local) {
+ continue;
+ }
+
+ if (!memcmp(&p_cur_proxy->ext_sta_addr, p_sta_addr, ETH_ALEN)) {
+ p_mesh_proxy = p_cur_proxy;
+ break;
+ }
+ }
+
+ /* Return the found information */
+ return p_mesh_proxy;
+}
diff --git a/drivers/net/wireless/eswin/fullmac/ecrnx_mesh.h b/drivers/net/wireless/eswin/fullmac/ecrnx_mesh.h
new file mode 100644
index 000000000000..e27ec9ba8d8d
--- /dev/null
+++ b/drivers/net/wireless/eswin/fullmac/ecrnx_mesh.h
@@ -0,0 +1,45 @@
+/**
+ ****************************************************************************************
+ *
+ * @file ecrnx_mesh.h
+ *
+ * @brief VHT Beamformer function declarations
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ****************************************************************************************
+ */
+
+#ifndef _ECRNX_MESH_H_
+#define _ECRNX_MESH_H_
+
+/**
+ * INCLUDE FILES
+ ****************************************************************************************
+ */
+
+#include "ecrnx_defs.h"
+
+/**
+ * DEFINES
+ ****************************************************************************************
+ */
+
+/**
+ * TYPE DEFINITIONS
+ ****************************************************************************************
+ */
+
+/**
+ * FUNCTION DECLARATIONS
+ ****************************************************************************************
+ */
+
+/**
+ ****************************************************************************************
+ * @brief TODO [LT]
+ ****************************************************************************************
+ */
+struct ecrnx_mesh_proxy *ecrnx_get_mesh_proxy_info(struct ecrnx_vif *p_ecrnx_vif, u8 *p_sta_addr, bool local);
+
+#endif /* _ECRNX_MESH_H_ */
diff --git a/drivers/net/wireless/eswin/fullmac/ecrnx_p2p.c b/drivers/net/wireless/eswin/fullmac/ecrnx_p2p.c
new file mode 100644
index 000000000000..bab577c121b2
--- /dev/null
+++ b/drivers/net/wireless/eswin/fullmac/ecrnx_p2p.c
@@ -0,0 +1,72 @@
+/**
+ ****************************************************************************************
+ *
+ * @file rwnx_p2p.c
+ *
+ *
+ *
+ ****************************************************************************************
+ */
+
+/**
+ * INCLUDE FILES
+ ****************************************************************************************
+ */
+
+#include "ecrnx_p2p.h"
+#include "ecrnx_msg_tx.h"
+
+
+/**
+ * FUNCTION DEFINITIONS
+ ****************************************************************************************
+ */
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0)
+static void ecrnx_p2p_discovery_timer_fn(struct timer_list *t)
+#else
+static void ecrnx_p2p_discovery_timer_fn(ulong x)
+#endif
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0)
+ struct ecrnx_hw *ecrnx_hw = from_timer(ecrnx_hw, t, p2p_listen.listen_timer);
+#else
+ struct ecrnx_hw *ecrnx_hw = (void *)x;
+#endif
+ ECRNX_PRINT("ecrnx_p2p_discovery_timer_fn\n");
+
+ schedule_work(&ecrnx_hw->p2p_listen.listen_expired_work);
+
+}
+
+void ecrnx_p2p_listen_init(struct ecrnx_p2p_listen *p2p_listen)
+{
+ struct ecrnx_hw *ecrnx_hw = container_of(p2p_listen, struct ecrnx_hw, p2p_listen);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0)
+ timer_setup(&p2p_listen->listen_timer, ecrnx_p2p_discovery_timer_fn, 0);
+#else
+ setup_timer(&p2p_listen->listen_timer, ecrnx_p2p_discovery_timer_fn, (ulong)ecrnx_hw);
+#endif
+ INIT_WORK(&p2p_listen->listen_expired_work, ecrnx_p2p_listen_expired);
+
+ init_waitqueue_head(&p2p_listen->rxdataq);
+}
+
+void ecrnx_p2p_listen_expired(struct work_struct *work)
+{
+ struct ecrnx_p2p_listen *p2p_listen = container_of(work, struct ecrnx_p2p_listen, listen_expired_work);
+ struct ecrnx_hw *ecrnx_hw = container_of(p2p_listen, struct ecrnx_hw, p2p_listen);
+
+ ECRNX_PRINT("p2p_listen_expired\n");
+
+ if (p2p_listen->listen_started)
+ {
+ del_timer_sync(&p2p_listen->listen_timer);
+ ecrnx_send_p2p_cancel_listen_req(ecrnx_hw, p2p_listen->ecrnx_vif);
+ }
+ else
+ {
+ return;
+ }
+}
+
diff --git a/drivers/net/wireless/eswin/fullmac/ecrnx_p2p.h b/drivers/net/wireless/eswin/fullmac/ecrnx_p2p.h
new file mode 100644
index 000000000000..390b95002855
--- /dev/null
+++ b/drivers/net/wireless/eswin/fullmac/ecrnx_p2p.h
@@ -0,0 +1,60 @@
+/**
+ ****************************************************************************************
+ *
+ * @file rwnx_p2p.h
+ *
+ * @brief P2P Listen function declarations
+ *
+ *
+ *
+ ****************************************************************************************
+ */
+
+#ifndef _ECRNX_P2P_H_
+#define _ECRNX_P2P_H_
+
+/**
+ * INCLUDE FILES
+ ****************************************************************************************
+ */
+#include <net/cfg80211.h>
+
+/**
+ * DEFINES
+ ****************************************************************************************
+ */
+
+/**
+ * TYPE DEFINITIONS
+ ****************************************************************************************
+ */
+struct ecrnx_p2p_listen {
+ struct ecrnx_vif *ecrnx_vif;
+ struct ieee80211_channel listen_chan;
+ bool listen_started;
+ bool pending_req;
+ u64 cookie;
+ struct wireless_dev *pending_listen_wdev;
+ unsigned int listen_duration;
+ struct timer_list listen_timer; /* listen duration */
+ struct work_struct listen_expired_work; /* listen expire */
+
+ wait_queue_head_t rxdataq;
+ int rxdatas;
+};
+
+/**
+ * FUNCTION DECLARATIONS
+ ****************************************************************************************
+ */
+void ecrnx_p2p_listen_init(struct ecrnx_p2p_listen *p2p_listen);
+void ecrnx_p2p_listen_expired(struct work_struct *work);
+
+/**
+ ****************************************************************************************
+ * @brief TODO [LT]
+ ****************************************************************************************
+ */
+
+
+#endif /* _RWNX_P2P_H_ */
diff --git a/drivers/net/wireless/eswin/fullmac/ecrnx_rx.c b/drivers/net/wireless/eswin/fullmac/ecrnx_rx.c
new file mode 100644
index 000000000000..b372fd72b53e
--- /dev/null
+++ b/drivers/net/wireless/eswin/fullmac/ecrnx_rx.c
@@ -0,0 +1,2541 @@
+/**
+ ******************************************************************************
+ *
+ * @file ecrnx_rx.c
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+#include <linux/dma-mapping.h>
+#include <linux/ieee80211.h>
+#include <linux/etherdevice.h>
+#include <net/ieee80211_radiotap.h>
+
+#include "ecrnx_defs.h"
+#include "ecrnx_rx.h"
+#include "ecrnx_tx.h"
+#include "ecrnx_prof.h"
+#include "ecrnx_events.h"
+#include "ecrnx_compat.h"
+#include "ipc_host.h"
+#if defined(CONFIG_ECRNX_ESWIN_SDIO)
+#include "ecrnx_sdio.h"
+#include "sdio.h"
+#elif defined(CONFIG_ECRNX_ESWIN_USB)
+#include "ecrnx_usb.h"
+#include "usb.h"
+#endif
+#ifdef CONFIG_ECRNX_WIFO_CAIL
+#include "core.h"
+#endif
+
+#ifndef IEEE80211_MAX_CHAINS
+#define IEEE80211_MAX_CHAINS 4
+#endif
+
+struct vendor_radiotap_hdr {
+ u8 oui[3];
+ u8 subns;
+ u16 len;
+ u8 data[];
+};
+
+/**
+ * ecrnx_rx_get_vif - Return pointer to the destination vif
+ *
+ * @ecrnx_hw: main driver data
+ * @vif_idx: vif index present in rx descriptor
+ *
+ * Select the vif that should receive this frame. Returns NULL if the destination
+ * vif is not active or vif is not specified in the descriptor.
+ */
+static inline
+struct ecrnx_vif *ecrnx_rx_get_vif(struct ecrnx_hw *ecrnx_hw, int vif_idx)
+{
+ struct ecrnx_vif *ecrnx_vif = NULL;
+
+ if (vif_idx < NX_VIRT_DEV_MAX) {
+ ecrnx_vif = ecrnx_hw->vif_table[vif_idx];
+ //ECRNX_DBG("%s, index : %d, up: %d \n", __func__, vif_idx, ecrnx_vif->up);
+ if (!ecrnx_vif || !ecrnx_vif->up)
+ return NULL;
+ }
+
+ return ecrnx_vif;
+}
+
+/**
+ * ecrnx_rx_statistic - save some statistics about received frames
+ *
+ * @ecrnx_hw: main driver data.
+ * @hw_rxhdr: Rx Hardware descriptor of the received frame.
+ * @sta: STA that sent the frame.
+ */
+static void ecrnx_rx_statistic(struct ecrnx_hw *ecrnx_hw, struct hw_rxhdr *hw_rxhdr,
+ struct ecrnx_sta *sta)
+{
+ struct ecrnx_stats *stats = &ecrnx_hw->stats;
+#ifdef CONFIG_ECRNX_DEBUGFS
+ struct ecrnx_rx_rate_stats *rate_stats = &sta->stats.rx_rate;
+ struct rx_vector_1 *rxvect = &hw_rxhdr->hwvect.rx_vect1;
+ int mpdu, ampdu, mpdu_prev, rate_idx;
+
+ /* save complete hwvect */
+
+ /* update ampdu rx stats */
+ mpdu = hw_rxhdr->hwvect.mpdu_cnt;
+ ampdu = hw_rxhdr->hwvect.ampdu_cnt;
+ mpdu_prev = stats->ampdus_rx_map[ampdu];
+
+ if (mpdu == 63) {
+ if (ampdu == stats->ampdus_rx_last)
+ mpdu = mpdu_prev + 1;
+ else
+ mpdu = 0;
+ }
+ if (ampdu != stats->ampdus_rx_last) {
+ stats->ampdus_rx[mpdu_prev]++;
+ stats->ampdus_rx_miss += mpdu;
+ } else {
+ if (mpdu <= mpdu_prev) {
+ stats->ampdus_rx_miss += mpdu;
+ } else {
+ stats->ampdus_rx_miss += mpdu - mpdu_prev - 1;
+ }
+ }
+ stats->ampdus_rx_map[ampdu] = mpdu;
+ stats->ampdus_rx_last = ampdu;
+
+ /* update rx rate statistic */
+ if (!rate_stats->size)
+ return;
+
+ if (rxvect->format_mod > FORMATMOD_NON_HT_DUP_OFDM) {
+ int mcs;
+ int bw = rxvect->ch_bw;
+ int sgi;
+ int nss;
+ switch (rxvect->format_mod) {
+ case FORMATMOD_HT_MF:
+ case FORMATMOD_HT_GF:
+ mcs = rxvect->ht.mcs % 8;
+ nss = rxvect->ht.mcs / 8;
+ sgi = rxvect->ht.short_gi;
+ rate_idx = N_CCK + N_OFDM + nss * 32 + mcs * 4 + bw * 2 + sgi;
+ break;
+ case FORMATMOD_VHT:
+ mcs = rxvect->vht.mcs;
+ nss = rxvect->vht.nss;
+ sgi = rxvect->vht.short_gi;
+ rate_idx = N_CCK + N_OFDM + N_HT + nss * 80 + mcs * 8 + bw * 2 + sgi;
+ break;
+ case FORMATMOD_HE_SU:
+ mcs = rxvect->he.mcs;
+ nss = rxvect->he.nss;
+ sgi = rxvect->he.gi_type;
+ rate_idx = N_CCK + N_OFDM + N_HT + N_VHT + nss * 144 + mcs * 12 + bw * 3 + sgi;
+ break;
+ default:
+ mcs = rxvect->he.mcs;
+ nss = rxvect->he.nss;
+ sgi = rxvect->he.gi_type;
+ rate_idx = N_CCK + N_OFDM + N_HT + N_VHT + N_HE_SU
+ + nss * 216 + mcs * 18 + rxvect->he.ru_size * 3 + sgi;
+ break;
+ }
+ } else {
+ int idx = legrates_lut[rxvect->leg_rate].idx;
+ if (idx < 4) {
+ rate_idx = idx * 2 + rxvect->pre_type;
+ } else {
+ rate_idx = N_CCK + idx - 4;
+ }
+ }
+ if (rate_idx < rate_stats->size) {
+ if (!rate_stats->table[rate_idx])
+ rate_stats->rate_cnt++;
+ rate_stats->table[rate_idx]++;
+ rate_stats->cpt++;
+ } else {
+ wiphy_err(ecrnx_hw->wiphy, "RX: Invalid index conversion => %d/%d\n",
+ rate_idx, rate_stats->size);
+ }
+#endif
+ sta->stats.last_rx = hw_rxhdr->hwvect;
+ sta->stats.rx_pkts ++;
+ sta->stats.rx_bytes += hw_rxhdr->hwvect.len;
+ sta->stats.last_act = stats->last_rx;
+}
+void ecrnx_rx_defer_skb(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *ecrnx_vif,
+ struct sk_buff *skb)
+{
+ struct ecrnx_defer_rx_cb *rx_cb = (struct ecrnx_defer_rx_cb *)skb->cb;
+ if (skb_shared(skb))
+ return;
+ skb_get(skb);
+ rx_cb->vif = ecrnx_vif;
+ skb_queue_tail(&ecrnx_hw->defer_rx.sk_list, skb);
+ schedule_work(&ecrnx_hw->defer_rx.work);
+}
+
+/**
+ * ecrnx_rx_data_skb - Process one data frame
+ *
+ * @ecrnx_hw: main driver data
+ * @ecrnx_vif: vif that received the buffer
+ * @skb: skb received
+ * @rxhdr: HW rx descriptor
+ * @return: true if buffer has been forwarded to upper layer
+ *
+ * If buffer is amsdu , it is first split into a list of skb.
+ * Then each skb may be:
+ * - forwarded to upper layer
+ * - resent on wireless interface
+ *
+ * When vif is a STA interface, every skb is only forwarded to upper layer.
+ * When vif is an AP interface, multicast skb are forwarded and resent, whereas
+ * skb for other BSS's STA are only resent.
+ */
+static bool ecrnx_rx_data_skb(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *ecrnx_vif,
+ struct sk_buff *skb, struct hw_rxhdr *rxhdr)
+{
+ struct sk_buff_head list;
+ struct sk_buff *rx_skb;
+ bool amsdu = rxhdr->flags_is_amsdu;
+ bool resend = false, forward = true;
+ int skip_after_eth_hdr = 0;
+
+ skb->dev = ecrnx_vif->ndev;
+
+ __skb_queue_head_init(&list);
+
+ if (amsdu) {
+ int count;
+ ieee80211_amsdu_to_8023s(skb, &list, ecrnx_vif->ndev->dev_addr,
+ ECRNX_VIF_TYPE(ecrnx_vif), 0, NULL, NULL, false);
+
+ count = skb_queue_len(&list);
+ if (count == 0)
+ {
+ ECRNX_PRINT("amsdu decode fail!!\n");
+ //return false;
+ print_hex_dump(KERN_INFO, "amsdu:", DUMP_PREFIX_ADDRESS, 16, 1,
+ skb->data, skb->len>64 ? 64 : skb->len, false);
+ }
+
+ if (count > ARRAY_SIZE(ecrnx_hw->stats.amsdus_rx))
+ count = ARRAY_SIZE(ecrnx_hw->stats.amsdus_rx);
+ if(count > 0)
+ ecrnx_hw->stats.amsdus_rx[count - 1]++;
+ } else {
+ ecrnx_hw->stats.amsdus_rx[0]++;
+ __skb_queue_head(&list, skb);
+ }
+
+ if (((ECRNX_VIF_TYPE(ecrnx_vif) == NL80211_IFTYPE_AP) ||
+ (ECRNX_VIF_TYPE(ecrnx_vif) == NL80211_IFTYPE_AP_VLAN) ||
+ (ECRNX_VIF_TYPE(ecrnx_vif) == NL80211_IFTYPE_P2P_GO)) &&
+ !(ecrnx_vif->ap.flags & ECRNX_AP_ISOLATE)) {
+ const struct ethhdr *eth;
+ rx_skb = skb_peek(&list);
+ skb_reset_mac_header(rx_skb);
+ eth = eth_hdr(rx_skb);
+
+ if (unlikely(is_multicast_ether_addr(eth->h_dest))) {
+ /* broadcast pkt need to be forwared to upper layer and resent
+ on wireless interface */
+ resend = true;
+ } else {
+ /* unicast pkt for STA inside the BSS, no need to forward to upper
+ layer simply resend on wireless interface */
+ if (rxhdr->flags_dst_idx != ECRNX_INVALID_STA)
+ {
+ struct ecrnx_sta *sta = &ecrnx_hw->sta_table[rxhdr->flags_dst_idx];
+ if (sta->valid && (sta->vlan_idx == ecrnx_vif->vif_index))
+ {
+ forward = false;
+ resend = true;
+ }
+ }
+ }
+ } else if (ECRNX_VIF_TYPE(ecrnx_vif) == NL80211_IFTYPE_MESH_POINT) {
+ const struct ethhdr *eth;
+ rx_skb = skb_peek(&list);
+ skb_reset_mac_header(rx_skb);
+ eth = eth_hdr(rx_skb);
+
+ /* unicast pkt for STA inside the BSS, no need to forward to upper
+ layer simply resend on wireless interface */
+ if (rxhdr->flags_dst_idx != ECRNX_INVALID_STA)
+ {
+ resend = true;
+ if (is_multicast_ether_addr(eth->h_dest)) {
+ uint8_t *mesh_ctrl = (uint8_t *)(eth + 1);
+ skip_after_eth_hdr = 8 + 6;
+ if ((*mesh_ctrl & MESH_FLAGS_AE) == MESH_FLAGS_AE_A4)
+ skip_after_eth_hdr += ETH_ALEN;
+ else if ((*mesh_ctrl & MESH_FLAGS_AE) == MESH_FLAGS_AE_A5_A6)
+ skip_after_eth_hdr += 2 * ETH_ALEN;
+ } else {
+ forward = false;
+ }
+ }
+ }
+
+ while (!skb_queue_empty(&list)) {
+ rx_skb = __skb_dequeue(&list);
+
+ /* resend pkt on wireless interface */
+ if (resend) {
+ struct sk_buff *skb_copy;
+ /* always need to copy buffer when forward=0 to get enough headrom for tsdesc */
+ skb_copy = skb_copy_expand(rx_skb, ECRNX_TX_MAX_HEADROOM, 0, GFP_ATOMIC);
+
+ if (skb_copy) {
+ int res;
+ skb_copy->protocol = htons(ETH_P_802_3);
+ skb_reset_network_header(skb_copy);
+ skb_reset_mac_header(skb_copy);
+
+ ecrnx_vif->is_resending = true;
+ res = dev_queue_xmit(skb_copy);
+ ecrnx_vif->is_resending = false;
+ /* note: buffer is always consummed by dev_queue_xmit */
+ if (res == NET_XMIT_DROP) {
+ ecrnx_vif->net_stats.rx_dropped++;
+ ecrnx_vif->net_stats.tx_dropped++;
+ } else if (res != NET_XMIT_SUCCESS) {
+ netdev_err(ecrnx_vif->ndev,
+ "Failed to re-send buffer to driver (res=%d)",
+ res);
+ ecrnx_vif->net_stats.tx_errors++;
+ }
+ } else {
+ netdev_err(ecrnx_vif->ndev, "Failed to copy skb");
+ }
+ }
+
+ /* forward pkt to upper layer */
+ if (forward) {
+ rx_skb->protocol = eth_type_trans(rx_skb, ecrnx_vif->ndev);
+ memset(rx_skb->cb, 0, sizeof(rx_skb->cb));
+
+ if (unlikely(skip_after_eth_hdr)) {
+ memmove(skb_mac_header(rx_skb) + skip_after_eth_hdr,
+ skb_mac_header(rx_skb), sizeof(struct ethhdr));
+ __skb_pull(rx_skb, skip_after_eth_hdr);
+ skb_reset_mac_header(rx_skb);
+ skip_after_eth_hdr = 0;
+ }
+ REG_SW_SET_PROFILING(ecrnx_hw, SW_PROF_IEEE80211RX);
+ ECRNX_DBG("netif_receive_skb enter!! \n");
+ /* netif_receive_skb only be called from softirq context, replace it with netif_rx */
+ netif_rx(rx_skb);
+ REG_SW_CLEAR_PROFILING(ecrnx_hw, SW_PROF_IEEE80211RX);
+
+ /* Update statistics */
+ ecrnx_vif->net_stats.rx_packets++;
+ ecrnx_vif->net_stats.rx_bytes += rx_skb->len;
+ }
+ }
+
+ return forward;
+}
+
+/**
+ * ecrnx_rx_mgmt - Process one 802.11 management frame
+ *
+ * @ecrnx_hw: main driver data
+ * @ecrnx_vif: vif to upload the buffer to
+ * @skb: skb received
+ * @rxhdr: HW rx descriptor
+ *
+ * Forward the management frame to a given interface.
+ */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0)
+extern u64_l getBootTime(void);
+#endif
+static void ecrnx_rx_mgmt(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *ecrnx_vif,
+ struct sk_buff *skb, struct hw_rxhdr *hw_rxhdr)
+{
+ struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
+ struct rx_vector_1 *rxvect = &hw_rxhdr->hwvect.rx_vect1;
+
+ if (ieee80211_is_beacon(mgmt->frame_control)) {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0)
+ mgmt->u.beacon.timestamp = getBootTime();
+#endif
+ if ((ECRNX_VIF_TYPE(ecrnx_vif) == NL80211_IFTYPE_MESH_POINT) &&
+ hw_rxhdr->flags_new_peer) {
+ cfg80211_notify_new_peer_candidate(ecrnx_vif->ndev, mgmt->sa,
+ mgmt->u.beacon.variable,
+ skb->len - offsetof(struct ieee80211_mgmt,
+ u.beacon.variable),
+ rxvect->rssi1, GFP_ATOMIC);
+ } else {
+ //ECRNX_DBG("%s:%d beacon\n", __func__, __LINE__);
+ cfg80211_report_obss_beacon(ecrnx_hw->wiphy, skb->data, skb->len,
+ hw_rxhdr->phy_info.phy_prim20_freq,
+ rxvect->rssi1);
+ }
+ } else if ((ieee80211_is_deauth(mgmt->frame_control) ||
+ ieee80211_is_disassoc(mgmt->frame_control)) &&
+ (mgmt->u.deauth.reason_code == WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA ||
+ mgmt->u.deauth.reason_code == WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA)) {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0) // TODO: process unprot mgmt
+ cfg80211_rx_unprot_mlme_mgmt(ecrnx_vif->ndev, skb->data, skb->len);
+#endif
+ } else if ((ECRNX_VIF_TYPE(ecrnx_vif) == NL80211_IFTYPE_STATION) &&
+ (ieee80211_is_action(mgmt->frame_control) &&
+ (mgmt->u.action.category == 6))) {
+ // Wpa_supplicant will ignore the FT action frame if reported via cfg80211_rx_mgmt
+ // and cannot call cfg80211_ft_event from atomic context so defer message processing
+ ecrnx_rx_defer_skb(ecrnx_hw, ecrnx_vif, skb);
+ } else {
+ cfg80211_rx_mgmt(&ecrnx_vif->wdev, hw_rxhdr->phy_info.phy_prim20_freq,
+ rxvect->rssi1, skb->data, skb->len, 0);
+ }
+}
+
+#if 0
+static void dump_mgmt_rx(struct hw_rxhdr *hw_rxhdr)
+{
+ ECRNX_DBG("%s, amsdu:%d, 80211_mpdu:%d, 4addr:%d, vif_idx:%d, sta_idx:%d, dst_idx:%d \n", \
+ __func__, \
+ hw_rxhdr->flags_is_amsdu, \
+ hw_rxhdr->flags_is_80211_mpdu, \
+ hw_rxhdr->flags_is_4addr, \
+ hw_rxhdr->flags_vif_idx, \
+ hw_rxhdr->flags_sta_idx, \
+ hw_rxhdr->flags_dst_idx);
+
+ ECRNX_DBG("phy_prim20_freq: 0x%x \n", hw_rxhdr->phy_info.phy_prim20_freq);
+}
+#endif
+
+/**
+ * ecrnx_rx_mgmt_any - Process one 802.11 management frame
+ *
+ * @ecrnx_hw: main driver data
+ * @skb: skb received
+ * @rxhdr: HW rx descriptor
+ *
+ * Process the management frame and free the corresponding skb.
+ * If vif is not specified in the rx descriptor, the the frame is uploaded
+ * on all active vifs.
+ */
+static void ecrnx_rx_mgmt_any(struct ecrnx_hw *ecrnx_hw, struct sk_buff *skb,
+ struct hw_rxhdr *hw_rxhdr)
+{
+ struct ecrnx_vif *ecrnx_vif;
+ int vif_idx = hw_rxhdr->flags_vif_idx;
+
+ //ECRNX_DBG("%s:%d \n", __func__, __LINE__);
+ trace_mgmt_rx(hw_rxhdr->phy_info.phy_prim20_freq, vif_idx,
+ hw_rxhdr->flags_sta_idx, (struct ieee80211_mgmt *)skb->data);
+
+ if (vif_idx == ECRNX_INVALID_VIF) {
+ //ECRNX_DBG("search the list \n");
+ list_for_each_entry(ecrnx_vif, &ecrnx_hw->vifs, list) {
+ //ECRNX_DBG("find a item, vif_up:%d \n", ecrnx_vif->up);
+ if (! ecrnx_vif->up)
+ continue;
+ //ECRNX_DBG("wpa up \n");
+ ecrnx_rx_mgmt(ecrnx_hw, ecrnx_vif, skb, hw_rxhdr);
+ }
+ } else {
+ ecrnx_vif = ecrnx_rx_get_vif(ecrnx_hw, vif_idx);
+ if (ecrnx_vif)
+ ecrnx_rx_mgmt(ecrnx_hw, ecrnx_vif, skb, hw_rxhdr);
+ }
+
+ dev_kfree_skb(skb);
+}
+
+/**
+ * ecrnx_rx_rtap_hdrlen - Return radiotap header length
+ *
+ * @rxvect: Rx vector used to fill the radiotap header
+ * @has_vend_rtap: boolean indicating if vendor specific data is present
+ *
+ * Compute the length of the radiotap header based on @rxvect and vendor
+ * specific data (if any).
+ */
+static u8 ecrnx_rx_rtap_hdrlen(struct rx_vector_1 *rxvect,
+ bool has_vend_rtap)
+{
+ u8 rtap_len;
+
+ /* Compute radiotap header length */
+ rtap_len = sizeof(struct ieee80211_radiotap_header) + 8;
+
+ // Check for multiple antennas
+ if (hweight32(rxvect->antenna_set) > 1)
+ // antenna and antenna signal fields
+ rtap_len += 4 * hweight8(rxvect->antenna_set);
+
+ // TSFT
+ if (!has_vend_rtap) {
+ rtap_len = ALIGN(rtap_len, 8);
+ rtap_len += 8;
+ }
+
+ // IEEE80211_HW_SIGNAL_DBM
+ rtap_len++;
+
+ // Check if single antenna
+ if (hweight32(rxvect->antenna_set) == 1)
+ rtap_len++; //Single antenna
+
+ // padding for RX FLAGS
+ rtap_len = ALIGN(rtap_len, 2);
+
+ // Check for HT frames
+ if ((rxvect->format_mod == FORMATMOD_HT_MF) ||
+ (rxvect->format_mod == FORMATMOD_HT_GF))
+ rtap_len += 3;
+
+ // Check for AMPDU
+ if (!(has_vend_rtap) && ((rxvect->format_mod >= FORMATMOD_VHT) ||
+ ((rxvect->format_mod > FORMATMOD_NON_HT_DUP_OFDM) &&
+ (rxvect->ht.aggregation)))) {
+ rtap_len = ALIGN(rtap_len, 4);
+ rtap_len += 8;
+ }
+
+ // Check for VHT frames
+ if (rxvect->format_mod == FORMATMOD_VHT) {
+ rtap_len = ALIGN(rtap_len, 2);
+ rtap_len += 12;
+ }
+
+ // Check for HE frames
+ if (rxvect->format_mod == FORMATMOD_HE_SU) {
+ rtap_len = ALIGN(rtap_len, 2);
+ rtap_len += sizeof(struct ieee80211_radiotap_he);
+ }
+
+ // Check for multiple antennas
+ if (hweight32(rxvect->antenna_set) > 1) {
+ // antenna and antenna signal fields
+ rtap_len += 2 * hweight8(rxvect->antenna_set);
+ }
+
+ // Check for vendor specific data
+ if (has_vend_rtap) {
+ /* vendor presence bitmap */
+ rtap_len += 4;
+ /* alignment for fixed 6-byte vendor data header */
+ rtap_len = ALIGN(rtap_len, 2);
+ }
+
+ return rtap_len;
+}
+
+/**
+ * ecrnx_rx_add_rtap_hdr - Add radiotap header to sk_buff
+ *
+ * @ecrnx_hw: main driver data
+ * @skb: skb received (will include the radiotap header)
+ * @rxvect: Rx vector
+ * @phy_info: Information regarding the phy
+ * @hwvect: HW Info (NULL if vendor specific data is available)
+ * @rtap_len: Length of the radiotap header
+ * @vend_rtap_len: radiotap vendor length (0 if not present)
+ * @vend_it_present: radiotap vendor present
+ *
+ * Builds a radiotap header and add it to @skb.
+ */
+static void ecrnx_rx_add_rtap_hdr(struct ecrnx_hw* ecrnx_hw,
+ struct sk_buff *skb,
+ struct rx_vector_1 *rxvect,
+ struct phy_channel_info_desc *phy_info,
+ struct hw_vect *hwvect,
+ int rtap_len,
+ u8 vend_rtap_len,
+ u32 vend_it_present)
+{
+ struct ieee80211_radiotap_header *rtap;
+ u8 *pos, rate_idx;
+ __le32 *it_present;
+ u32 it_present_val = 0;
+ bool fec_coding = false;
+ bool short_gi = false;
+ bool stbc = false;
+ bool aggregation = false;
+
+ rtap = (struct ieee80211_radiotap_header *)skb_push(skb, rtap_len);
+ memset((u8*) rtap, 0, rtap_len);
+
+ rtap->it_version = 0;
+ rtap->it_pad = 0;
+ rtap->it_len = cpu_to_le16(rtap_len + vend_rtap_len);
+
+ it_present = (__le32*)&rtap->it_present;
+
+ // Check for multiple antennas
+ if (hweight32(rxvect->antenna_set) > 1) {
+ int chain;
+ unsigned long chains = rxvect->antenna_set;
+
+ for_each_set_bit(chain, &chains, IEEE80211_MAX_CHAINS) {
+ it_present_val |=
+ BIT(IEEE80211_RADIOTAP_EXT) |
+ BIT(IEEE80211_RADIOTAP_RADIOTAP_NAMESPACE);
+ put_unaligned_le32(it_present_val, it_present);
+ it_present++;
+ it_present_val = BIT(IEEE80211_RADIOTAP_ANTENNA) |
+ BIT(IEEE80211_RADIOTAP_DBM_ANTSIGNAL);
+ }
+ }
+
+ // Check if vendor specific data is present
+ if (vend_rtap_len) {
+ it_present_val |= BIT(IEEE80211_RADIOTAP_VENDOR_NAMESPACE) |
+ BIT(IEEE80211_RADIOTAP_EXT);
+ put_unaligned_le32(it_present_val, it_present);
+ it_present++;
+ it_present_val = vend_it_present;
+ }
+
+ put_unaligned_le32(it_present_val, it_present);
+ pos = (void *)(it_present + 1);
+
+ // IEEE80211_RADIOTAP_TSFT
+ if (hwvect) {
+ rtap->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_TSFT);
+ // padding
+ while ((pos - (u8 *)rtap) & 7)
+ *pos++ = 0;
+ put_unaligned_le64((((u64)le32_to_cpu(hwvect->tsf_hi) << 32) +
+ (u64)le32_to_cpu(hwvect->tsf_lo)), pos);
+ pos += 8;
+ }
+
+ // IEEE80211_RADIOTAP_FLAGS
+ rtap->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_FLAGS);
+ if (hwvect && (!hwvect->status.frm_successful_rx))
+ *pos |= IEEE80211_RADIOTAP_F_BADFCS;
+ if (!rxvect->pre_type
+ && (rxvect->format_mod <= FORMATMOD_NON_HT_DUP_OFDM))
+ *pos |= IEEE80211_RADIOTAP_F_SHORTPRE;
+ pos++;
+
+ // IEEE80211_RADIOTAP_RATE
+ // check for HT, VHT or HE frames
+ if (rxvect->format_mod >= FORMATMOD_HE_SU) {
+ rate_idx = rxvect->he.mcs;
+ fec_coding = rxvect->he.fec;
+ stbc = rxvect->he.stbc;
+ aggregation = true;
+ *pos = 0;
+ } else if (rxvect->format_mod == FORMATMOD_VHT) {
+ rate_idx = rxvect->vht.mcs;
+ fec_coding = rxvect->vht.fec;
+ short_gi = rxvect->vht.short_gi;
+ stbc = rxvect->vht.stbc;
+ aggregation = true;
+ *pos = 0;
+ } else if (rxvect->format_mod > FORMATMOD_NON_HT_DUP_OFDM) {
+ rate_idx = rxvect->ht.mcs;
+ fec_coding = rxvect->ht.fec;
+ short_gi = rxvect->ht.short_gi;
+ stbc = rxvect->ht.stbc;
+ aggregation = rxvect->ht.aggregation;
+ *pos = 0;
+ } else {
+ struct ieee80211_supported_band* band =
+ ecrnx_hw->wiphy->bands[phy_info->phy_band];
+ rtap->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_RATE);
+ BUG_ON((rate_idx = legrates_lut[rxvect->leg_rate].idx) == -1);
+#ifdef CONFIG_ECRNX_5G
+ if (phy_info->phy_band == NL80211_BAND_5GHZ)
+ rate_idx -= 4; /* ecrnx_ratetable_5ghz[0].hw_value == 4 */
+#endif
+ *pos = DIV_ROUND_UP(band->bitrates[rate_idx].bitrate, 5);
+ }
+ pos++;
+
+ // IEEE80211_RADIOTAP_CHANNEL
+ rtap->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_CHANNEL);
+ put_unaligned_le16(phy_info->phy_prim20_freq, pos);
+ pos += 2;
+
+#ifdef CONFIG_ECRNX_5G
+ if (phy_info->phy_band == NL80211_BAND_5GHZ)
+ put_unaligned_le16(IEEE80211_CHAN_OFDM | IEEE80211_CHAN_5GHZ, pos);
+ else if (rxvect->format_mod > FORMATMOD_NON_HT_DUP_OFDM)
+#else
+ if (rxvect->format_mod > FORMATMOD_NON_HT_DUP_OFDM)
+#endif
+ put_unaligned_le16(IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ, pos);
+ else
+ put_unaligned_le16(IEEE80211_CHAN_CCK | IEEE80211_CHAN_2GHZ, pos);
+ pos += 2;
+
+ if (hweight32(rxvect->antenna_set) == 1) {
+ // IEEE80211_RADIOTAP_DBM_ANTSIGNAL
+ rtap->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL);
+ *pos++ = rxvect->rssi1;
+
+ // IEEE80211_RADIOTAP_ANTENNA
+ rtap->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_ANTENNA);
+ *pos++ = rxvect->antenna_set;
+ }
+
+ // IEEE80211_RADIOTAP_LOCK_QUALITY is missing
+ // IEEE80211_RADIOTAP_DB_ANTNOISE is missing
+
+ // IEEE80211_RADIOTAP_RX_FLAGS
+ rtap->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_RX_FLAGS);
+ // 2 byte alignment
+ if ((pos - (u8 *)rtap) & 1)
+ *pos++ = 0;
+ put_unaligned_le16(0, pos);
+ //Right now, we only support fcs error (no RX_FLAG_FAILED_PLCP_CRC)
+ pos += 2;
+
+ // Check if HT
+ if ((rxvect->format_mod == FORMATMOD_HT_MF) ||
+ (rxvect->format_mod == FORMATMOD_HT_GF)) {
+ rtap->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_MCS);
+ *pos++ = (IEEE80211_RADIOTAP_MCS_HAVE_MCS |
+ IEEE80211_RADIOTAP_MCS_HAVE_GI |
+ IEEE80211_RADIOTAP_MCS_HAVE_BW |
+ IEEE80211_RADIOTAP_MCS_HAVE_FMT |
+ IEEE80211_RADIOTAP_MCS_HAVE_FEC |
+#if LINUX_VERSION_CODE > KERNEL_VERSION(3, 17, 0)
+ IEEE80211_RADIOTAP_MCS_HAVE_STBC |
+#endif
+ 0);
+ pos++;
+ *pos = 0;
+ if (short_gi)
+ *pos |= IEEE80211_RADIOTAP_MCS_SGI;
+ if (rxvect->ch_bw == PHY_CHNL_BW_40)
+ *pos |= IEEE80211_RADIOTAP_MCS_BW_40;
+ if (rxvect->format_mod == FORMATMOD_HT_GF)
+ *pos |= IEEE80211_RADIOTAP_MCS_FMT_GF;
+ if (fec_coding)
+ *pos |= IEEE80211_RADIOTAP_MCS_FEC_LDPC;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0)
+ *pos++ |= stbc << 5;
+#else
+ *pos++ |= stbc << IEEE80211_RADIOTAP_MCS_STBC_SHIFT;
+#endif
+ *pos++ = rate_idx;
+ }
+
+ // check for HT or VHT frames
+ if (aggregation && hwvect) {
+ // 4 byte alignment
+ while ((pos - (u8 *)rtap) & 3)
+ pos++;
+ rtap->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_AMPDU_STATUS);
+ put_unaligned_le32(hwvect->ampdu_cnt, pos);
+ pos += 4;
+ put_unaligned_le32(0, pos);
+ pos += 4;
+ }
+
+ // Check for VHT frames
+ if (rxvect->format_mod == FORMATMOD_VHT) {
+ u16 vht_details = IEEE80211_RADIOTAP_VHT_KNOWN_GI |
+ IEEE80211_RADIOTAP_VHT_KNOWN_BANDWIDTH;
+ u8 vht_nss = rxvect->vht.nss + 1;
+
+ rtap->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_VHT);
+
+ if ((rxvect->ch_bw == PHY_CHNL_BW_160)
+ && phy_info->phy_center2_freq)
+ vht_details &= ~IEEE80211_RADIOTAP_VHT_KNOWN_BANDWIDTH;
+ put_unaligned_le16(vht_details, pos);
+ pos += 2;
+
+ // flags
+ if (short_gi)
+ *pos |= IEEE80211_RADIOTAP_VHT_FLAG_SGI;
+ if (stbc)
+ *pos |= IEEE80211_RADIOTAP_VHT_FLAG_STBC;
+ pos++;
+
+ // bandwidth
+ if (rxvect->ch_bw == PHY_CHNL_BW_40)
+ *pos++ = 1;
+ if (rxvect->ch_bw == PHY_CHNL_BW_80)
+ *pos++ = 4;
+ else if ((rxvect->ch_bw == PHY_CHNL_BW_160)
+ && phy_info->phy_center2_freq)
+ *pos++ = 0; //80P80
+ else if (rxvect->ch_bw == PHY_CHNL_BW_160)
+ *pos++ = 11;
+ else // 20 MHz
+ *pos++ = 0;
+
+ // MCS/NSS
+ *pos++ = (rate_idx << 4) | vht_nss;
+ *pos++ = 0;
+ *pos++ = 0;
+ *pos++ = 0;
+ if (fec_coding){
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 15, 0)
+ *pos |= 0x01;
+#else
+ *pos |= IEEE80211_RADIOTAP_CODING_LDPC_USER0;
+#endif
+ }
+ pos++;
+ // group ID
+ pos++;
+ // partial_aid
+ pos += 2;
+ }
+
+ // Check for HE frames
+ if (rxvect->format_mod >= FORMATMOD_HE_SU) {
+ struct ieee80211_radiotap_he he;
+ memset(&he, 0, sizeof(struct ieee80211_radiotap_he));
+ #define HE_PREP(f, val) cpu_to_le16(FIELD_PREP(IEEE80211_RADIOTAP_HE_##f, val))
+ #define D1_KNOWN(f) cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_##f##_KNOWN)
+ #define D2_KNOWN(f) cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA2_##f##_KNOWN)
+
+ he.data1 = D1_KNOWN(BSS_COLOR) | D1_KNOWN(BEAM_CHANGE) |
+ D1_KNOWN(UL_DL) | D1_KNOWN(STBC) |
+ D1_KNOWN(DOPPLER) | D1_KNOWN(DATA_DCM);
+ he.data2 = D2_KNOWN(GI) | D2_KNOWN(TXBF) | D2_KNOWN(TXOP);
+
+ he.data3 |= HE_PREP(DATA3_BSS_COLOR, rxvect->he.bss_color);
+ he.data3 |= HE_PREP(DATA3_BEAM_CHANGE, rxvect->he.beam_change);
+ he.data3 |= HE_PREP(DATA3_UL_DL, rxvect->he.uplink_flag);
+ he.data3 |= HE_PREP(DATA3_BSS_COLOR, rxvect->he.bss_color);
+ he.data3 |= HE_PREP(DATA3_DATA_DCM, rxvect->he.dcm);
+ he.data5 |= HE_PREP(DATA5_GI, rxvect->he.gi_type);
+ he.data5 |= HE_PREP(DATA5_TXBF, rxvect->he.beamformed);
+ he.data5 |= HE_PREP(DATA5_LTF_SIZE, rxvect->he.he_ltf_type + 1);
+ he.data6 |= HE_PREP(DATA6_DOPPLER, rxvect->he.doppler);
+ he.data6 |= HE_PREP(DATA6_TXOP, rxvect->he.txop_duration);
+ if (rxvect->format_mod != FORMATMOD_HE_TB) {
+ he.data1 |= (D1_KNOWN(DATA_MCS) | D1_KNOWN(CODING) |
+ D1_KNOWN(SPTL_REUSE) | D1_KNOWN(BW_RU_ALLOC));
+ if (stbc) {
+ he.data6 |= HE_PREP(DATA6_NSTS, 2);
+ he.data3 |= HE_PREP(DATA3_STBC, 1);
+ } else {
+ he.data6 |= HE_PREP(DATA6_NSTS, rxvect->he.nss);
+ }
+
+ he.data3 |= HE_PREP(DATA3_DATA_MCS, rxvect->he.mcs);
+ he.data3 |= HE_PREP(DATA3_CODING, rxvect->he.fec);
+
+ he.data4 = HE_PREP(DATA4_SU_MU_SPTL_REUSE, rxvect->he.spatial_reuse);
+
+ if (rxvect->format_mod == FORMATMOD_HE_MU) {
+ he.data1 |= IEEE80211_RADIOTAP_HE_DATA1_FORMAT_MU;
+ he.data5 |= HE_PREP(DATA5_DATA_BW_RU_ALLOC,
+ rxvect->he.ru_size +
+ IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_26T);
+ } else {
+ if (rxvect->format_mod == FORMATMOD_HE_SU)
+ he.data1 |= IEEE80211_RADIOTAP_HE_DATA1_FORMAT_SU;
+ else
+ he.data1 |= IEEE80211_RADIOTAP_HE_DATA1_FORMAT_EXT_SU;
+
+ switch (rxvect->ch_bw) {
+ case PHY_CHNL_BW_20:
+ he.data5 |= HE_PREP(DATA5_DATA_BW_RU_ALLOC,
+ IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_20MHZ);
+ break;
+ case PHY_CHNL_BW_40:
+ he.data5 |= HE_PREP(DATA5_DATA_BW_RU_ALLOC,
+ IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_40MHZ);
+ break;
+ case PHY_CHNL_BW_80:
+ he.data5 |= HE_PREP(DATA5_DATA_BW_RU_ALLOC,
+ IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_80MHZ);
+ break;
+ case PHY_CHNL_BW_160:
+ he.data5 |= HE_PREP(DATA5_DATA_BW_RU_ALLOC,
+ IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_160MHZ);
+ break;
+ default:
+ WARN_ONCE(1, "Invalid SU BW %d\n", rxvect->ch_bw);
+ }
+ }
+ } else {
+ he.data1 |= IEEE80211_RADIOTAP_HE_DATA1_FORMAT_TRIG;
+ }
+
+ /* ensure 2 byte alignment */
+ while ((pos - (u8 *)rtap) & 1)
+ pos++;
+ rtap->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_HE);
+ memcpy(pos, &he, sizeof(he));
+ pos += sizeof(he);
+ }
+
+ // Rx Chains
+ if (hweight32(rxvect->antenna_set) > 1) {
+ int chain;
+ unsigned long chains = rxvect->antenna_set;
+ u8 rssis[4] = {rxvect->rssi1, rxvect->rssi1, rxvect->rssi1, rxvect->rssi1};
+
+ for_each_set_bit(chain, &chains, IEEE80211_MAX_CHAINS) {
+ *pos++ = rssis[chain];
+ *pos++ = chain;
+ }
+ }
+}
+
+/**
+ * ecrnx_rx_monitor - Build radiotap header for skb an send it to netdev
+ *
+ * @ecrnx_hw: main driver data
+ * @ecrnx_vif: vif that received the buffer
+ * @skb: sk_buff received
+ * @hw_rxhdr_ptr: Pointer to HW RX header
+ * @rtap_len: Radiotap Header length
+ *
+ * Add radiotap header to the receved skb and send it to netdev
+ */
+static int ecrnx_rx_monitor(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *ecrnx_vif,
+ struct sk_buff *skb, struct hw_rxhdr *hw_rxhdr_ptr,
+ u8 rtap_len)
+{
+ skb->dev = ecrnx_vif->ndev;
+
+ ECRNX_DBG("%s enter!!", __func__);
+ if (ecrnx_vif->wdev.iftype != NL80211_IFTYPE_MONITOR) {
+ netdev_err(ecrnx_vif->ndev, "not a monitor vif\n");
+ return -1;
+ }
+
+ /* Add RadioTap Header */
+ ecrnx_rx_add_rtap_hdr(ecrnx_hw, skb, &hw_rxhdr_ptr->hwvect.rx_vect1,
+ &hw_rxhdr_ptr->phy_info, &hw_rxhdr_ptr->hwvect,
+ rtap_len, 0, 0);
+
+ skb_reset_mac_header(skb);
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ skb->pkt_type = PACKET_OTHERHOST;
+ skb->protocol = htons(ETH_P_802_2);
+
+ netif_receive_skb(skb);
+ ECRNX_DBG("%s exit!!", __func__);
+ return 0;
+}
+
+/**
+* ecrnx_rx_deferred - Work function to defer processing of buffer that cannot be
+* done in ecrnx_rxdataind (that is called in atomic context)
+*
+* @ws: work field within struct ecrnx_defer_rx
+*/
+void ecrnx_rx_deferred(struct work_struct *ws)
+{
+ struct ecrnx_defer_rx *rx = container_of(ws, struct ecrnx_defer_rx, work);
+ struct sk_buff *skb;
+
+ while ((skb = skb_dequeue(&rx->sk_list)) != NULL) {
+ // Currently only management frame can be deferred
+ struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
+ struct ecrnx_defer_rx_cb *rx_cb = (struct ecrnx_defer_rx_cb *)skb->cb;
+
+ if (ieee80211_is_action(mgmt->frame_control) &&
+ (mgmt->u.action.category == 6)) {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+ struct cfg80211_ft_event_params ft_event;
+ struct ecrnx_vif *vif = rx_cb->vif;
+ u8 *action_frame = (u8 *)&mgmt->u.action;
+ u8 action_code = action_frame[1];
+ u16 status_code = *((u16 *)&action_frame[2 + 2 * ETH_ALEN]);
+
+ if ((action_code == 2) && (status_code == 0)) {
+ ft_event.target_ap = action_frame + 2 + ETH_ALEN;
+ ft_event.ies = action_frame + 2 + 2 * ETH_ALEN + 2;
+ ft_event.ies_len = skb->len - (ft_event.ies - (u8 *)mgmt);
+ ft_event.ric_ies = NULL;
+ ft_event.ric_ies_len = 0;
+ cfg80211_ft_event(rx_cb->vif->ndev, &ft_event);
+ vif->sta.flags |= ECRNX_STA_FT_OVER_DS;
+ memcpy(vif->sta.ft_target_ap, ft_event.target_ap, ETH_ALEN);
+
+ }
+#endif
+ } else if (ieee80211_is_auth(mgmt->frame_control)) {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+ struct cfg80211_ft_event_params ft_event;
+ struct ecrnx_vif *vif = rx_cb->vif;
+ ft_event.target_ap = vif->sta.ft_target_ap;
+ ft_event.ies = mgmt->u.auth.variable;
+ ft_event.ies_len = (skb->len -
+ offsetof(struct ieee80211_mgmt, u.auth.variable));
+ ft_event.ric_ies = NULL;
+ ft_event.ric_ies_len = 0;
+ cfg80211_ft_event(rx_cb->vif->ndev, &ft_event);
+ vif->sta.flags |= ECRNX_STA_FT_OVER_AIR;
+#endif
+ } else {
+ netdev_warn(rx_cb->vif->ndev, "Unexpected deferred frame fctl=0x%04x",
+ mgmt->frame_control);
+ }
+
+ dev_kfree_skb(skb);
+ }
+}
+
+/**
+ * ecrnx_unsup_rx_vec_ind() - IRQ handler callback for %IPC_IRQ_E2A_UNSUP_RX_VEC
+ *
+ * LMAC has triggered an IT saying that a rx vector of an unsupported frame has been
+ * captured and sent to upper layer. Then we need to fill the rx status, create a vendor
+ * specific header and fill it with the HT packet length. Finally, we need to specify at
+ * least 2 bytes of data and send the sk_buff to mac80211.
+ *
+ * @pthis: Pointer to main driver data
+ * @hostid: Pointer to IPC elem from e2aradars_pool
+ */
+u8 ecrnx_unsup_rx_vec_ind(void *pthis, void *hostid) {
+ struct ecrnx_hw *ecrnx_hw = pthis;
+ struct ecrnx_ipc_skb_elem *elem = hostid;
+ struct rx_vector_desc *rx_desc;
+ struct sk_buff *skb;
+ struct rx_vector_1 *rx_vect1;
+ struct phy_channel_info_desc *phy_info;
+ struct vendor_radiotap_hdr *rtap;
+ u16 ht_length;
+ struct ecrnx_vif *ecrnx_vif;
+ struct rx_vector_desc rx_vect_desc;
+ u8 rtap_len, vend_rtap_len = sizeof(*rtap);
+
+ dma_sync_single_for_cpu(ecrnx_hw->dev, elem->dma_addr,
+ sizeof(struct rx_vector_desc), DMA_FROM_DEVICE);
+
+ skb = elem->skb;
+ if (((struct rx_vector_desc *) (skb->data))->pattern == 0) {
+ /*sync is needed even if the driver did not modify the memory*/
+ dma_sync_single_for_device(ecrnx_hw->dev, elem->dma_addr,
+ sizeof(struct rx_vector_desc), DMA_FROM_DEVICE);
+ return -1;
+ }
+
+ if (ecrnx_hw->monitor_vif == ECRNX_INVALID_VIF) {
+ /* Unmap will synchronize buffer for CPU */
+#ifndef CONFIG_ECRNX_ESWIN
+ dma_unmap_single(ecrnx_hw->dev, elem->dma_addr, ecrnx_hw->ipc_env->unsuprxvec_bufsz,
+ DMA_FROM_DEVICE);
+#endif
+ elem->skb = NULL;
+
+ /* Free skb */
+ dev_kfree_skb(skb);
+
+#ifndef CONFIG_ECRNX_ESWIN
+ /* Allocate and push a new buffer to fw to replace this one */
+ if (ecrnx_ipc_unsup_rx_vec_elem_allocs(ecrnx_hw, elem))
+ dev_err(ecrnx_hw->dev, "Failed to alloc new unsupported rx vector buf\n");
+ return -1;
+#endif
+ }
+
+ ecrnx_vif = ecrnx_hw->vif_table[ecrnx_hw->monitor_vif];
+ skb->dev = ecrnx_vif->ndev;
+ memcpy(&rx_vect_desc, skb->data, sizeof(rx_vect_desc));
+ rx_desc = &rx_vect_desc;
+
+ rx_vect1 = (struct rx_vector_1 *) (rx_desc->rx_vect1);
+ ecrnx_rx_vector_convert(ecrnx_hw->machw_type, rx_vect1, NULL);
+ phy_info = (struct phy_channel_info_desc *) (&rx_desc->phy_info);
+ if (rx_vect1->format_mod >= FORMATMOD_VHT)
+ ht_length = 0;
+ else
+ ht_length = (u16) le32_to_cpu(rx_vect1->ht.length);
+
+ // Reserve space for radiotap
+ skb_reserve(skb, RADIOTAP_HDR_MAX_LEN);
+
+ /* Fill vendor specific header with fake values */
+ rtap = (struct vendor_radiotap_hdr *) skb->data;
+ rtap->oui[0] = 0x00;
+ rtap->oui[1] = 0x25;
+ rtap->oui[2] = 0x3A;
+ rtap->subns = 0;
+ rtap->len = sizeof(ht_length);
+ put_unaligned_le16(ht_length, rtap->data);
+ vend_rtap_len += rtap->len;
+ skb_put(skb, vend_rtap_len);
+
+ /* Copy fake data */
+ put_unaligned_le16(0, skb->data + vend_rtap_len);
+ skb_put(skb, UNSUP_RX_VEC_DATA_LEN);
+
+ /* Get RadioTap Header length */
+ rtap_len = ecrnx_rx_rtap_hdrlen(rx_vect1, true);
+
+ /* Check headroom space */
+ if (skb_headroom(skb) < rtap_len) {
+ netdev_err(ecrnx_vif->ndev, "not enough headroom %d need %d\n", skb_headroom(skb), rtap_len);
+ return -1;
+ }
+
+ /* Add RadioTap Header */
+ ecrnx_rx_add_rtap_hdr(ecrnx_hw, skb, rx_vect1, phy_info, NULL,
+ rtap_len, vend_rtap_len, BIT(0));
+
+ skb_reset_mac_header(skb);
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ skb->pkt_type = PACKET_OTHERHOST;
+ skb->protocol = htons(ETH_P_802_2);
+
+ /* Unmap will synchronize buffer for CPU */
+#ifndef CONFIG_ECRNX_ESWIN
+ dma_unmap_single(ecrnx_hw->dev, elem->dma_addr, ecrnx_hw->ipc_env->unsuprxvec_bufsz,
+ DMA_FROM_DEVICE);
+#endif
+ elem->skb = NULL;
+
+ netif_receive_skb(skb);
+
+#ifndef CONFIG_ECRNX_ESWIN
+ /* Allocate and push a new buffer to fw to replace this one */
+ if (ecrnx_ipc_unsup_rx_vec_elem_allocs(ecrnx_hw, elem))
+ netdev_err(ecrnx_vif->ndev, "Failed to alloc new unsupported rx vector buf\n");
+#endif
+ return 0;
+}
+
+/**
+ * ecrnx_rxdataind - Process rx buffer
+ *
+ * @pthis: Pointer to the object attached to the IPC structure
+ * (points to struct ecrnx_hw is this case)
+ * @hostid: Address of the RX descriptor
+ *
+ * This function is called for each buffer received by the fw
+ *
+ */
+#ifndef CONFIG_ECRNX_ESWIN
+u8 ecrnx_rxdataind(void *pthis, void *hostid)
+{
+ struct ecrnx_hw *ecrnx_hw = pthis;
+ struct ecrnx_ipc_elem *elem = hostid;
+ struct hw_rxhdr *hw_rxhdr;
+ struct rxdesc_tag *rxdesc;
+ struct ecrnx_vif *ecrnx_vif;
+ struct sk_buff *skb = NULL;
+ int rx_buff_idx;
+ int msdu_offset = sizeof(struct hw_rxhdr) + 2;
+
+ int peek_len = msdu_offset + sizeof(struct ethhdr);
+ u16_l status;
+
+ REG_SW_SET_PROFILING(ecrnx_hw, SW_PROF_ECRNXDATAIND);
+
+ if(!pthis || !hostid)
+ {
+ return -1;
+ }
+
+ /* Get the ownership of the descriptor */
+ dma_sync_single_for_cpu(ecrnx_hw->dev, elem->dma_addr,
+ sizeof(struct rxdesc_tag), DMA_FROM_DEVICE);
+
+ rxdesc = elem->addr;
+ status = rxdesc->status;
+
+ /* check that frame is completely uploaded */
+ if (!status){
+ /* Get the ownership of the descriptor */
+ dma_sync_single_for_device(ecrnx_hw->dev, elem->dma_addr,
+ sizeof(struct rxdesc_tag), DMA_FROM_DEVICE);
+ return -1;
+ }
+
+ /* Get the buffer linked with the received descriptor */
+ rx_buff_idx = ECRNX_RXBUFF_HOSTID_TO_IDX(rxdesc->host_id);
+ if (ECRNX_RXBUFF_VALID_IDX(rx_buff_idx))
+ skb = ecrnx_hw->rxbuf_elems.skb[rx_buff_idx];
+
+ if (!skb){
+ dev_err(ecrnx_hw->dev, "RX Buff invalid idx [%d]\n", rx_buff_idx);
+ return -1;
+ }
+
+ /* Check the pattern */
+ if (ECRNX_RXBUFF_PATTERN_GET(skb) != ecrnx_rxbuff_pattern) {
+ dev_err(ecrnx_hw->dev, "RX Buff Pattern not correct\n");
+ BUG();
+ }
+
+ /* Check if we need to delete the buffer */
+ if (status & RX_STAT_DELETE) {
+ /* Remove the SK buffer from the rxbuf_elems table */
+ ecrnx_ipc_rxbuf_elem_pull(ecrnx_hw, skb);
+ /* Free the buffer */
+ dev_kfree_skb(skb);
+ goto end;
+ }
+
+ /* Check if we need to forward the buffer coming from a monitor interface */
+ if (status & RX_STAT_MONITOR) {
+ struct sk_buff *skb_monitor;
+ struct hw_rxhdr hw_rxhdr_copy;
+ u8 rtap_len;
+ u16 frm_len;
+
+ //Check if monitor interface exists and is open
+ ecrnx_vif = ecrnx_rx_get_vif(ecrnx_hw, ecrnx_hw->monitor_vif);
+ if (!ecrnx_vif) {
+ dev_err(ecrnx_hw->dev, "Received monitor frame but there is no monitor interface open\n");
+ goto check_len_update;
+ }
+
+ hw_rxhdr = (struct hw_rxhdr *)skb->data;
+ ecrnx_rx_vector_convert(ecrnx_hw->machw_type,
+ &hw_rxhdr->hwvect.rx_vect1,
+ &hw_rxhdr->hwvect.rx_vect2);
+ rtap_len = ecrnx_rx_rtap_hdrlen(&hw_rxhdr->hwvect.rx_vect1, false);
+
+ // Move skb->data pointer to MAC Header or Ethernet header
+ skb->data += msdu_offset;
+
+ //Save frame length
+ frm_len = le32_to_cpu(hw_rxhdr->hwvect.len);
+
+ // Reserve space for frame
+ skb->len = frm_len;
+
+ if (status == RX_STAT_MONITOR) {
+ /* Remove the SK buffer from the rxbuf_elems table. It will also
+ unmap the buffer and then sync the buffer for the cpu */
+ ecrnx_ipc_rxbuf_elem_pull(ecrnx_hw, skb);
+
+ //Check if there is enough space to add the radiotap header
+ if (skb_headroom(skb) > rtap_len) {
+
+ skb_monitor = skb;
+
+ //Duplicate the HW Rx Header to override with the radiotap header
+ memcpy(&hw_rxhdr_copy, hw_rxhdr, sizeof(hw_rxhdr_copy));
+
+ hw_rxhdr = &hw_rxhdr_copy;
+ } else {
+ //Duplicate the skb and extend the headroom
+ skb_monitor = skb_copy_expand(skb, rtap_len, 0, GFP_ATOMIC);
+
+ //Reset original skb->data pointer
+ skb->data = (void*) hw_rxhdr;
+ }
+ }
+ else
+ {
+ #ifdef CONFIG_ECRNX_MON_DATA
+ // Check if MSDU
+ if (!hw_rxhdr->flags_is_80211_mpdu) {
+ // MSDU
+ //Extract MAC header
+ u16 machdr_len = hw_rxhdr->mac_hdr_backup.buf_len;
+ u8* machdr_ptr = hw_rxhdr->mac_hdr_backup.buffer;
+
+ //Pull Ethernet header from skb
+ skb_pull(skb, sizeof(struct ethhdr));
+
+ // Copy skb and extend for adding the radiotap header and the MAC header
+ skb_monitor = skb_copy_expand(skb,
+ rtap_len + machdr_len,
+ 0, GFP_ATOMIC);
+
+ //Reserve space for the MAC Header
+ skb_push(skb_monitor, machdr_len);
+
+ //Copy MAC Header
+ memcpy(skb_monitor->data, machdr_ptr, machdr_len);
+
+ //Update frame length
+ frm_len += machdr_len - sizeof(struct ethhdr);
+ } else {
+ // MPDU
+ skb_monitor = skb_copy_expand(skb, rtap_len, 0, GFP_ATOMIC);
+ }
+
+ //Reset original skb->data pointer
+ skb->data = (void*) hw_rxhdr;
+ #else
+ //Reset original skb->data pointer
+ skb->data = (void*) hw_rxhdr;
+
+ wiphy_err(ecrnx_hw->wiphy, "RX status %d is invalid when MON_DATA is disabled\n", status);
+ goto check_len_update;
+ #endif
+ }
+
+ skb_reset_tail_pointer(skb);
+ skb->len = 0;
+ skb_reset_tail_pointer(skb_monitor);
+ skb_monitor->len = 0;
+
+ skb_put(skb_monitor, frm_len);
+ if (ecrnx_rx_monitor(ecrnx_hw, ecrnx_vif, skb_monitor, hw_rxhdr, rtap_len))
+ dev_kfree_skb(skb_monitor);
+
+ if (status == RX_STAT_MONITOR) {
+ status |= RX_STAT_ALLOC;
+ if (skb_monitor != skb) {
+ dev_kfree_skb(skb);
+ }
+ }
+ }
+
+check_len_update:
+ /* Check if we need to update the length */
+ if (status & RX_STAT_LEN_UPDATE) {
+ dma_addr_t dma_addr = ECRNX_RXBUFF_DMA_ADDR_GET(skb);
+ dma_sync_single_for_cpu(ecrnx_hw->dev, dma_addr,
+ peek_len, DMA_FROM_DEVICE);
+
+ hw_rxhdr = (struct hw_rxhdr *)skb->data;
+
+ hw_rxhdr->hwvect.len = rxdesc->frame_len;
+
+ if (status & RX_STAT_ETH_LEN_UPDATE) {
+ /* Update Length Field inside the Ethernet Header */
+ struct ethhdr *hdr = (struct ethhdr *)((u8 *)hw_rxhdr + msdu_offset);
+
+ hdr->h_proto = htons(rxdesc->frame_len - sizeof(struct ethhdr));
+ }
+
+ dma_sync_single_for_device(ecrnx_hw->dev, dma_addr,
+ peek_len, DMA_BIDIRECTIONAL);
+ goto end;
+ }
+
+ /* Check if it must be discarded after informing upper layer */
+ if (status & RX_STAT_SPURIOUS) {
+ struct ieee80211_hdr *hdr;
+
+ /* Read mac header to obtain Transmitter Address */
+ ecrnx_ipc_rxbuf_elem_sync(ecrnx_hw, skb, msdu_offset + sizeof(*hdr));
+
+ hw_rxhdr = (struct hw_rxhdr *)skb->data;
+ hdr = (struct ieee80211_hdr *)(skb->data + msdu_offset);
+ ecrnx_vif = ecrnx_rx_get_vif(ecrnx_hw, hw_rxhdr->flags_vif_idx);
+ if (ecrnx_vif) {
+ cfg80211_rx_spurious_frame(ecrnx_vif->ndev, hdr->addr2, GFP_ATOMIC);
+ }
+ ecrnx_ipc_rxbuf_elem_repush(ecrnx_hw, skb);
+ goto end;
+ }
+
+ /* Check if we need to forward the buffer */
+ if (status & RX_STAT_FORWARD) {
+ struct ecrnx_sta *sta = NULL;
+
+ /* Remove the SK buffer from the rxbuf_elems table. It will also
+ unmap the buffer and then sync the buffer for the cpu */
+ ecrnx_ipc_rxbuf_elem_pull(ecrnx_hw, skb);
+
+ hw_rxhdr = (struct hw_rxhdr *)skb->data;
+ ecrnx_rx_vector_convert(ecrnx_hw->machw_type,
+ &hw_rxhdr->hwvect.rx_vect1,
+ &hw_rxhdr->hwvect.rx_vect2);
+ skb_reserve(skb, msdu_offset);
+ skb_put(skb, le32_to_cpu(hw_rxhdr->hwvect.len));
+ if (hw_rxhdr->flags_sta_idx != ECRNX_INVALID_STA) {
+ sta = &ecrnx_hw->sta_table[hw_rxhdr->flags_sta_idx];
+ ecrnx_rx_statistic(ecrnx_hw, hw_rxhdr, sta);
+ }
+
+ if (hw_rxhdr->flags_is_80211_mpdu) {
+ ecrnx_rx_mgmt_any(ecrnx_hw, skb, hw_rxhdr);
+ } else {
+ ecrnx_vif = ecrnx_rx_get_vif(ecrnx_hw, hw_rxhdr->flags_vif_idx);
+
+ if (!ecrnx_vif) {
+ dev_err(ecrnx_hw->dev, "Frame received but no active vif (%d)",
+ hw_rxhdr->flags_vif_idx);
+ dev_kfree_skb(skb);
+ goto check_alloc;
+ }
+
+ if (sta) {
+
+ if (sta->vlan_idx != ecrnx_vif->vif_index) {
+ ecrnx_vif = ecrnx_hw->vif_table[sta->vlan_idx];
+ if (!ecrnx_vif) {
+ dev_kfree_skb(skb);
+ goto check_alloc;
+ }
+ }
+
+ if (hw_rxhdr->flags_is_4addr && !ecrnx_vif->use_4addr) {
+ cfg80211_rx_unexpected_4addr_frame(ecrnx_vif->ndev,
+ sta->mac_addr, GFP_ATOMIC);
+ }
+ }
+
+ skb->priority = 256 + hw_rxhdr->flags_user_prio;
+ if (!ecrnx_rx_data_skb(ecrnx_hw, ecrnx_vif, skb, hw_rxhdr))
+ dev_kfree_skb(skb);
+ }
+ }
+
+check_alloc:
+ /* Check if we need to allocate a new buffer */
+ if ((status & RX_STAT_ALLOC) &&
+ ecrnx_ipc_rxbuf_elem_allocs(ecrnx_hw)) {
+ dev_err(ecrnx_hw->dev, "Failed to alloc new RX buf\n");
+ }
+
+end:
+ REG_SW_CLEAR_PROFILING(ecrnx_hw, SW_PROF_ECRNXDATAIND);
+
+ /* Reset and repush descriptor to FW */
+ ecrnx_ipc_rxdesc_elem_repush(ecrnx_hw, elem);
+
+ return 0;
+}
+
+#else
+
+#ifdef CONFIG_ESWIN_RX_REORDER
+
+static bool ecrnx_rx_data_pn_check(struct ecrnx_hw *ecrnx_hw, struct sk_buff *skb, u8_l vif_idx, u8_l sta_idx, u8_l tid, u8_l is_ga)
+{
+ struct ecrnx_sta *sta;
+ struct ecrnx_vif *vif;
+ u64_l *last_pn;
+ u64_l pn;
+
+ memcpy(&pn, skb->data, sizeof(u64_l));
+
+ if (is_ga) {
+ last_pn = &ecrnx_hw->vif_table[vif_idx]->rx_pn[tid];
+ } else if(ECRNX_INVALID_STA != sta_idx) {
+ last_pn = &ecrnx_hw->sta_table[sta_idx].rx_pn[tid];
+ } else
+ {
+ return true;
+ }
+
+ //printk("sta_idx:%d tid:%d pn:%llu last:%llu\n ", sta_idx, tid, pn, *last_pn);
+ if (pn > (*last_pn)){
+ *last_pn = pn;
+ return true;
+ }
+
+ return false;
+}
+
+void ecrnx_rx_reord_msdu_free(spinlock_t *lock, struct list_head *q, struct list_head *list)
+{
+ spin_lock_bh(lock);
+ list_add(list, q);
+ spin_unlock_bh(lock);
+}
+
+struct reord_msdu_info *ecrnx_rx_reord_msdu_alloc(spinlock_t *lock, struct list_head *q)
+{
+ struct reord_msdu_info *pmsdu;
+
+ spin_lock_bh(lock);
+ if (list_empty(q)) {
+ spin_unlock_bh(lock);
+ return NULL;
+ }
+
+ pmsdu = list_entry(q->next, struct reord_msdu_info, rx_msdu_list);
+ list_del_init(q->next);
+ spin_unlock_bh(lock);
+
+ return pmsdu;
+}
+
+int ecrnx_rx_reord_single_msdu(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *ecrnx_vif, struct reord_msdu_info *pmsdu)
+{
+ struct list_head *rx_msdu_free_list = NULL;
+ struct sk_buff *skb = NULL;
+ struct hw_rxhdr* hw_rxhdr;
+ //static u16_l last_sn[TID_MAX] = {0};
+
+ rx_msdu_free_list = &ecrnx_hw->rx_msdu_free_list;
+ skb = pmsdu->skb;
+ if (skb == NULL) {
+ ECRNX_ERR("skb is NULL\n");
+ return -1;
+ }
+
+ if (pmsdu->need_pn_check) {
+ if (ecrnx_rx_data_pn_check(ecrnx_hw, skb, pmsdu->hw_rxhdr->flags_vif_idx, pmsdu->hw_rxhdr->flags_sta_idx, pmsdu->tid, pmsdu->is_ga)){
+ skb_pull(skb, 8);
+ } else {
+ ECRNX_DBG("rx_data_check_pn error\n");
+ dev_kfree_skb(skb);
+ goto end;
+ }
+ }
+#if 0
+ if ((last_sn[pmsdu->tid] != pmsdu->sn) && ((last_sn[pmsdu->tid] + 1) % 4096 != pmsdu->sn))
+ {
+ ECRNX_PRINT("miss[%d] last:%d sn=%d\n",pmsdu->tid,last_sn[pmsdu->tid], pmsdu->sn);
+ }
+
+ last_sn[pmsdu->tid] = pmsdu->sn;
+#endif
+ if (!ecrnx_rx_data_skb(ecrnx_hw, ecrnx_vif, skb, pmsdu->hw_rxhdr))
+ dev_kfree_skb(skb);
+
+end:
+ pmsdu->skb = NULL;
+ ecrnx_rx_reord_msdu_free(&ecrnx_hw->rx_msdu_free_lock, rx_msdu_free_list, &pmsdu->rx_msdu_list);
+
+ return 0;
+}
+
+
+void ecrnx_rx_reord_timer_update(struct ecrnx_hw *ecrnx_hw, struct reord_cntrl *reord_cntrl, int force)
+{
+ struct list_head *phead, *plist;
+ struct reord_msdu_info *pmsdu;
+ bool update = false;
+
+ if (force == true) {
+ phead = &reord_cntrl->reord_list;
+ if (list_empty(phead)) {
+ goto end;
+ }
+
+ plist = phead->next;
+ pmsdu = list_entry(plist, struct reord_msdu_info, reord_pending_list);
+ reord_cntrl->win_start = pmsdu->sn;
+ }
+
+ phead = &reord_cntrl->reord_list;
+ if (list_empty(phead)) {
+ goto end;
+ }
+
+ list_for_each_entry(pmsdu, phead, reord_pending_list) {
+ if (!SN_LESS(reord_cntrl->win_start, pmsdu->sn)) {
+ if (SN_EQUAL(reord_cntrl->win_start, pmsdu->sn)) {
+ reord_cntrl->win_start = (reord_cntrl->win_start + 1) & 0xFFF;
+ }
+ } else {
+ update = true;
+ break;
+ }
+ }
+
+end:
+ if (update == true) {
+ if (!timer_pending(&reord_cntrl->reord_timer)) {
+ mod_timer(&reord_cntrl->reord_timer, jiffies + msecs_to_jiffies(ECRNX_REORD_TIMEOUT));
+ }
+ } else {
+ if(timer_pending(&reord_cntrl->reord_timer)) {
+ del_timer(&reord_cntrl->reord_timer);
+ }
+ }
+
+}
+
+void ecrnx_rx_reord_list_flush(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *ecrnx_vif, struct reord_cntrl *reord_cntrl)
+{
+ struct list_head *phead, *plist;
+ struct reord_msdu_info *pmsdu;
+
+ phead = &reord_cntrl->reord_list;
+ while (1) {
+ if (list_empty(phead)) {
+ break;
+ }
+
+ plist = phead->next;
+ pmsdu = list_entry(plist, struct reord_msdu_info, reord_pending_list);
+
+ if (!SN_LESS(reord_cntrl->win_start, pmsdu->sn)) {
+ list_del_init(&(pmsdu->reord_pending_list));
+ ecrnx_rx_reord_single_msdu(ecrnx_hw, ecrnx_vif, pmsdu);
+ } else {
+ break;
+ }
+ }
+}
+
+
+void ecrnx_rx_reord_timeout_handler(struct timer_list *t)
+{
+ struct reord_cntrl *reord_cntrl = from_timer(reord_cntrl, t, reord_timer);
+
+ if(!reord_cntrl->valid || !reord_cntrl->active)
+ return ;
+
+ if(!work_pending(&reord_cntrl->reord_timer_work))
+ schedule_work(&reord_cntrl->reord_timer_work);
+
+}
+
+void ecrnx_rx_reord_timeout_worker(struct work_struct *work)
+{
+ struct reord_cntrl *reord_cntrl = container_of(work, struct reord_cntrl, reord_timer_work);
+ struct ecrnx_hw *ecrnx_hw = reord_cntrl->ecrnx_hw;
+ struct ecrnx_vif *ecrnx_vif = reord_cntrl->ecrnx_vif;
+
+ if(!reord_cntrl->valid || !reord_cntrl->active)
+ return ;
+
+ spin_lock_bh(&reord_cntrl->reord_list_lock);
+
+ ecrnx_rx_reord_timer_update(ecrnx_hw, reord_cntrl, true);
+
+ ecrnx_rx_reord_list_flush(ecrnx_hw, ecrnx_vif, reord_cntrl);
+ spin_unlock_bh(&reord_cntrl->reord_list_lock);
+
+ return ;
+}
+
+void ecrnx_rx_reord_sta_init(struct ecrnx_hw* ecrnx_hw, struct ecrnx_vif *ecrnx_vif, u8 sta_idx)
+{
+ struct reord_cntrl *reord_cntrl = NULL;
+ u32_l i = 0;
+
+ ECRNX_DBG("%s sta_idx:%d\n", __func__, sta_idx);
+ for (i = 0; i < TID_MAX; i++) {
+ reord_cntrl = &ecrnx_hw->sta_table[sta_idx].reord_cntrl[i];
+ if (!reord_cntrl->valid) {
+ reord_cntrl->active = true;
+ reord_cntrl->win_start = 0xffff;
+ reord_cntrl->win_size = ECRNX_REORD_WINSIZE;
+ reord_cntrl->ecrnx_hw = ecrnx_hw;
+ reord_cntrl->ecrnx_vif = ecrnx_vif;
+ INIT_LIST_HEAD(&reord_cntrl->reord_list);
+ spin_lock_init(&reord_cntrl->reord_list_lock);
+ timer_setup(&reord_cntrl->reord_timer, ecrnx_rx_reord_timeout_handler, 0);
+ INIT_WORK(&reord_cntrl->reord_timer_work, ecrnx_rx_reord_timeout_worker);
+ reord_cntrl->valid = true;
+ }
+ }
+
+}
+
+int ecrnx_rx_reord_tid_flush(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *ecrnx_vif, struct sk_buff *skb, u8 sta_idx, u8 tid)
+{
+ struct reord_cntrl *reord_cntrl;
+ struct list_head *phead, *plist;
+ struct reord_msdu_info *pmsdu;
+
+ if (sta_idx == ECRNX_INVALID_STA)
+ return -1;
+
+ reord_cntrl = &ecrnx_hw->sta_table[sta_idx].reord_cntrl[tid];
+
+ if(!reord_cntrl->valid || !reord_cntrl->active)
+ return -1;
+
+ spin_lock(&reord_cntrl->reord_list_lock);
+ phead = &reord_cntrl->reord_list;
+ while (1) {
+ if (list_empty(phead)) {
+ break;
+ }
+ plist = phead->next;
+ pmsdu = list_entry(plist, struct reord_msdu_info, reord_pending_list);
+ ecrnx_rx_reord_single_msdu(ecrnx_hw, ecrnx_vif, pmsdu);
+ list_del_init(&(pmsdu->reord_pending_list));
+ }
+
+ //printk("flush:sta_idx:%d tid=%d \n", sta_idx,tid);
+ reord_cntrl->active = false;
+ spin_unlock(&reord_cntrl->reord_list_lock);
+ if (timer_pending(&reord_cntrl->reord_timer))
+ del_timer_sync(&reord_cntrl->reord_timer);
+ //cancel_work_sync(&reord_cntrl->reord_timer_work);
+
+ return 0;
+}
+
+void ecrnx_rx_reord_sta_deinit(struct ecrnx_hw* ecrnx_hw, u8 sta_idx, bool is_del)
+{
+ struct reord_cntrl *reord_cntrl = NULL;
+ u32_l i = 0;
+
+ if (ecrnx_hw == NULL || sta_idx >= (NX_REMOTE_STA_MAX + NX_VIRT_DEV_MAX)) {
+ return;
+ }
+ ECRNX_DBG("%s sta_idx:%d\n", __func__, sta_idx);
+
+ for (i=0; i < TID_MAX; i++) {
+ struct reord_msdu_info *req, *next;
+ reord_cntrl = &ecrnx_hw->sta_table[sta_idx].reord_cntrl[i];
+ if (reord_cntrl->valid) {
+ reord_cntrl->valid = false;
+ if(reord_cntrl->active){
+ reord_cntrl->active = false;
+ if (timer_pending(&reord_cntrl->reord_timer)) {
+ del_timer_sync(&reord_cntrl->reord_timer);
+ }
+
+ if (!is_del) {
+ cancel_work_sync(&reord_cntrl->reord_timer_work);
+ }
+ }
+
+ spin_lock(&reord_cntrl->reord_list_lock);
+ list_for_each_entry_safe(req, next, &reord_cntrl->reord_list, reord_pending_list) {
+ list_del_init(&req->reord_pending_list);
+ if(req->skb != NULL)
+ dev_kfree_skb(req->skb);
+ req->skb = NULL;
+ ecrnx_rx_reord_msdu_free(&ecrnx_hw->rx_msdu_free_lock, &ecrnx_hw->rx_msdu_free_list, &req->rx_msdu_list);
+ }
+ spin_unlock(&reord_cntrl->reord_list_lock);
+ }
+ }
+
+}
+
+static struct reord_msdu_info *ecrnx_rx_reord_queue_init(struct list_head *q, int qsize)
+{
+ int i;
+ struct reord_msdu_info *req, *reqs;
+
+ reqs = vmalloc(qsize*sizeof(struct reord_msdu_info));
+ if (reqs == NULL)
+ return NULL;
+
+ req = reqs;
+ for (i = 0; i < qsize; i++)
+ {
+ INIT_LIST_HEAD(&req->rx_msdu_list);
+ list_add(&req->rx_msdu_list, q);
+ req++;
+ }
+
+ return reqs;
+}
+
+void ecrnx_rx_reord_deinit(struct ecrnx_hw *ecrnx_hw)
+{
+ struct reord_msdu_info *req, *next;
+ u32_l sta_idx;
+
+ ECRNX_DBG("%s\n", __func__);
+ for (sta_idx = 0; sta_idx < NX_REMOTE_STA_MAX + NX_VIRT_DEV_MAX; sta_idx++)
+ {
+ if (ecrnx_hw->sta_table[sta_idx].valid)
+ {
+ ecrnx_rx_reord_sta_deinit(ecrnx_hw, sta_idx, false);
+ }
+ }
+ list_for_each_entry_safe(req, next, &ecrnx_hw->rx_msdu_free_list, rx_msdu_list) {
+ list_del_init(&req->rx_msdu_list);
+ }
+
+ if (ecrnx_hw->rx_reord_buf)
+ vfree(ecrnx_hw->rx_reord_buf);
+}
+
+void ecrnx_rx_reord_init(struct ecrnx_hw *ecrnx_hw)
+{
+ ECRNX_DBG("%s\n", __func__);
+ INIT_LIST_HEAD(&ecrnx_hw->rx_msdu_free_list);
+ spin_lock_init(&ecrnx_hw->rx_msdu_free_lock);
+ ecrnx_hw->rx_reord_buf = ecrnx_rx_reord_queue_init(&ecrnx_hw->rx_msdu_free_list, ECRNX_REORD_RX_MSDU_CNT);
+ spin_lock_init(&ecrnx_hw->rx_reord_lock);
+ INIT_LIST_HEAD(&ecrnx_hw->rx_reord_list);
+
+}
+
+int ecrnx_rx_reord_sn_check(struct reord_cntrl *reord_cntrl, u16 sn)
+{
+ u16 win_size = reord_cntrl->win_size;
+ u16 win_end = (reord_cntrl->win_start + win_size -1) & 0xFFF;
+
+ if (reord_cntrl->win_start == 0xFFFF) {
+ reord_cntrl->win_start = sn;
+ }
+
+ if (SN_LESS(sn, reord_cntrl->win_start)) {
+ return -1;
+ }
+
+ if (SN_EQUAL(sn, reord_cntrl->win_start)){
+ reord_cntrl->win_start = (reord_cntrl->win_start + 1) & 0xFFF;
+ } else if (SN_LESS(win_end, sn)) {
+ if (sn >= (win_size-1))
+ reord_cntrl->win_start = sn-(win_size-1);
+ else
+ reord_cntrl->win_start = 0xFFF - (win_size - (sn + 1)) + 1;
+ }
+
+ return 0;
+}
+
+int ecrnx_rx_reord_msdu_insert(struct reord_cntrl *reord_cntrl, struct reord_msdu_info *pmsdu)
+{
+ struct list_head *preord_list = &reord_cntrl->reord_list;
+ struct list_head *phead, *plist;
+ struct reord_msdu_info *nextmsdu;
+
+//first time:not any prframe in preord_list, so phead = phead->next
+ phead = preord_list;
+ plist = phead->next;
+
+ while(phead != plist) {
+ nextmsdu = list_entry(plist, struct reord_msdu_info, reord_pending_list);
+ if (SN_LESS(nextmsdu->sn, pmsdu->sn)) {
+ plist = plist->next;
+ } else if (SN_EQUAL(nextmsdu->sn, pmsdu->sn)){
+ return -1;
+ } else {
+ break;
+ }
+ }
+
+ list_add_tail(&(pmsdu->reord_pending_list), plist);
+
+ return 0;
+}
+
+void ecrnx_rx_reord_check(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *ecrnx_vif, struct hw_rxhdr *hw_rxhdr, struct sk_buff *skb, struct rxu_stat_mm* orignal_rxu_state)
+{
+ int ret=0;
+ struct reord_msdu_info *pmsdu;
+ struct reord_cntrl *reord_cntrl;
+
+ if (ecrnx_vif == NULL || skb->len <= 14) {
+ dev_kfree_skb(skb);
+ return ;
+ }
+
+ pmsdu = ecrnx_rx_reord_msdu_alloc(&ecrnx_hw->rx_msdu_free_lock, &ecrnx_hw->rx_msdu_free_list);
+ if (!pmsdu) {
+ dev_err(ecrnx_hw->dev, "ecrnx_rx_reord_msdu_alloc fail\n");
+ dev_kfree_skb(skb);
+ return ;
+ }
+
+ INIT_LIST_HEAD(&pmsdu->reord_pending_list);
+ pmsdu->hw_rxhdr = hw_rxhdr;
+ pmsdu->sn = orignal_rxu_state->sn;
+ pmsdu->tid = orignal_rxu_state->tid;
+ pmsdu->is_ga = orignal_rxu_state->is_ga;
+ pmsdu->skb = skb;
+ pmsdu->need_pn_check = orignal_rxu_state->need_pn_check;
+
+ if (hw_rxhdr->flags_sta_idx != ECRNX_INVALID_STA) {
+ reord_cntrl = &ecrnx_hw->sta_table[hw_rxhdr->flags_sta_idx].reord_cntrl[orignal_rxu_state->tid];
+ pmsdu->preorder_ctrl = reord_cntrl;
+ if(reord_cntrl->valid) {
+ if (!reord_cntrl->active) {
+ reord_cntrl->active = true;
+ reord_cntrl->win_start = 0xffff;
+ //reord_cntrl->win_size = ECRNX_REORD_WINSIZE;
+ //reord_cntrl->ecrnx_hw = ecrnx_hw;
+ //reord_cntrl->ecrnx_vif = ecrnx_vif;
+ }
+ } else {
+ ECRNX_PRINT("reord_cntrl invalid sta:%d sn:%d start:%d \n", hw_rxhdr->flags_sta_idx , pmsdu->sn, reord_cntrl->win_start);
+ ecrnx_rx_reord_single_msdu(ecrnx_hw, ecrnx_vif, pmsdu);
+ return ;
+ }
+ } else {
+ ECRNX_PRINT("sta_idx invalid sta:%d sn:%d start:%d \n", hw_rxhdr->flags_sta_idx , pmsdu->sn, reord_cntrl->win_start);
+ ecrnx_rx_reord_single_msdu(ecrnx_hw, ecrnx_vif, pmsdu);
+ return ;
+ }
+
+ spin_lock(&reord_cntrl->reord_list_lock);
+ if (ecrnx_rx_reord_sn_check(reord_cntrl, pmsdu->sn)) {
+ //printk("%s discard sn:%d s:%d \n", __func__, pmsdu->sn, reord_cntrl->win_start);
+ //ecrnx_rx_reord_single_msdu(ecrnx_hw, ecrnx_vif, pmsdu);
+ spin_unlock(&reord_cntrl->reord_list_lock);
+ goto discard;
+ }
+ //printk("start:%d %d\n",reord_cntrl->win_start, pmsdu->sn);
+ if (ecrnx_rx_reord_msdu_insert(reord_cntrl, pmsdu)) {
+ spin_unlock(&reord_cntrl->reord_list_lock);
+ goto discard;
+ }
+
+ ecrnx_rx_reord_timer_update(ecrnx_hw, reord_cntrl, false);
+
+ ecrnx_rx_reord_list_flush(ecrnx_hw, ecrnx_vif, reord_cntrl);
+
+ spin_unlock(&reord_cntrl->reord_list_lock);
+
+ return ;
+
+discard:
+ if (pmsdu->skb) {
+ dev_kfree_skb(pmsdu->skb);
+ pmsdu->skb = NULL;
+ }
+
+ ecrnx_rx_reord_msdu_free(&ecrnx_hw->rx_msdu_free_lock, &ecrnx_hw->rx_msdu_free_list, &pmsdu->rx_msdu_list);
+}
+#endif
+
+
+u8 ecrnx_rx_agg_data_ind(struct ecrnx_hw *ecrnx_hw, u16_l status, struct sk_buff* skb, int msdu_offset)
+{
+ struct hw_rxhdr *hw_rxhdr = NULL;
+ struct ecrnx_vif *ecrnx_vif = NULL;
+ hw_rxhdr = (struct hw_rxhdr*)skb->data;
+
+#if 0
+ ECRNX_DBG("[eswin_agg] hw_vect_len: %d , rxu_stat_mm: %d \n", sizeof(struct hw_rxhdr), sizeof(struct rxu_stat_mm));
+ ECRNX_DBG("[eswin_agg] rcv_frm_len: %d \n", hw_rxhdr->hwvect.len);
+ ECRNX_DBG("%s, parttern:0x%2x, status:%d !!", __func__, hw_rxhdr->pattern, status);
+#endif
+ /* Check the pattern */
+ if (hw_rxhdr->pattern != ecrnx_rxbuff_pattern) {
+ dev_err(ecrnx_hw->dev, "RX Buff Pattern not correct, pattern (%x), status %d\n", hw_rxhdr->pattern, status);
+ print_hex_dump(KERN_DEBUG, DBG_PREFIX_PAT, DUMP_PREFIX_NONE, 16, 1, hw_rxhdr, skb->len, false);
+ BUG();
+ }
+
+ /* Check if we need to delete the buffer */
+ if (status & RX_STAT_DELETE) {
+ dev_kfree_skb(skb);
+ goto end;
+ }
+ /* Check if we need to forward the buffer */
+ else if (status & RX_STAT_FORWARD) {
+
+ /* Remove the SK buffer from the rxbuf_elems table. It will also
+ unmap the buffer and then sync the buffer for the cpu */
+ //ecrnx_ipc_rxbuf_elem_pull(ecrnx_hw, skb);
+
+ hw_rxhdr = (struct hw_rxhdr *)skb->data;
+ ecrnx_rx_vector_convert(ecrnx_hw->machw_type,
+ &hw_rxhdr->hwvect.rx_vect1,
+ &hw_rxhdr->hwvect.rx_vect2);
+
+ //skb_reserve(skb, msdu_offset);
+ //ECRNX_DBG("[eswin_agg]before pull skb len: %d \n", skb->len);
+ skb_pull(skb, msdu_offset);
+ //ECRNX_DBG("[eswin_agg]after pull skb len: %d \n", skb->len);
+
+ if (hw_rxhdr->flags_is_80211_mpdu) {
+ //ECRNX_DBG("[eswin_agg]recv mgmt\n");
+ ecrnx_rx_mgmt_any(ecrnx_hw, skb, hw_rxhdr);
+ } else {
+ ecrnx_vif = ecrnx_rx_get_vif(ecrnx_hw, hw_rxhdr->flags_vif_idx);
+ //ECRNX_DBG("[eswin_agg] flags vif idx1:%d, vif: 0x%x \n", hw_rxhdr->flags_vif_idx, ecrnx_vif);
+ if (!ecrnx_vif) {
+ dev_err(ecrnx_hw->dev, "Frame received but no active vif (%d)",
+ hw_rxhdr->flags_vif_idx);
+ dev_kfree_skb(skb);
+ ECRNX_ERR("[agg] check_alloc, %d !!", __LINE__);
+ goto exit_no_free;
+ }
+
+ if (hw_rxhdr->flags_sta_idx != ECRNX_INVALID_STA) {
+ struct ecrnx_sta *sta;
+
+ sta = &ecrnx_hw->sta_table[hw_rxhdr->flags_sta_idx];
+ ecrnx_rx_statistic(ecrnx_hw, hw_rxhdr, sta);
+ //ECRNX_DBG("[eswin_agg] sta idx:%d, vif idx: %d \n", sta->vlan_idx, ecrnx_vif->vif_index);
+ if (sta->vlan_idx != ecrnx_vif->vif_index) {
+ ecrnx_vif = ecrnx_hw->vif_table[sta->vlan_idx];
+ if (!ecrnx_vif) {
+ dev_kfree_skb(skb);
+ ECRNX_ERR("[agg] check_alloc, %d !!", __LINE__);
+ goto exit_no_free;
+ }
+ }
+
+ if (hw_rxhdr->flags_is_4addr && !ecrnx_vif->use_4addr) {
+ cfg80211_rx_unexpected_4addr_frame(ecrnx_vif->ndev,
+ sta->mac_addr, GFP_ATOMIC);
+ }
+ }
+
+ skb->priority = 256 + hw_rxhdr->flags_user_prio;
+ if (!ecrnx_rx_data_skb(ecrnx_hw, ecrnx_vif, skb, hw_rxhdr)){
+ dev_kfree_skb(skb);
+ dev_err(ecrnx_hw->dev, "ecrnx_rx_data_skb_sdio error \n");
+ }
+ }
+ }
+
+exit_no_free:
+ /* Check if we need to allocate a new buffer */
+ //dev_err(ecrnx_hw->dev, "Failed to alloc new RX buf\n");
+
+end:
+ /* Reset and repush descriptor to FW */
+ //sdio_rx_buf_repush(&orignal_rxu_state, &orignal_rx_hd, skb);
+
+ return 0;
+}
+
+static int ecrnx_set_station_info(struct ecrnx_vif *vif, const u8 *mac, struct rx_vector_1 *rx_vect1)
+{
+ struct ecrnx_sta *sta = NULL;
+ static u32 rx_pkts = 0x1ff;
+ static u32 tx_pkts = 0x2ff;
+ static u64 rx_bytes = 0x3ff;
+ static u64 tx_bytes = 0x4ff;
+
+ if (ECRNX_VIF_TYPE(vif) == NL80211_IFTYPE_MONITOR)
+ return -EINVAL;
+ else if ((ECRNX_VIF_TYPE(vif) == NL80211_IFTYPE_STATION) ||
+ (ECRNX_VIF_TYPE(vif) == NL80211_IFTYPE_P2P_CLIENT)) {
+ if (vif->sta.ap && ether_addr_equal(vif->sta.ap->mac_addr, mac))
+ sta = vif->sta.ap;
+ }
+ else
+ {
+ struct ecrnx_sta *sta_iter;
+ list_for_each_entry(sta_iter, &vif->ap.sta_list, list) {
+ if (sta_iter->valid && ether_addr_equal(sta_iter->mac_addr, mac)) {
+ sta = sta_iter;
+ break;
+ }
+ }
+ }
+
+ if (sta)
+ {
+ struct ecrnx_sta_stats *stats = &sta->stats;
+
+ stats->rx_bytes = rx_bytes++;
+ stats->tx_bytes = tx_bytes++;
+ stats->rx_pkts = rx_pkts++;
+ stats->tx_pkts = tx_pkts++;
+ memcpy(&stats->last_rx.rx_vect1, rx_vect1, sizeof(struct rx_vector_1));
+ }
+
+ return 0;
+}
+
+u8 ecrnx_rxdataind(void *pthis, void *hostid)
+{
+ struct ecrnx_hw *ecrnx_hw = pthis;
+ struct hw_rxhdr *hw_rxhdr = NULL;
+ struct ecrnx_vif *ecrnx_vif = NULL;
+ int msdu_offset = sizeof(struct hw_rxhdr);
+ u16_l status;
+
+ struct rxu_stat_mm* orignal_rxu_state = NULL;
+ struct sk_buff *skb = NULL, *frg_ctrl_skb = NULL;
+
+ if(!pthis || !hostid){
+ ECRNX_ERR("ecrnx_rxdataind error! \n");
+ return 0;
+ }
+
+ spin_lock_bh(&ecrnx_hw->rx_lock);
+ ecrnx_hw->data_rx++;
+ skb = (struct sk_buff *)hostid;
+ orignal_rxu_state = (struct rxu_stat_mm*)skb->data;
+ skb_pull(skb, sizeof(struct rxu_stat_mm));
+
+#ifdef CONFIG_ECRNX_ESWIN
+ if (orignal_rxu_state->real_offset)
+ {
+ msdu_offset += orignal_rxu_state->real_offset;
+ //ECRNX_DBG("orignal_rxu_state->real_offset = %hu, msdu_offset:%d \n", orignal_rxu_state->real_offset, msdu_offset);
+ }
+
+ if(orignal_rxu_state->fragment_flag && ((orignal_rxu_state->status & RX_STAT_FORWARD) || (orignal_rxu_state->status & RX_STAT_DELETE))) { //defrag frame
+ struct defrag_elem *defrag_elem_rx = NULL, *next = NULL;
+
+ if(list_empty(&ecrnx_hw->defrag_rx_list)){
+ ECRNX_ERR("error defrag_rx_list is epmty!!! \n");
+ dev_kfree_skb(skb);
+ goto end;
+ }
+
+ ECRNX_DBG("[eswin_agg]%s enter, tid:0x%x, fragment_flag:0x%llx, status:0x%x \n", __func__, orignal_rxu_state->tid, orignal_rxu_state->fragment_flag, orignal_rxu_state->status);
+ if(orignal_rxu_state->status & RX_STAT_DELETE){
+ /*delete all the same tid frame*/
+ list_for_each_entry_safe(defrag_elem_rx, next, &ecrnx_hw->defrag_rx_list, head) {
+ ECRNX_DBG("[eswin_agg]delete: sn1:%d, sn2:%d, tid1:%d, skb:0x%08x \n",defrag_elem_rx->sn, orignal_rxu_state->sn, defrag_elem_rx->tid, defrag_elem_rx->skb);
+ if((defrag_elem_rx->tid == orignal_rxu_state->tid) && (defrag_elem_rx->sn == orignal_rxu_state->sn)){ ///==sn
+ dev_kfree_skb(defrag_elem_rx->skb);
+ list_del(&defrag_elem_rx->head);
+ kfree(defrag_elem_rx);
+ }
+ }
+ dev_kfree_skb(skb);
+ goto end;
+ }
+ else if(orignal_rxu_state->status & RX_STAT_FORWARD){
+ struct defrag_elem *defrag_elem_rx = NULL, *next = NULL;
+ u32_l recv_len = 0, offset_len = 0, hw_rxhdr_len = sizeof(*hw_rxhdr);
+ unsigned char* skb_ptr = NULL;
+ int fg_first = 1;
+ int fg_total_len = 0;
+
+ /*caculate the defrag frames total lens*/
+ list_for_each_entry_safe(defrag_elem_rx, next, &ecrnx_hw->defrag_rx_list, head) {
+ if((defrag_elem_rx->tid == orignal_rxu_state->tid) && (defrag_elem_rx->sn == orignal_rxu_state->sn)){
+ recv_len += defrag_elem_rx->skb->len;
+ }
+ }
+
+ frg_ctrl_skb = skb;
+ /*alloc a new skb, and put the same tid frame to the new skb*/
+ skb = dev_alloc_skb(recv_len);
+
+ list_for_each_entry_safe(defrag_elem_rx, next, &ecrnx_hw->defrag_rx_list, head) {
+ ECRNX_DBG("[eswin_agg]forward: sn1:%d, sn2:%d, tid1:%d, skb:0x%p \n",defrag_elem_rx->sn, orignal_rxu_state->sn, defrag_elem_rx->tid, defrag_elem_rx->skb);
+ if((defrag_elem_rx->tid == orignal_rxu_state->tid) && (defrag_elem_rx->sn == orignal_rxu_state->sn)){
+ if (fg_first) {
+ offset_len = 0;
+ } else {
+ /*first skb should include the hw_rxhdr, other's not need*/
+ offset_len = hw_rxhdr_len;
+ offset_len += sizeof(struct ethhdr);
+ }
+ offset_len += defrag_elem_rx->real_offset;
+ skb_ptr = skb_put(skb, defrag_elem_rx->skb->len - offset_len);
+ if (fg_first) {
+ memcpy(skb_ptr, defrag_elem_rx->skb->data, hw_rxhdr_len);
+ memcpy(skb_ptr + hw_rxhdr_len, defrag_elem_rx->skb->data + hw_rxhdr_len + offset_len, defrag_elem_rx->skb->len - hw_rxhdr_len - offset_len);
+ fg_total_len = defrag_elem_rx->skb->len - hw_rxhdr_len - offset_len;
+ hw_rxhdr = (struct hw_rxhdr*)skb_ptr;
+ } else {
+ memcpy(skb_ptr, defrag_elem_rx->skb->data + offset_len, defrag_elem_rx->skb->len - offset_len);
+ fg_total_len += defrag_elem_rx->skb->len - offset_len;
+ }
+ dev_kfree_skb(defrag_elem_rx->skb);
+ list_del(&defrag_elem_rx->head);
+ kfree(defrag_elem_rx);
+ fg_first = 0;
+ }
+ }
+
+ if (!fg_first) {
+ hw_rxhdr->hwvect.len = fg_total_len;//update len
+ msdu_offset = hw_rxhdr_len;
+ //printk("[llm] sn %d: total %d, skb %x (%d-%x-%x)\n", orignal_rxu_state->sn, fg_total_len, skb, skb->len, skb->data, skb->tail);
+ }
+ }
+ }
+#endif
+
+ hw_rxhdr = (struct hw_rxhdr*)skb->data;
+
+#if 0
+ ECRNX_DBG(" hw_vect_len: %d , rxu_stat_mm: %d \n", sizeof(struct hw_rxhdr), sizeof(struct rxu_stat_mm));
+ ECRNX_DBG(" rcv_frm_len: %d \n", hw_rxhdr->hwvect.len);
+#endif
+
+ status = orignal_rxu_state->status;
+ //ECRNX_DBG("%s, parttern:0x%2x, status:%d !!", __func__, hw_rxhdr->pattern, status);
+ /* Check the pattern */
+ if (hw_rxhdr->pattern != ecrnx_rxbuff_pattern) {
+ dev_err(ecrnx_hw->dev, "RX Buff Pattern not correct, pattern (%x), status %d\n", hw_rxhdr->pattern, status);
+ ECRNX_ERR("RX Buff Pattern not correct, pattern (%x), status %d, skb (%p), skb_len %d\n", hw_rxhdr->pattern, status, skb->data, skb->len);
+ print_hex_dump(KERN_DEBUG, DBG_PREFIX_PAT, DUMP_PREFIX_NONE, 16, 1, hw_rxhdr, skb->len, false);
+ BUG();
+ }
+
+ /* Check if we need to delete the buffer */
+ if (status & RX_STAT_DELETE) {
+ dev_kfree_skb(skb);
+ goto end;
+ }
+
+ /* Check if we need to forward the buffer coming from a monitor interface */
+ if (status & RX_STAT_MONITOR) {
+ struct sk_buff *skb_monitor;
+ struct hw_rxhdr hw_rxhdr_copy;
+ u8 rtap_len;
+ u16 frm_len;
+
+ //Check if monitor interface exists and is open
+ ECRNX_DBG("monitor_vif: %d \n", ecrnx_hw->monitor_vif);
+ ecrnx_vif = ecrnx_rx_get_vif(ecrnx_hw, ecrnx_hw->monitor_vif);
+ if (!ecrnx_vif) {
+ dev_err(ecrnx_hw->dev, "Received monitor frame but there is no monitor interface open\n");
+ dev_kfree_skb(skb);
+ goto check_len_update;
+ }
+
+ ecrnx_rx_vector_convert(ecrnx_hw->machw_type,
+ &hw_rxhdr->hwvect.rx_vect1,
+ &hw_rxhdr->hwvect.rx_vect2);
+ rtap_len = ecrnx_rx_rtap_hdrlen(&hw_rxhdr->hwvect.rx_vect1, false);
+
+ // Move skb->data pointer to MAC Header or Ethernet header
+ skb->data += msdu_offset;
+
+ //Save frame length
+ frm_len = le32_to_cpu(hw_rxhdr->hwvect.len) - msdu_offset;
+
+ // Reserve space for frame
+ skb->len = frm_len;
+
+ if (status == RX_STAT_MONITOR) {
+ //Check if there is enough space to add the radiotap header
+ if (skb_headroom(skb) > rtap_len) {
+
+ skb_monitor = skb;
+
+ //Duplicate the HW Rx Header to override with the radiotap header
+ memcpy(&hw_rxhdr_copy, hw_rxhdr, sizeof(hw_rxhdr_copy));
+
+ } else {
+ //Duplicate the skb and extend the headroom
+ skb_monitor = skb_copy_expand(skb, rtap_len, 0, GFP_ATOMIC);
+
+ //Reset original skb->data pointer
+ skb->data = (void*) hw_rxhdr;
+ }
+ }
+ else
+ {
+#ifdef CONFIG_ECRNX_MON_DATA
+ // Check if MSDU
+ if (!hw_rxhdr->flags_is_80211_mpdu) {
+ // MSDU
+ //Extract MAC header
+ u16 machdr_len = hw_rxhdr->mac_hdr_backup.buf_len;
+ u8* machdr_ptr = hw_rxhdr->mac_hdr_backup.buffer;
+
+ //Pull Ethernet header from skb
+ skb_pull(skb, sizeof(struct ethhdr));
+
+ // Copy skb and extend for adding the radiotap header and the MAC header
+ skb_monitor = skb_copy_expand(skb,
+ rtap_len + machdr_len,
+ 0, GFP_ATOMIC);
+
+ //Reserve space for the MAC Header
+ skb_push(skb_monitor, machdr_len);
+
+ //Copy MAC Header
+ memcpy(skb_monitor->data, machdr_ptr, machdr_len);
+
+ //Update frame length
+ frm_len += machdr_len - sizeof(struct ethhdr);
+ } else {
+ // MPDU
+ skb_monitor = skb_copy_expand(skb, rtap_len, 0, GFP_ATOMIC);
+ }
+
+ //Reset original skb->data pointer
+ skb->data = (void*) hw_rxhdr;
+#else
+ //Reset original skb->data pointer
+ skb->data = (void*) hw_rxhdr;
+
+ wiphy_err(ecrnx_hw->wiphy, "RX status %d is invalid when MON_DATA is disabled\n", status);
+ dev_kfree_skb(skb);
+ goto check_len_update;
+#endif
+ }
+
+ skb_reset_tail_pointer(skb);
+ skb->len = 0;
+ skb_reset_tail_pointer(skb_monitor);
+ skb_monitor->len = 0;
+
+ skb_put(skb_monitor, frm_len);
+ if (ecrnx_rx_monitor(ecrnx_hw, ecrnx_vif, skb_monitor, hw_rxhdr, rtap_len)){
+ dev_kfree_skb(skb);
+ dev_err(ecrnx_hw->dev, "skb monitor handle error \n");
+ }
+
+ if (status == RX_STAT_MONITOR) {
+ status |= RX_STAT_ALLOC;
+ if (skb_monitor != skb) {
+ dev_kfree_skb(skb);
+ dev_err(ecrnx_hw->dev, "skb handle status error \n");
+ }
+ }
+ }
+
+check_len_update:
+ /* Check if we need to update the length */
+ if (status & RX_STAT_LEN_UPDATE) {
+
+ hw_rxhdr = (struct hw_rxhdr *)skb->data;
+
+ hw_rxhdr->hwvect.len = orignal_rxu_state->frame_len - msdu_offset;
+
+ if (status & RX_STAT_ETH_LEN_UPDATE) {
+ /* Update Length Field inside the Ethernet Header */
+ struct ethhdr *hdr = (struct ethhdr *)((u8 *)hw_rxhdr + msdu_offset);
+
+ hdr->h_proto = htons(orignal_rxu_state->frame_len - sizeof(struct ethhdr));
+ }
+ dev_kfree_skb(skb);
+ goto end;
+ }
+
+ /* Check if it must be discarded after informing upper layer */
+ if (status & RX_STAT_SPURIOUS) {
+ struct ieee80211_hdr *hdr;
+
+ /* Read mac header to obtain Transmitter Address */
+ //ecrnx_ipc_rxbuf_elem_sync(ecrnx_hw, skb, msdu_offset + sizeof(*hdr));
+
+ hw_rxhdr = (struct hw_rxhdr *)skb->data;
+ hdr = (struct ieee80211_hdr *)(skb->data + msdu_offset);
+ ecrnx_vif = ecrnx_rx_get_vif(ecrnx_hw, hw_rxhdr->flags_vif_idx);
+ ECRNX_DBG(" flags vif idx:%d, vif: 0x%x \n", hw_rxhdr->flags_vif_idx, ecrnx_vif);
+ if (ecrnx_vif) {
+ cfg80211_rx_spurious_frame(ecrnx_vif->ndev, hdr->addr2, GFP_ATOMIC);
+ }else{
+ dev_kfree_skb(skb);
+ }
+
+ goto end;
+ }
+
+ /* Check if we need to forward the buffer */
+ if (status & RX_STAT_FORWARD) {
+
+ /* Remove the SK buffer from the rxbuf_elems table. It will also
+ unmap the buffer and then sync the buffer for the cpu */
+ //ecrnx_ipc_rxbuf_elem_pull(ecrnx_hw, skb);
+
+ hw_rxhdr = (struct hw_rxhdr *)skb->data;
+ ecrnx_rx_vector_convert(ecrnx_hw->machw_type,
+ &hw_rxhdr->hwvect.rx_vect1,
+ &hw_rxhdr->hwvect.rx_vect2);
+
+ //skb_reserve(skb, msdu_offset);
+ //ECRNX_DBG("before pull skb len: %d, msdu_offset:%d \n", skb->len, msdu_offset);
+ skb_pull(skb, msdu_offset);
+ //ECRNX_DBG("after pull skb len: %d \n", skb->len);
+
+ if (hw_rxhdr->flags_is_80211_mpdu) {
+ //ECRNX_DBG("recv mgmt\n");
+ ecrnx_rx_mgmt_any(ecrnx_hw, skb, hw_rxhdr);
+ } else {
+ ecrnx_vif = ecrnx_rx_get_vif(ecrnx_hw, hw_rxhdr->flags_vif_idx);
+ //ECRNX_DBG(" flags vif idx1:%d, vif: 0x%x \n", hw_rxhdr->flags_vif_idx, ecrnx_vif);
+ if (!ecrnx_vif) {
+ dev_err(ecrnx_hw->dev, "Frame received but no active vif (%d)",
+ hw_rxhdr->flags_vif_idx);
+ dev_kfree_skb(skb);
+ ECRNX_ERR(" check_alloc, %d !!", __LINE__);
+ goto end;
+ }
+
+ if (hw_rxhdr->flags_sta_idx != ECRNX_INVALID_STA) {
+ struct ecrnx_sta *sta;
+
+ sta = &ecrnx_hw->sta_table[hw_rxhdr->flags_sta_idx];
+ ecrnx_rx_statistic(ecrnx_hw, hw_rxhdr, sta);
+ //ECRNX_DBG(" sta idx:%d, vif idx: %d \n", sta->vlan_idx, ecrnx_vif->vif_index);
+ if (sta->vlan_idx != ecrnx_vif->vif_index) {
+ ecrnx_vif = ecrnx_hw->vif_table[sta->vlan_idx];
+ if (!ecrnx_vif) {
+ dev_kfree_skb(skb);
+ ECRNX_ERR(" check_alloc, %d !!", __LINE__);
+ goto end;
+ }
+ }
+
+ ecrnx_set_station_info(ecrnx_vif, (const u8*)sta->mac_addr, &hw_rxhdr->hwvect.rx_vect1);
+ if (hw_rxhdr->flags_is_4addr && !ecrnx_vif->use_4addr) {
+ cfg80211_rx_unexpected_4addr_frame(ecrnx_vif->ndev,
+ sta->mac_addr, GFP_ATOMIC);
+ }
+ }
+
+ skb->priority = 256 + hw_rxhdr->flags_user_prio;
+#ifdef CONFIG_ESWIN_RX_REORDER
+ //printk("sn:%d %d %d %d l:%d\n",orignal_rxu_state->is_qos, orignal_rxu_state->need_reord, orignal_rxu_state->need_pn_check,orignal_rxu_state->sn, skb->len -8);
+ if(orignal_rxu_state->is_qos && orignal_rxu_state->need_reord) {
+ ecrnx_rx_reord_check(ecrnx_hw, ecrnx_vif, hw_rxhdr, skb, orignal_rxu_state);
+ }else if(orignal_rxu_state->is_qos && !orignal_rxu_state->need_reord) {
+ ecrnx_rx_reord_tid_flush(ecrnx_hw, ecrnx_vif, skb, hw_rxhdr->flags_sta_idx,orignal_rxu_state->tid);
+ if (orignal_rxu_state->need_pn_check)
+ {
+ if (!ecrnx_rx_data_pn_check(ecrnx_hw, skb, hw_rxhdr->flags_vif_idx, hw_rxhdr->flags_sta_idx, orignal_rxu_state->tid, orignal_rxu_state->is_ga))
+ {
+ ECRNX_DBG("rx_data_check_pn error\n");
+ dev_kfree_skb(skb);
+ goto end;
+ }
+ skb_pull(skb, 8);
+ }
+ if (!ecrnx_rx_data_skb(ecrnx_hw, ecrnx_vif, skb, hw_rxhdr))
+ dev_kfree_skb(skb);
+ }else {
+ if (orignal_rxu_state->need_pn_check)
+ {
+ if (!ecrnx_rx_data_pn_check(ecrnx_hw, skb, hw_rxhdr->flags_vif_idx, hw_rxhdr->flags_sta_idx, orignal_rxu_state->tid, orignal_rxu_state->is_ga))
+ {
+ ECRNX_DBG("rx_data_check_pn error\n");
+ dev_kfree_skb(skb);
+ goto end;
+ }
+ skb_pull(skb, 8);
+ }
+ if (!ecrnx_rx_data_skb(ecrnx_hw, ecrnx_vif, skb, hw_rxhdr))
+ dev_kfree_skb(skb);
+ }
+#else
+ if (!ecrnx_rx_data_skb(ecrnx_hw, ecrnx_vif, skb, hw_rxhdr)){
+ dev_kfree_skb(skb);
+ dev_err(ecrnx_hw->dev, "ecrnx_rx_data_skb_sdio error \n");
+ }
+#endif
+ }
+ goto end;
+ }
+
+ else if (status & RX_STAT_ALLOC) {
+ /*agg frame*/
+ if (hw_rxhdr->flags_sta_idx != ECRNX_INVALID_STA) {
+ if (orignal_rxu_state->need_pn_check)
+ {
+ if (!ecrnx_rx_data_pn_check(ecrnx_hw, skb, hw_rxhdr->flags_vif_idx, hw_rxhdr->flags_sta_idx, orignal_rxu_state->tid, orignal_rxu_state->is_ga))
+ {
+ dev_err(ecrnx_hw->dev, "rx_data_check_pn error \n");
+ dev_kfree_skb(skb);
+ goto end;
+ }
+ skb_pull(skb, 8);
+ }
+ }
+
+ if(orignal_rxu_state->fragment_flag){
+ struct defrag_elem* defrag_elem_rx = (struct defrag_elem*)kzalloc(sizeof(struct defrag_elem), GFP_ATOMIC);
+
+ if(defrag_elem_rx){
+ defrag_elem_rx->tid = orignal_rxu_state->tid;
+ defrag_elem_rx->sn = orignal_rxu_state->sn;
+ defrag_elem_rx->real_offset = orignal_rxu_state->real_offset;
+ defrag_elem_rx->skb = skb;
+ ECRNX_DBG("ecrnx_rxdataind:insert_skb:0x%08x, sn:%d, tid:%d, sn:%d \n", skb, orignal_rxu_state->tid, orignal_rxu_state->sn);
+ list_add_tail(&defrag_elem_rx->head, &ecrnx_hw->defrag_rx_list);
+ }else{
+ ECRNX_ERR("no buffer !!!! \n");
+ }
+ }
+ //ECRNX_DBG("status set alloc\n");
+ }
+
+end:
+ if(frg_ctrl_skb){
+ dev_kfree_skb(frg_ctrl_skb);
+ }
+ spin_unlock_bh(&ecrnx_hw->rx_lock);
+ return 0;
+}
+#endif
+
+#if 0
+static void sdio_rx_debug(struct sk_buff *skb)
+{
+ int i;
+
+ if(!skb || !skb->len){
+ ECRNX_DBG("skb error \n");
+ }
+
+ ECRNX_DBG("%s, len: 0x%x \n", __func__, skb->len);
+ for(i = 0; i< skb->len; i++){
+ printk("0x%02x ", skb->data[i]);
+ if (i && (i % 16) == 0) {
+ printk("\n");
+ }
+ }
+}
+#endif
+
+int ecrnx_data_cfm_callback(void *priv, void *host_id)
+{
+ struct ecrnx_hw *ecrnx_hw = (struct ecrnx_hw*)priv;
+ struct ipc_host_env_tag *env;
+ env = ecrnx_hw->ipc_env;
+
+ ecrnx_hw->data_tx_done++;
+ if(!env || !env->pthis || host_id == 0)
+ {
+ ECRNX_ERR("ecrnx_data_cfm_callback input param error!! \n!");
+ return -1;
+ }
+ spin_lock_bh(&((struct ecrnx_hw *)env->pthis)->tx_lock);
+ env->cb.handle_data_cfm(env->pthis, host_id);
+ spin_unlock_bh(&((struct ecrnx_hw *)env->pthis)->tx_lock);
+ return 0;
+}
+
+int ecrnx_msg_cfm_callback(void *priv, void *host_id)
+{
+ struct ecrnx_hw *ecrnx_hw = (struct ecrnx_hw*)priv;
+ struct ipc_host_env_tag *env;
+ env = ecrnx_hw->ipc_env;
+
+ if(!env || !env->pthis || host_id == 0)
+ {
+ ECRNX_ERR("ecrnx_msg_cfm_callback input param error!! \n!");
+ return -1;
+ }
+
+ env->msga2e_hostid = NULL;
+ ecrnx_hw->cmd_mgr.llind(&ecrnx_hw->cmd_mgr, (struct ecrnx_cmd *)host_id);
+ return 0;
+}
+
+#ifdef CONFIG_ECRNX_ESWIN_SDIO
+int ecrnx_rx_callback(void *priv, struct sk_buff *skb)
+#elif defined(CONFIG_ECRNX_ESWIN_USB)
+extern void usb_skb_debug(struct sk_buff *skb);
+int ecrnx_rx_callback(void *priv, struct sk_buff *skb, uint8_t endpoint)
+#endif
+{
+ //struct sk_buff *ret_skb = NULL;
+ struct ecrnx_hw *ecrnx_hw = (struct ecrnx_hw*)priv;
+ uint32_t frm_type = 0;
+ //ECRNX_DBG("%s enter, skb_len: %d, skb: 0x%x !!", __func__, skb->len, skb);
+
+ //print_hex_dump(KERN_DEBUG, "rx skb: ", DUMP_PREFIX_NONE, 16, 1, skb->data, skb->len, false);
+
+ if(!skb || !skb->data)
+ {
+ ECRNX_PRINT("sdio rx err error \n");
+ }
+
+ //usb_rx_debug(skb);
+#ifdef CONFIG_ECRNX_ESWIN_SDIO
+ frm_type = (skb->data[11] << 24) | (skb->data[10] << 16) | (skb->data[9] << 8) | skb->data[8];
+#elif defined(CONFIG_ECRNX_ESWIN_USB)
+ frm_type = (skb->data[3] << 24) | (skb->data[2] << 16) | (skb->data[1] << 8) | skb->data[0];
+#endif
+
+ //ECRNX_DBG(" frame_type: 0x%x, frame_len: %d, ecrnx_hw: 0x%x", frm_type, skb->len, ecrnx_hw);
+
+#if defined(CONFIG_ECRNX_ESWIN_SDIO)
+#ifdef CONFIG_ECRNX_WIFO_CAIL
+ if (amt_mode == true) {
+ sdio_host_amt_rx_handler(frm_type, skb);
+ }
+ else
+#endif
+ sdio_host_rx_handler( frm_type, ecrnx_hw->ipc_env, skb);
+#elif defined(CONFIG_ECRNX_ESWIN_USB)
+ if(MSG_TYPE_DATA == endpoint)
+ {
+ //msg is USB_FRM_TYPE_RXDESC
+ usb_host_rx_handler( USB_FRM_TYPE_RXDESC, ecrnx_hw->ipc_env, skb); //current just used a point
+ }
+ else if(MSG_TYPE_CTRL == endpoint)
+ {
+#ifdef CONFIG_ECRNX_WIFO_CAIL
+ if (amt_mode == true) {
+ usb_host_amt_rx_handler(frm_type, skb);
+ }
+ else
+#endif
+ usb_host_rx_handler( frm_type, ecrnx_hw->ipc_env, skb);
+ }
+ else
+ {
+ ECRNX_ERR("endopint error \n");
+ }
+#endif
+ //ECRNX_DBG("%s exit!!", __func__);
+ return 0;
+}
diff --git a/drivers/net/wireless/eswin/fullmac/ecrnx_rx.h b/drivers/net/wireless/eswin/fullmac/ecrnx_rx.h
new file mode 100644
index 000000000000..c84cd8b871ed
--- /dev/null
+++ b/drivers/net/wireless/eswin/fullmac/ecrnx_rx.h
@@ -0,0 +1,125 @@
+/**
+ ******************************************************************************
+ *
+ * @file ecrnx_rx.h
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+#ifndef _ECRNX_RX_H_
+#define _ECRNX_RX_H_
+
+#include <linux/workqueue.h>
+#include "hal_desc.h"
+enum rx_status_bits
+{
+ /// The buffer can be forwarded to the networking stack
+ RX_STAT_FORWARD = 1 << 0,
+ /// A new buffer has to be allocated
+ RX_STAT_ALLOC = 1 << 1,
+ /// The buffer has to be deleted
+ RX_STAT_DELETE = 1 << 2,
+ /// The length of the buffer has to be updated
+ RX_STAT_LEN_UPDATE = 1 << 3,
+ /// The length in the Ethernet header has to be updated
+ RX_STAT_ETH_LEN_UPDATE = 1 << 4,
+ /// Simple copy
+ RX_STAT_COPY = 1 << 5,
+ /// Spurious frame (inform upper layer and discard)
+ RX_STAT_SPURIOUS = 1 << 6,
+ /// packet for monitor interface
+ RX_STAT_MONITOR = 1 << 7,
+};
+
+#if defined(CONFIG_ECRNX_ESWIN_USB)
+typedef enum
+{
+ MSG_TYPE_DATA = 1,
+ MSG_TYPE_CTRL
+}MSG_TYPE_E;
+#endif
+
+/*
+ * Decryption status subfields.
+ * {
+ */
+// @}
+
+#define _ASOCREQ_IE_OFFSET_ 4 /* excluding wlan_hdr */
+#define _REASOCREQ_IE_OFFSET_ 10
+#define STATION_INFO_ASSOC_REQ_IES 0
+#define WLAN_HDR_A3_LEN 24
+#define get_addr2_ptr(pbuf) ((unsigned char *)((unsigned int)(pbuf) + 10))
+
+/* keep it same with the FW */
+#define RX_CNTRL_REORD_WIN_SIZE 42
+#ifdef CONFIG_ECRNX_MON_DATA
+#define RX_MACHDR_BACKUP_LEN 64
+/// MAC header backup descriptor
+struct mon_machdrdesc
+{
+ /// Length of the buffer
+ u32 buf_len;
+ /// Buffer containing mac header, LLC and SNAP
+ u8 buffer[RX_MACHDR_BACKUP_LEN];
+};
+#endif
+
+struct hw_rxhdr {
+ /** RX vector */
+ struct hw_vect hwvect;
+
+ /** PHY channel information */
+ struct phy_channel_info_desc phy_info;
+
+ /** RX flags */
+ u32 flags_is_amsdu : 1;
+ u32 flags_is_80211_mpdu: 1;
+ u32 flags_is_4addr : 1;
+ u32 flags_new_peer : 1;
+ u32 flags_user_prio : 3;
+ u32 flags_rsvd0 : 1;
+ u32 flags_vif_idx : 8; // 0xFF if invalid VIF index
+ u32 flags_sta_idx : 8; // 0xFF if invalid STA index
+ u32 flags_dst_idx : 8; // 0xFF if unknown destination STA
+#ifdef CONFIG_ECRNX_MON_DATA
+ /// MAC header backup descriptor (used only for MSDU when there is a monitor and a data interface)
+ struct mon_machdrdesc mac_hdr_backup;
+#endif
+ /** Pattern indicating if the buffer is available for the driver */
+ u32 pattern;
+};
+
+struct ecrnx_defer_rx {
+ struct sk_buff_head sk_list;
+ struct work_struct work;
+};
+
+/**
+ * struct ecrnx_defer_rx_cb - Control buffer for deferred buffers
+ *
+ * @vif: VIF that received the buffer
+ */
+struct ecrnx_defer_rx_cb {
+ struct ecrnx_vif *vif;
+};
+
+u8 ecrnx_unsup_rx_vec_ind(void *pthis, void *hostid);
+u8 ecrnx_rxdataind(void *pthis, void *hostid);
+void ecrnx_rx_deferred(struct work_struct *ws);
+void ecrnx_rx_defer_skb(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *ecrnx_vif,
+ struct sk_buff *skb);
+#ifdef CONFIG_ECRNX_ESWIN_SDIO
+int ecrnx_rx_callback(void *priv, struct sk_buff *skb);
+#elif defined(CONFIG_ECRNX_ESWIN_USB)
+int ecrnx_rx_callback(void *priv, struct sk_buff *skb, uint8_t endpoint);
+#endif
+
+void ecrnx_rx_reord_deinit(struct ecrnx_hw *ecrnx_hw);
+void ecrnx_rx_reord_init(struct ecrnx_hw *ecrnx_hw);
+void ecrnx_rx_reord_sta_init(struct ecrnx_hw* ecrnx_hw, struct ecrnx_vif *ecrnx_vif, u8 sta_idx);
+void ecrnx_rx_reord_sta_deinit(struct ecrnx_hw* ecrnx_hw, u8 sta_idx, bool is_del);
+
+
+#endif /* _ECRNX_RX_H_ */
diff --git a/drivers/net/wireless/eswin/fullmac/ecrnx_tdls.c b/drivers/net/wireless/eswin/fullmac/ecrnx_tdls.c
new file mode 100644
index 000000000000..7fe1ff2ce736
--- /dev/null
+++ b/drivers/net/wireless/eswin/fullmac/ecrnx_tdls.c
@@ -0,0 +1,788 @@
+/**
+ ******************************************************************************
+ *
+ * @file ecrnx_tx.c
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+
+/**
+ * INCLUDE FILES
+ ******************************************************************************
+ */
+
+#include "ecrnx_tdls.h"
+#include "ecrnx_compat.h"
+
+/**
+ * FUNCTION DEFINITIONS
+ ******************************************************************************
+ */
+
+static u16
+ecrnx_get_tdls_sta_capab(struct ecrnx_vif *ecrnx_vif, u16 status_code)
+{
+ u16 capab = 0;
+
+ /* The capability will be 0 when sending a failure code */
+ if (status_code != 0)
+ return capab;
+
+ if (ecrnx_vif->sta.ap->band != NL80211_BAND_2GHZ)
+ return capab;
+
+ capab |= WLAN_CAPABILITY_SHORT_SLOT_TIME;
+ capab |= WLAN_CAPABILITY_SHORT_PREAMBLE;
+
+ return capab;
+}
+
+static int
+ecrnx_tdls_prepare_encap_data(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *ecrnx_vif,
+ const u8 *peer, u8 action_code, u8 dialog_token,
+ u16 status_code, struct sk_buff *skb)
+{
+ struct ieee80211_tdls_data *tf;
+ tf = (void *)skb_put(skb, sizeof(struct ieee80211_tdls_data) - sizeof(tf->u));
+
+ // set eth header
+ memcpy(tf->da, peer, ETH_ALEN);
+ memcpy(tf->sa, ecrnx_hw->wiphy->perm_addr, ETH_ALEN);
+ tf->ether_type = cpu_to_be16(ETH_P_TDLS);
+
+ // set common TDLS info
+ tf->payload_type = WLAN_TDLS_SNAP_RFTYPE;
+ tf->category = WLAN_CATEGORY_TDLS;
+ tf->action_code = action_code;
+
+ // set action specific TDLS info
+ switch (action_code) {
+ case WLAN_TDLS_SETUP_REQUEST:
+ skb_put(skb, sizeof(tf->u.setup_req));
+ tf->u.setup_req.dialog_token = dialog_token;
+ tf->u.setup_req.capability =
+ cpu_to_le16(ecrnx_get_tdls_sta_capab(ecrnx_vif, status_code));
+ break;
+
+ case WLAN_TDLS_SETUP_RESPONSE:
+ skb_put(skb, sizeof(tf->u.setup_resp));
+ tf->u.setup_resp.status_code = cpu_to_le16(status_code);
+ tf->u.setup_resp.dialog_token = dialog_token;
+ tf->u.setup_resp.capability =
+ cpu_to_le16(ecrnx_get_tdls_sta_capab(ecrnx_vif, status_code));
+ break;
+
+ case WLAN_TDLS_SETUP_CONFIRM:
+ skb_put(skb, sizeof(tf->u.setup_cfm));
+ tf->u.setup_cfm.status_code = cpu_to_le16(status_code);
+ tf->u.setup_cfm.dialog_token = dialog_token;
+ break;
+
+ case WLAN_TDLS_TEARDOWN:
+ skb_put(skb, sizeof(tf->u.teardown));
+ tf->u.teardown.reason_code = cpu_to_le16(status_code);
+ break;
+
+ case WLAN_TDLS_DISCOVERY_REQUEST:
+ skb_put(skb, sizeof(tf->u.discover_req));
+ tf->u.discover_req.dialog_token = dialog_token;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int
+ecrnx_prep_tdls_direct(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *ecrnx_vif,
+ const u8 *peer, u8 action_code, u8 dialog_token,
+ u16 status_code, struct sk_buff *skb)
+{
+ struct ieee80211_mgmt *mgmt;
+
+ mgmt = (void *)skb_put(skb, 24);
+ memset(mgmt, 0, 24);
+ memcpy(mgmt->da, peer, ETH_ALEN);
+ memcpy(mgmt->sa, ecrnx_hw->wiphy->perm_addr, ETH_ALEN);
+ memcpy(mgmt->bssid, ecrnx_vif->sta.ap->mac_addr, ETH_ALEN);
+
+ mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
+ IEEE80211_STYPE_ACTION);
+
+ switch (action_code) {
+ case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
+ skb_put(skb, 1 + sizeof(mgmt->u.action.u.tdls_discover_resp));
+ mgmt->u.action.category = WLAN_CATEGORY_PUBLIC;
+ mgmt->u.action.u.tdls_discover_resp.action_code = WLAN_PUB_ACTION_TDLS_DISCOVER_RES;
+ mgmt->u.action.u.tdls_discover_resp.dialog_token = dialog_token;
+ mgmt->u.action.u.tdls_discover_resp.capability =
+ cpu_to_le16(ecrnx_get_tdls_sta_capab(ecrnx_vif, status_code));
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int
+ecrnx_add_srates_ie(struct ecrnx_hw *ecrnx_hw, struct sk_buff *skb)
+{
+ u8 i, rates, *pos;
+ int rate;
+ struct ieee80211_supported_band *ecrnx_band_2GHz = ecrnx_hw->wiphy->bands[NL80211_BAND_2GHZ];
+
+ rates = 8;
+
+ if (skb_tailroom(skb) < rates + 2)
+ return -ENOMEM;
+
+ pos = skb_put(skb, rates + 2);
+ *pos++ = WLAN_EID_SUPP_RATES;
+ *pos++ = rates;
+ for (i = 0; i < rates; i++) {
+ rate = ecrnx_band_2GHz->bitrates[i].bitrate;
+ rate = DIV_ROUND_UP(rate, 5);
+ *pos++ = (u8)rate;
+ }
+
+ return 0;
+}
+
+static int
+ecrnx_add_ext_srates_ie(struct ecrnx_hw *ecrnx_hw, struct sk_buff *skb)
+{
+ u8 i, exrates, *pos;
+ int rate;
+ struct ieee80211_supported_band *ecrnx_band_2GHz = ecrnx_hw->wiphy->bands[NL80211_BAND_2GHZ];
+
+ exrates = ecrnx_band_2GHz->n_bitrates - 8;
+
+ if (skb_tailroom(skb) < exrates + 2)
+ return -ENOMEM;
+
+ pos = skb_put(skb, exrates + 2);
+ *pos++ = WLAN_EID_EXT_SUPP_RATES;
+ *pos++ = exrates;
+ for (i = 8; i < (8+exrates); i++) {
+ rate = ecrnx_band_2GHz->bitrates[i].bitrate;
+ rate = DIV_ROUND_UP(rate, 5);
+ *pos++ = (u8)rate;
+ }
+
+ return 0;
+}
+
+static void
+ecrnx_tdls_add_supp_channels(struct ecrnx_hw *ecrnx_hw, struct sk_buff *skb)
+{
+ /*
+ * Add possible channels for TDLS. These are channels that are allowed
+ * to be active.
+ */
+ u8 subband_cnt = 0;
+ u8 *pos_subband;
+ u8 *pos = skb_put(skb, 2);
+ struct ieee80211_supported_band *ecrnx_band_2GHz = ecrnx_hw->wiphy->bands[NL80211_BAND_2GHZ];
+#ifdef CONFIG_ECRNX_5G
+ struct ieee80211_supported_band *ecrnx_band_5GHz = ecrnx_hw->wiphy->bands[NL80211_BAND_5GHZ];
+#endif
+
+ *pos++ = WLAN_EID_SUPPORTED_CHANNELS;
+
+ /*
+ * 5GHz and 2GHz channels numbers can overlap. Ignore this for now, as
+ * this doesn't happen in real world scenarios.
+ */
+
+ /* 2GHz, with 5MHz spacing */
+ pos_subband = skb_put(skb, 2);
+ if (ecrnx_band_2GHz->n_channels > 0)
+ {
+ *pos_subband++ = ieee80211_frequency_to_channel(ecrnx_band_2GHz->channels[0].center_freq);
+ *pos_subband++ = ecrnx_band_2GHz->n_channels;
+ subband_cnt++;
+ }
+
+#ifdef CONFIG_ECRNX_5G
+ /* 5GHz, with 20MHz spacing */
+ pos_subband = skb_put(skb, 2);
+ if (ecrnx_band_5GHz->n_channels > 0)
+ {
+ *pos_subband++ = ieee80211_frequency_to_channel(ecrnx_band_5GHz->channels[0].center_freq);
+ *pos_subband++ = ecrnx_band_5GHz->n_channels;
+ subband_cnt++;
+ }
+#endif
+
+ /* length */
+ *pos = 2 * subband_cnt;
+}
+
+static void
+ecrnx_tdls_add_ext_capab(struct ecrnx_hw *ecrnx_hw, struct sk_buff *skb)
+{
+ u8 *pos = (void *)skb_put(skb, 7);
+ bool chan_switch = ecrnx_hw->wiphy->features &
+ NL80211_FEATURE_TDLS_CHANNEL_SWITCH;
+
+ *pos++ = WLAN_EID_EXT_CAPABILITY;
+ *pos++ = 5; /* len */
+ *pos++ = 0x0;
+ *pos++ = 0x0;
+ *pos++ = 0x0;
+ *pos++ = WLAN_EXT_CAPA4_TDLS_BUFFER_STA |
+ (chan_switch ? WLAN_EXT_CAPA4_TDLS_CHAN_SWITCH : 0);
+ *pos++ = WLAN_EXT_CAPA5_TDLS_ENABLED;
+}
+
+static void
+ecrnx_add_wmm_info_ie(struct sk_buff *skb, u8 qosinfo)
+{
+ u8 *pos = (void *)skb_put(skb, 9);
+
+ *pos++ = WLAN_EID_VENDOR_SPECIFIC;
+ *pos++ = 7; /* len */
+ *pos++ = 0x00; /* Microsoft OUI 00:50:F2 */
+ *pos++ = 0x50;
+ *pos++ = 0xf2;
+ *pos++ = 2; /* WME */
+ *pos++ = 0; /* WME info */
+ *pos++ = 1; /* WME ver */
+ *pos++ = qosinfo; /* U-APSD no in use */
+}
+
+/* translate numbering in the WMM parameter IE to the mac80211 notation */
+static u8 ecrnx_ac_from_wmm(int ac)
+{
+ switch (ac) {
+ case 0:
+ return AC_BE;
+ case 1:
+ return AC_BK;
+ case 2:
+ return AC_VI;
+ case 3:
+ return AC_VO;
+ default:
+ WARN_ON_ONCE(1);
+ }
+ return -1;
+}
+
+static void
+ecrnx_add_wmm_param_ie(struct sk_buff *skb, u8 acm_bits, u32 *ac_params)
+{
+ struct ieee80211_wmm_param_ie *wmm;
+ int i, j;
+ u8 cw_min, cw_max;
+ bool acm;
+
+ wmm = (void *)skb_put(skb, sizeof(struct ieee80211_wmm_param_ie));
+ memset(wmm, 0, sizeof(*wmm));
+
+ wmm->element_id = WLAN_EID_VENDOR_SPECIFIC;
+ wmm->len = sizeof(*wmm) - 2;
+
+ wmm->oui[0] = 0x00; /* Microsoft OUI 00:50:F2 */
+ wmm->oui[1] = 0x50;
+ wmm->oui[2] = 0xf2;
+ wmm->oui_type = 2; /* WME */
+ wmm->oui_subtype = 1; /* WME param */
+ wmm->version = 1; /* WME ver */
+ wmm->qos_info = 0; /* U-APSD not in use */
+
+ /*
+ * Use the EDCA parameters defined for the BSS, or default if the AP
+ * doesn't support it, as mandated by 802.11-2012 section 10.22.4
+ */
+ for (i = 0; i < AC_MAX; i++) {
+ j = ecrnx_ac_from_wmm(i);
+ cw_min = (ac_params[j] & 0xF0 ) >> 4;
+ cw_max = (ac_params[j] & 0xF00 ) >> 8;
+ acm = (acm_bits & (1 << j)) != 0;
+
+ wmm->ac[i].aci_aifsn = (i << 5) | (acm << 4) | (ac_params[j] & 0xF);
+ wmm->ac[i].cw = (cw_max << 4) | cw_min;
+ wmm->ac[i].txop_limit = (ac_params[j] & 0x0FFFF000 ) >> 12;
+ }
+}
+
+static void
+ecrnx_tdls_add_oper_classes(struct ecrnx_vif *ecrnx_vif, struct sk_buff *skb)
+{
+ u8 *pos;
+ u8 op_class;
+ struct cfg80211_chan_def chan_def;
+ struct ieee80211_channel chan;
+
+ chan.band = ecrnx_vif->sta.ap->band;
+ chan.center_freq = ecrnx_vif->sta.ap->center_freq;
+ chan_def.chan = &chan;
+ chan_def.width = ecrnx_vif->sta.ap->width;
+ chan_def.center_freq1 = ecrnx_vif->sta.ap->center_freq1;
+ chan_def.center_freq2 = ecrnx_vif->sta.ap->center_freq2;
+
+ if (!ieee80211_chandef_to_operating_class(&chan_def, &op_class))
+ return;
+
+ pos = skb_put(skb, 4);
+ *pos++ = WLAN_EID_SUPPORTED_REGULATORY_CLASSES;
+ *pos++ = 2; /* len */
+
+ // current op class
+ *pos++ = op_class;
+ *pos++ = op_class; /* give current operating class as alternate too */
+
+ // need to add 5GHz classes?
+}
+
+static void
+ecrnx_ie_build_ht_cap(struct sk_buff *skb, struct ieee80211_sta_ht_cap *ht_cap,
+ u16 cap)
+{
+ u8 *pos;
+ __le16 tmp;
+
+ pos = skb_put(skb, sizeof(struct ieee80211_ht_cap) + 2);
+ *pos++ = WLAN_EID_HT_CAPABILITY;
+ *pos++ = sizeof(struct ieee80211_ht_cap);
+ memset(pos, 0, sizeof(struct ieee80211_ht_cap));
+
+ /* capability flags */
+ tmp = cpu_to_le16(cap);
+ memcpy(pos, &tmp, sizeof(u16));
+ pos += sizeof(u16);
+
+ /* AMPDU parameters */
+ *pos++ = ht_cap->ampdu_factor |
+ (ht_cap->ampdu_density <<
+ IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT);
+
+ /* MCS set */
+ memcpy(pos, &ht_cap->mcs, sizeof(ht_cap->mcs));
+ pos += sizeof(ht_cap->mcs);
+
+ /* extended capabilities */
+ pos += sizeof(__le16);
+
+ /* BF capabilities */
+ pos += sizeof(__le32);
+
+ /* antenna selection */
+ pos += sizeof(u8);
+}
+
+static void
+ecrnx_ie_build_vht_cap(struct sk_buff *skb, struct ieee80211_sta_vht_cap *vht_cap,
+ u32 cap)
+{
+ u8 *pos;
+ __le32 tmp;
+
+ pos = skb_put(skb, 14);
+
+ *pos++ = WLAN_EID_VHT_CAPABILITY;
+ *pos++ = sizeof(struct ieee80211_vht_cap);
+ memset(pos, 0, sizeof(struct ieee80211_vht_cap));
+
+ /* capability flags */
+ tmp = cpu_to_le32(cap);
+ memcpy(pos, &tmp, sizeof(u32));
+ pos += sizeof(u32);
+
+ /* VHT MCS set */
+ memcpy(pos, &vht_cap->vht_mcs, sizeof(vht_cap->vht_mcs));
+ pos += sizeof(vht_cap->vht_mcs);
+}
+
+static void
+ecrnx_tdls_add_bss_coex_ie(struct sk_buff *skb)
+{
+ u8 *pos = (void *)skb_put(skb, 3);
+
+ *pos++ = WLAN_EID_BSS_COEX_2040;
+ *pos++ = 1; /* len */
+
+ *pos++ = WLAN_BSS_COEX_INFORMATION_REQUEST;
+}
+
+static void
+ecrnx_tdls_add_link_ie(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *ecrnx_vif,
+ struct sk_buff *skb, const u8 *peer,
+ bool initiator)
+{
+ struct ieee80211_tdls_lnkie *lnkid;
+ const u8 *init_addr, *rsp_addr;
+
+ if (initiator) {
+ init_addr = ecrnx_hw->wiphy->perm_addr;
+ rsp_addr = peer;
+ } else {
+ init_addr = peer;
+ rsp_addr = ecrnx_hw->wiphy->perm_addr;
+ }
+
+ lnkid = (void *)skb_put(skb, sizeof(struct ieee80211_tdls_lnkie));
+
+ lnkid->ie_type = WLAN_EID_LINK_ID;
+ lnkid->ie_len = sizeof(struct ieee80211_tdls_lnkie) - 2;
+
+ memcpy(lnkid->bssid, ecrnx_vif->sta.ap->mac_addr, ETH_ALEN);
+ memcpy(lnkid->init_sta, init_addr, ETH_ALEN);
+ memcpy(lnkid->resp_sta, rsp_addr, ETH_ALEN);
+}
+
+static void
+ecrnx_tdls_add_aid_ie(struct ecrnx_vif *ecrnx_vif, struct sk_buff *skb)
+{
+ u8 *pos = (void *)skb_put(skb, 4);
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)
+ *pos++ = WLAN_EID_AID;
+#else
+ *pos++ = 197;
+#endif
+ *pos++ = 2; /* len */
+ *pos++ = ecrnx_vif->sta.ap->aid;
+}
+
+static u8 *
+ecrnx_ie_build_ht_oper(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *ecrnx_vif,
+ u8 *pos, struct ieee80211_sta_ht_cap *ht_cap,
+ u16 prot_mode)
+{
+ struct ieee80211_ht_operation *ht_oper;
+ /* Build HT Information */
+ *pos++ = WLAN_EID_HT_OPERATION;
+ *pos++ = sizeof(struct ieee80211_ht_operation);
+ ht_oper = (struct ieee80211_ht_operation *)pos;
+ ht_oper->primary_chan = ieee80211_frequency_to_channel(
+ ecrnx_vif->sta.ap->center_freq);
+ switch (ecrnx_vif->sta.ap->width) {
+ case NL80211_CHAN_WIDTH_160:
+ case NL80211_CHAN_WIDTH_80P80:
+ case NL80211_CHAN_WIDTH_80:
+ case NL80211_CHAN_WIDTH_40:
+ if (ecrnx_vif->sta.ap->center_freq1 > ecrnx_vif->sta.ap->center_freq)
+ ht_oper->ht_param = IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
+ else
+ ht_oper->ht_param = IEEE80211_HT_PARAM_CHA_SEC_BELOW;
+ break;
+ default:
+ ht_oper->ht_param = IEEE80211_HT_PARAM_CHA_SEC_NONE;
+ break;
+ }
+ if (ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 &&
+ ecrnx_vif->sta.ap->width != NL80211_CHAN_WIDTH_20_NOHT &&
+ ecrnx_vif->sta.ap->width != NL80211_CHAN_WIDTH_20)
+ ht_oper->ht_param |= IEEE80211_HT_PARAM_CHAN_WIDTH_ANY;
+
+ ht_oper->operation_mode = cpu_to_le16(prot_mode);
+ ht_oper->stbc_param = 0x0000;
+
+ /* It seems that Basic MCS set and Supported MCS set
+ are identical for the first 10 bytes */
+ memset(&ht_oper->basic_set, 0, 16);
+ memcpy(&ht_oper->basic_set, &ht_cap->mcs, 10);
+
+ return pos + sizeof(struct ieee80211_ht_operation);
+}
+
+static u8 *
+ecrnx_ie_build_vht_oper(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *ecrnx_vif,
+ u8 *pos, struct ieee80211_sta_ht_cap *ht_cap,
+ u16 prot_mode)
+{
+ struct ieee80211_vht_operation *vht_oper;
+ /* Build HT Information */
+ *pos++ = WLAN_EID_VHT_OPERATION;
+ *pos++ = sizeof(struct ieee80211_vht_operation);
+ vht_oper = (struct ieee80211_vht_operation *)pos;
+
+ switch (ecrnx_vif->sta.ap->width) {
+ case NL80211_CHAN_WIDTH_80:
+ vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_80MHZ; // Channel Width
+ CCFS0(vht_oper) =
+ ieee80211_frequency_to_channel(ecrnx_vif->sta.ap->center_freq); // Channel Center Frequency Segment 0
+ CCFS1(vht_oper) = 0; // Channel Center Frequency Segment 1 (N.A.)
+ break;
+ case NL80211_CHAN_WIDTH_160:
+ vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_160MHZ; // Channel Width
+ CCFS0(vht_oper) =
+ ieee80211_frequency_to_channel(ecrnx_vif->sta.ap->center_freq); // Channel Center Frequency Segment 0
+ CCFS1(vht_oper) = 0; // Channel Center Frequency Segment 1 (N.A.)
+ break;
+ case NL80211_CHAN_WIDTH_80P80:
+ vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_80P80MHZ; // Channel Width
+ CCFS0(vht_oper) =
+ ieee80211_frequency_to_channel(ecrnx_vif->sta.ap->center_freq1); // Channel Center Frequency Segment 0
+ CCFS1(vht_oper) =
+ ieee80211_frequency_to_channel(ecrnx_vif->sta.ap->center_freq2); // Channel Center Frequency Segment 1
+ break;
+ default:
+ vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_USE_HT;
+ CCFS0(vht_oper) = 0;
+ CCFS1(vht_oper) = 0;
+ break;
+ }
+
+ vht_oper->basic_mcs_set = cpu_to_le16(ecrnx_hw->mod_params->mcs_map);
+
+ return pos + sizeof(struct ieee80211_vht_operation);
+
+}
+
+static void
+ecrnx_tdls_add_setup_start_ies(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *ecrnx_vif,
+ struct sk_buff *skb, const u8 *peer,
+ u8 action_code, bool initiator,
+ const u8 *extra_ies, size_t extra_ies_len)
+{
+ enum nl80211_band band = ecrnx_vif->sta.ap->band;
+ struct ieee80211_supported_band *sband;
+ struct ieee80211_sta_ht_cap ht_cap;
+ struct ieee80211_sta_vht_cap vht_cap;
+ size_t offset = 0, noffset;
+ u8 *pos;
+
+ rcu_read_lock();
+
+ ecrnx_add_srates_ie(ecrnx_hw, skb);
+ ecrnx_add_ext_srates_ie(ecrnx_hw, skb);
+ ecrnx_tdls_add_supp_channels(ecrnx_hw, skb);
+ ecrnx_tdls_add_ext_capab(ecrnx_hw, skb);
+
+ /* add the QoS element if we support it */
+ if (/*local->hw.queues >= IEEE80211_NUM_ACS &&*/
+ action_code != WLAN_PUB_ACTION_TDLS_DISCOVER_RES)
+ ecrnx_add_wmm_info_ie(skb, 0); /* no U-APSD */
+
+ ecrnx_tdls_add_oper_classes(ecrnx_vif, skb);
+
+ /*
+ * with TDLS we can switch channels, and HT-caps are not necessarily
+ * the same on all bands. The specification limits the setup to a
+ * single HT-cap, so use the current band for now.
+ */
+ sband = ecrnx_hw->wiphy->bands[band];
+ memcpy(&ht_cap, &sband->ht_cap, sizeof(ht_cap));
+ if (((action_code == WLAN_TDLS_SETUP_REQUEST) ||
+ (action_code == WLAN_TDLS_SETUP_RESPONSE) ||
+ (action_code == WLAN_PUB_ACTION_TDLS_DISCOVER_RES)) &&
+ ht_cap.ht_supported /* (!sta || sta->sta.ht_cap.ht_supported)*/) {
+ ecrnx_ie_build_ht_cap(skb, &ht_cap, ht_cap.cap);
+ }
+
+ if (ht_cap.ht_supported &&
+ (ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40))
+ ecrnx_tdls_add_bss_coex_ie(skb);
+
+ ecrnx_tdls_add_link_ie(ecrnx_hw, ecrnx_vif, skb, peer, initiator);
+
+ memcpy(&vht_cap, &sband->vht_cap, sizeof(vht_cap));
+ if (vht_cap.vht_supported) {
+ ecrnx_tdls_add_aid_ie(ecrnx_vif, skb);
+ ecrnx_ie_build_vht_cap(skb, &vht_cap, vht_cap.cap);
+ // Operating mode Notification (optional)
+ }
+
+ /* add any remaining IEs */
+ if (extra_ies_len) {
+ noffset = extra_ies_len;
+ pos = skb_put(skb, noffset - offset);
+ memcpy(pos, extra_ies + offset, noffset - offset);
+ }
+
+ rcu_read_unlock();
+}
+
+
+static void
+ecrnx_tdls_add_setup_cfm_ies(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *ecrnx_vif,
+ struct sk_buff *skb, const u8 *peer, bool initiator,
+ const u8 *extra_ies, size_t extra_ies_len)
+{
+ struct ieee80211_supported_band *sband;
+ enum nl80211_band band = ecrnx_vif->sta.ap->band;
+ struct ieee80211_sta_ht_cap ht_cap;
+ struct ieee80211_sta_vht_cap vht_cap;
+
+ size_t offset = 0, noffset;
+ struct ecrnx_sta *sta, *ap_sta;
+ u8 *pos;
+
+ rcu_read_lock();
+
+ sta = ecrnx_get_sta(ecrnx_hw, peer);
+ ap_sta = ecrnx_vif->sta.ap;
+ if (WARN_ON_ONCE(!sta || !ap_sta)) {
+ rcu_read_unlock();
+ return;
+ }
+
+ /* add the QoS param IE if both the peer and we support it */
+ if (sta->qos)
+ ecrnx_add_wmm_param_ie(skb, ap_sta->acm, ap_sta->ac_param);
+
+ /* if HT support is only added in TDLS, we need an HT-operation IE */
+ sband = ecrnx_hw->wiphy->bands[band];
+ memcpy(&ht_cap, &sband->ht_cap, sizeof(ht_cap));
+ if (ht_cap.ht_supported && !ap_sta->ht && sta->ht) {
+ pos = skb_put(skb, 2 + sizeof(struct ieee80211_ht_operation));
+ /* send an empty HT operation IE */
+ ecrnx_ie_build_ht_oper(ecrnx_hw, ecrnx_vif, pos, &ht_cap, 0);
+ }
+
+ ecrnx_tdls_add_link_ie(ecrnx_hw, ecrnx_vif, skb, peer, initiator);
+
+ memcpy(&vht_cap, &sband->vht_cap, sizeof(vht_cap));
+ if (vht_cap.vht_supported && !ap_sta->vht && sta->vht) {
+ pos = skb_put(skb, 2 + sizeof(struct ieee80211_vht_operation));
+ ecrnx_ie_build_vht_oper(ecrnx_hw, ecrnx_vif, pos, &ht_cap, 0);
+ // Operating mode Notification (optional)
+ }
+
+ /* add any remaining IEs */
+ if (extra_ies_len) {
+ noffset = extra_ies_len;
+ pos = skb_put(skb, noffset - offset);
+ memcpy(pos, extra_ies + offset, noffset - offset);
+ }
+
+ rcu_read_unlock();
+}
+
+static void
+ecrnx_tdls_add_ies(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *ecrnx_vif,
+ struct sk_buff *skb, const u8 *peer,
+ u8 action_code, u16 status_code,
+ bool initiator, const u8 *extra_ies,
+ size_t extra_ies_len, u8 oper_class,
+ struct cfg80211_chan_def *chandef)
+{
+ switch (action_code) {
+ case WLAN_TDLS_SETUP_REQUEST:
+ case WLAN_TDLS_SETUP_RESPONSE:
+ case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
+ if (status_code == 0)
+ ecrnx_tdls_add_setup_start_ies(ecrnx_hw, ecrnx_vif, skb, peer, action_code,
+ initiator, extra_ies, extra_ies_len);
+ break;
+ case WLAN_TDLS_SETUP_CONFIRM:
+ if (status_code == 0)
+ ecrnx_tdls_add_setup_cfm_ies(ecrnx_hw, ecrnx_vif, skb, peer, initiator,
+ extra_ies, extra_ies_len);
+ break;
+
+ case WLAN_TDLS_TEARDOWN:
+ case WLAN_TDLS_DISCOVERY_REQUEST:
+ if (extra_ies_len)
+ memcpy(skb_put(skb, extra_ies_len), extra_ies,
+ extra_ies_len);
+ if (status_code == 0 || action_code == WLAN_TDLS_TEARDOWN)
+ ecrnx_tdls_add_link_ie(ecrnx_hw, ecrnx_vif, skb, peer, initiator);
+ break;
+ }
+}
+
+int
+ecrnx_tdls_send_mgmt_packet_data(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *ecrnx_vif,
+ const u8 *peer, u8 action_code, u8 dialog_token,
+ u16 status_code, u32 peer_capability, bool initiator,
+ const u8 *extra_ies, size_t extra_ies_len, u8 oper_class,
+ struct cfg80211_chan_def *chandef)
+{
+ struct sk_buff *skb;
+ int ret = 0;
+ struct ieee80211_supported_band *ecrnx_band_2GHz = ecrnx_hw->wiphy->bands[NL80211_BAND_2GHZ];
+#ifdef CONFIG_ECRNX_5G
+ struct ieee80211_supported_band *ecrnx_band_5GHz = ecrnx_hw->wiphy->bands[NL80211_BAND_5GHZ];
+#endif
+ skb = netdev_alloc_skb(ecrnx_vif->ndev,
+ sizeof(struct ieee80211_tdls_data) + // ethhdr + TDLS info
+ 10 + /* supported rates */
+ 6 + /* extended supported rates */
+ #ifdef CONFIG_ECRNX_5G
+ (2 + ecrnx_band_2GHz->n_channels + ecrnx_band_5GHz->n_channels) + /* supported channels */
+ #else
+ (2 + ecrnx_band_2GHz->n_channels) +
+ #endif
+ sizeof(struct ieee_types_extcap) +
+ sizeof(struct ieee80211_wmm_param_ie) +
+ 4 + /* oper classes */
+ 28 + //sizeof(struct ieee80211_ht_cap) +
+ sizeof(struct ieee_types_bss_co_2040) +
+ sizeof(struct ieee80211_tdls_lnkie) +
+ (2 + sizeof(struct ieee80211_vht_cap)) +
+ 4 + /*AID*/
+ (2 + sizeof(struct ieee80211_ht_operation)) +
+ extra_ies_len);
+
+ if (!skb)
+ return 0;
+
+ switch (action_code) {
+ case WLAN_TDLS_SETUP_REQUEST:
+ case WLAN_TDLS_SETUP_RESPONSE:
+ case WLAN_TDLS_SETUP_CONFIRM:
+ case WLAN_TDLS_TEARDOWN:
+ case WLAN_TDLS_DISCOVERY_REQUEST:
+ ret = ecrnx_tdls_prepare_encap_data(ecrnx_hw, ecrnx_vif, peer, action_code,
+ dialog_token, status_code, skb);
+ break;
+
+ case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
+ ret = ecrnx_prep_tdls_direct(ecrnx_hw, ecrnx_vif, peer, action_code,
+ dialog_token, status_code, skb);
+ break;
+
+ default:
+ ret = -ENOTSUPP;
+ break;
+ }
+
+ if (ret < 0)
+ goto fail;
+
+ ecrnx_tdls_add_ies(ecrnx_hw, ecrnx_vif, skb, peer, action_code, status_code,
+ initiator, extra_ies, extra_ies_len, oper_class, chandef);
+
+ if (action_code == WLAN_PUB_ACTION_TDLS_DISCOVER_RES) {
+ u64 cookie;
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0))
+ struct cfg80211_mgmt_tx_params params;
+
+ params.len = skb->len;
+ params.buf = skb->data;
+ ret = ecrnx_start_mgmt_xmit(ecrnx_vif, NULL, &params, false, &cookie);
+#else
+ ret = ecrnx_start_mgmt_xmit(ecrnx_vif, NULL, NULL, false, 0, skb->data, skb->len, false, false, &cookie);
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) */
+ return ret;
+ }
+
+ switch (action_code) {
+ case WLAN_TDLS_SETUP_REQUEST:
+ case WLAN_TDLS_SETUP_RESPONSE:
+ case WLAN_TDLS_SETUP_CONFIRM:
+ skb->priority = 2;
+ break;
+ default:
+ skb->priority = 5;
+ break;
+ }
+
+ ret = ecrnx_select_txq(ecrnx_vif, skb);
+ ret = ecrnx_start_xmit(skb, ecrnx_vif->ndev);
+
+ return ret;
+
+fail:
+ dev_kfree_skb(skb);
+ return ret;
+}
diff --git a/drivers/net/wireless/eswin/fullmac/ecrnx_tdls.h b/drivers/net/wireless/eswin/fullmac/ecrnx_tdls.h
new file mode 100644
index 000000000000..4bec85dddea1
--- /dev/null
+++ b/drivers/net/wireless/eswin/fullmac/ecrnx_tdls.h
@@ -0,0 +1,54 @@
+/**
+ ******************************************************************************
+ *
+ * @file ecrnx_tdls.h
+ *
+ * @brief TDLS function declarations
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+
+#ifndef ECRNX_TDLS_H_
+#define ECRNX_TDLS_H_
+
+#include "ecrnx_defs.h"
+
+struct ieee_types_header {
+ u8 element_id;
+ u8 len;
+} __packed;
+
+struct ieee_types_bss_co_2040 {
+ struct ieee_types_header ieee_hdr;
+ u8 bss_2040co;
+} __packed;
+
+struct ieee_types_extcap {
+ struct ieee_types_header ieee_hdr;
+ u8 ext_capab[8];
+} __packed;
+
+struct ieee_types_vht_cap {
+ struct ieee_types_header ieee_hdr;
+ struct ieee80211_vht_cap vhtcap;
+} __packed;
+
+struct ieee_types_vht_oper {
+ struct ieee_types_header ieee_hdr;
+ struct ieee80211_vht_operation vhtoper;
+} __packed;
+
+struct ieee_types_aid {
+ struct ieee_types_header ieee_hdr;
+ u16 aid;
+} __packed;
+
+int ecrnx_tdls_send_mgmt_packet_data(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *ecrnx_vif,
+ const u8 *peer, u8 action_code, u8 dialog_token,
+ u16 status_code, u32 peer_capability, bool initiator,
+ const u8 *extra_ies, size_t extra_ies_len, u8 oper_class,
+ struct cfg80211_chan_def *chandef);
+
+#endif /* ECRNX_TDLS_H_ */
diff --git a/drivers/net/wireless/eswin/fullmac/ecrnx_tx.c b/drivers/net/wireless/eswin/fullmac/ecrnx_tx.c
new file mode 100644
index 000000000000..459e8185e984
--- /dev/null
+++ b/drivers/net/wireless/eswin/fullmac/ecrnx_tx.c
@@ -0,0 +1,1877 @@
+/**
+ ******************************************************************************
+ *
+ * @file ecrnx_tx.c
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+#include <linux/dma-mapping.h>
+#include <linux/etherdevice.h>
+#include <net/sock.h>
+
+#include "ecrnx_defs.h"
+#include "ecrnx_tx.h"
+#include "ecrnx_msg_tx.h"
+#include "ecrnx_mesh.h"
+#include "ecrnx_events.h"
+#include "ecrnx_compat.h"
+
+#ifdef CONFIG_ECRNX_ESWIN
+#include "eswin_utils.h"
+#endif
+/******************************************************************************
+ * Power Save functions
+ *****************************************************************************/
+/**
+ * ecrnx_set_traffic_status - Inform FW if traffic is available for STA in PS
+ *
+ * @ecrnx_hw: Driver main data
+ * @sta: Sta in PS mode
+ * @available: whether traffic is buffered for the STA
+ * @ps_id: type of PS data requested (@LEGACY_PS_ID or @UAPSD_ID)
+ */
+void ecrnx_set_traffic_status(struct ecrnx_hw *ecrnx_hw,
+ struct ecrnx_sta *sta,
+ bool available,
+ u8 ps_id)
+{
+ if (sta->tdls.active) {
+ ecrnx_send_tdls_peer_traffic_ind_req(ecrnx_hw,
+ ecrnx_hw->vif_table[sta->vif_idx]);
+ } else {
+ bool uapsd = (ps_id != LEGACY_PS_ID);
+ ecrnx_send_me_traffic_ind(ecrnx_hw, sta->sta_idx, uapsd, available);
+ trace_ps_traffic_update(sta->sta_idx, available, uapsd);
+ }
+}
+
+/**
+ * ecrnx_ps_bh_enable - Enable/disable PS mode for one STA
+ *
+ * @ecrnx_hw: Driver main data
+ * @sta: Sta which enters/leaves PS mode
+ * @enable: PS mode status
+ *
+ * This function will enable/disable PS mode for one STA.
+ * When enabling PS mode:
+ * - Stop all STA's txq for ECRNX_TXQ_STOP_STA_PS reason
+ * - Count how many buffers are already ready for this STA
+ * - For BC/MC sta, update all queued SKB to use hw_queue BCMC
+ * - Update TIM if some packet are ready
+ *
+ * When disabling PS mode:
+ * - Start all STA's txq for ECRNX_TXQ_STOP_STA_PS reason
+ * - For BC/MC sta, update all queued SKB to use hw_queue AC_BE
+ * - Update TIM if some packet are ready (otherwise fw will not update TIM
+ * in beacon for this STA)
+ *
+ * All counter/skb updates are protected from TX path by taking tx_lock
+ *
+ * NOTE: _bh_ in function name indicates that this function is called
+ * from a bottom_half tasklet.
+ */
+void ecrnx_ps_bh_enable(struct ecrnx_hw *ecrnx_hw, struct ecrnx_sta *sta,
+ bool enable)
+{
+ struct ecrnx_txq *txq;
+
+ if (enable) {
+ trace_ps_enable(sta);
+
+ spin_lock(&ecrnx_hw->tx_lock);
+ sta->ps.active = true;
+ sta->ps.sp_cnt[LEGACY_PS_ID] = 0;
+ sta->ps.sp_cnt[UAPSD_ID] = 0;
+ ecrnx_txq_sta_stop(sta, ECRNX_TXQ_STOP_STA_PS, ecrnx_hw);
+
+ if (is_multicast_sta(sta->sta_idx)) {
+ txq = ecrnx_txq_sta_get(sta, 0, ecrnx_hw);
+ sta->ps.pkt_ready[LEGACY_PS_ID] = skb_queue_len(&txq->sk_list);
+ sta->ps.pkt_ready[UAPSD_ID] = 0;
+ txq->hwq = &ecrnx_hw->hwq[ECRNX_HWQ_BCMC];
+ } else {
+ int i;
+ sta->ps.pkt_ready[LEGACY_PS_ID] = 0;
+ sta->ps.pkt_ready[UAPSD_ID] = 0;
+ foreach_sta_txq(sta, txq, i, ecrnx_hw) {
+ sta->ps.pkt_ready[txq->ps_id] += skb_queue_len(&txq->sk_list);
+ }
+ }
+
+ spin_unlock(&ecrnx_hw->tx_lock);
+
+ if (sta->ps.pkt_ready[LEGACY_PS_ID])
+ ecrnx_set_traffic_status(ecrnx_hw, sta, true, LEGACY_PS_ID);
+
+ if (sta->ps.pkt_ready[UAPSD_ID])
+ ecrnx_set_traffic_status(ecrnx_hw, sta, true, UAPSD_ID);
+ } else {
+ trace_ps_disable(sta->sta_idx);
+
+ spin_lock(&ecrnx_hw->tx_lock);
+ sta->ps.active = false;
+
+ if (is_multicast_sta(sta->sta_idx)) {
+ txq = ecrnx_txq_sta_get(sta, 0, ecrnx_hw);
+ txq->hwq = &ecrnx_hw->hwq[ECRNX_HWQ_BE];
+ txq->push_limit = 0;
+ } else {
+ int i;
+ foreach_sta_txq(sta, txq, i, ecrnx_hw) {
+ txq->push_limit = 0;
+ }
+ }
+
+ ecrnx_txq_sta_start(sta, ECRNX_TXQ_STOP_STA_PS, ecrnx_hw);
+ spin_unlock(&ecrnx_hw->tx_lock);
+
+ if (sta->ps.pkt_ready[LEGACY_PS_ID])
+ ecrnx_set_traffic_status(ecrnx_hw, sta, false, LEGACY_PS_ID);
+
+ if (sta->ps.pkt_ready[UAPSD_ID])
+ ecrnx_set_traffic_status(ecrnx_hw, sta, false, UAPSD_ID);
+ }
+}
+
+/**
+ * ecrnx_ps_bh_traffic_req - Handle traffic request for STA in PS mode
+ *
+ * @ecrnx_hw: Driver main data
+ * @sta: Sta which enters/leaves PS mode
+ * @pkt_req: number of pkt to push
+ * @ps_id: type of PS data requested (@LEGACY_PS_ID or @UAPSD_ID)
+ *
+ * This function will make sure that @pkt_req are pushed to fw
+ * whereas the STA is in PS mode.
+ * If request is 0, send all traffic
+ * If request is greater than available pkt, reduce request
+ * Note: request will also be reduce if txq credits are not available
+ *
+ * All counter updates are protected from TX path by taking tx_lock
+ *
+ * NOTE: _bh_ in function name indicates that this function is called
+ * from the bottom_half tasklet.
+ */
+void ecrnx_ps_bh_traffic_req(struct ecrnx_hw *ecrnx_hw, struct ecrnx_sta *sta,
+ u16 pkt_req, u8 ps_id)
+{
+ int pkt_ready_all;
+ u16 txq_len;
+ struct ecrnx_txq *txq;
+
+#ifndef CONFIG_ECRNX_ESWIN
+ if (WARN(!sta->ps.active, "sta %pM is not in Power Save mode",
+ sta->mac_addr))
+ return;
+#else
+ if (!sta->ps.active)
+ {
+ ECRNX_DBG(" sta is not in Power Save mode %02x:%02x:%02x:%02x:%02x:%02x %d %d \n", sta->mac_addr[0], sta->mac_addr[1], sta->mac_addr[2], \
+ sta->mac_addr[3], sta->mac_addr[4], sta->mac_addr[5],pkt_req, ps_id);
+ return;
+ }
+#endif
+
+ trace_ps_traffic_req(sta, pkt_req, ps_id);
+
+ spin_lock(&ecrnx_hw->tx_lock);
+
+ /* Fw may ask to stop a service period with PS_SP_INTERRUPTED. This only
+ happens for p2p-go interface if NOA starts during a service period */
+ if ((pkt_req == PS_SP_INTERRUPTED) && (ps_id == UAPSD_ID)) {
+ int tid;
+ sta->ps.sp_cnt[ps_id] = 0;
+ foreach_sta_txq(sta, txq, tid, ecrnx_hw) {
+ txq->push_limit = 0;
+ }
+ goto done;
+ }
+
+ pkt_ready_all = (sta->ps.pkt_ready[ps_id] - sta->ps.sp_cnt[ps_id]);
+
+ /* Don't start SP until previous one is finished or we don't have
+ packet ready (which must not happen for U-APSD) */
+ if (sta->ps.sp_cnt[ps_id] || pkt_ready_all <= 0) {
+ goto done;
+ }
+
+ /* Adapt request to what is available. */
+ if (pkt_req == 0 || pkt_req > pkt_ready_all) {
+ pkt_req = pkt_ready_all;
+ }
+
+ /* Reset the SP counter */
+ sta->ps.sp_cnt[ps_id] = 0;
+
+ /* "dispatch" the request between txq */
+ if (is_multicast_sta(sta->sta_idx)) {
+ txq = ecrnx_txq_sta_get(sta, 0, ecrnx_hw);
+ if (txq->credits <= 0)
+ goto done;
+ if (pkt_req > txq->credits)
+ pkt_req = txq->credits;
+ txq->push_limit = pkt_req;
+ sta->ps.sp_cnt[ps_id] = pkt_req;
+ ECRNX_DBG("%s-%d:sta:0x%p, sta_idx:%d, txq:0x%p, txq status:%d \n", __func__, __LINE__, sta, sta->sta_idx, txq, txq->status);
+ ecrnx_txq_add_to_hw_list(txq);
+ ecrnx_txq_sta_start(sta, ECRNX_TXQ_STOP_STA_PS, ecrnx_hw);
+ } else {
+ int i, tid;
+
+ for (i = 0; i < NX_NB_TID_PER_STA; i++) {
+ tid = nx_tid_prio[i];
+
+#ifdef CONFIG_ECRNX_SOFTMAC
+ txq = ecrnx_txq_sta_get(sta, tid);
+#else
+ txq = ecrnx_txq_sta_get(sta, tid, ecrnx_hw);
+#endif
+
+ txq_len = skb_queue_len(&txq->sk_list);
+
+ if (txq->ps_id != ps_id)
+ continue;
+
+ if (txq_len > txq->credits)
+ txq_len = txq->credits;
+
+ if (txq_len == 0)
+ continue;
+
+ if (txq_len < pkt_req) {
+ /* Not enough pkt queued in this txq, add this
+ txq to hwq list and process next txq */
+ pkt_req -= txq_len;
+ txq->push_limit = txq_len;
+ sta->ps.sp_cnt[ps_id] += txq_len;
+ ecrnx_txq_add_to_hw_list(txq);
+ } else {
+ /* Enough pkt in this txq to comlete the request
+ add this txq to hwq list and stop processing txq */
+ txq->push_limit = pkt_req;
+ sta->ps.sp_cnt[ps_id] += pkt_req;
+ ecrnx_txq_add_to_hw_list(txq);
+ break;
+ }
+ }
+ }
+
+ done:
+ spin_unlock(&ecrnx_hw->tx_lock);
+}
+
+/******************************************************************************
+ * TX functions
+ *****************************************************************************/
+#define PRIO_STA_NULL 0xAA
+
+static const int ecrnx_down_hwq2tid[3] = {
+ [ECRNX_HWQ_BK] = 2,
+ [ECRNX_HWQ_BE] = 3,
+ [ECRNX_HWQ_VI] = 5,
+};
+
+static void ecrnx_downgrade_ac(struct ecrnx_sta *sta, struct sk_buff *skb)
+{
+ int8_t ac = ecrnx_tid2hwq[skb->priority];
+
+ if (WARN((ac > ECRNX_HWQ_VO),
+ "Unexepcted ac %d for skb before downgrade", ac))
+ ac = ECRNX_HWQ_VO;
+
+ while (sta->acm & BIT(ac)) {
+ if (ac == ECRNX_HWQ_BK) {
+ skb->priority = 1;
+ return;
+ }
+ ac--;
+ skb->priority = ecrnx_down_hwq2tid[ac];
+ }
+}
+
+static void ecrnx_tx_statistic(struct ecrnx_hw *ecrnx_hw, struct ecrnx_txq *txq,
+ union ecrnx_hw_txstatus ecrnx_txst, unsigned int data_len)
+{
+ struct ecrnx_sta *sta = txq->sta;
+ if (!sta || !ecrnx_txst.acknowledged)
+ return;
+ sta->stats.tx_pkts ++;
+ sta->stats.tx_bytes += data_len;
+ sta->stats.last_act = ecrnx_hw->stats.last_tx;
+}
+u16 ecrnx_select_txq(struct ecrnx_vif *ecrnx_vif, struct sk_buff *skb)
+{
+ struct ecrnx_hw *ecrnx_hw = ecrnx_vif->ecrnx_hw;
+ struct wireless_dev *wdev = &ecrnx_vif->wdev;
+ struct ecrnx_sta *sta = NULL;
+ struct ecrnx_txq *txq;
+ u16 netdev_queue;
+ bool tdls_mgmgt_frame = false;
+
+ switch (wdev->iftype) {
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_P2P_CLIENT:
+ {
+ struct ethhdr *eth;
+ eth = (struct ethhdr *)skb->data;
+ if (eth->h_proto == cpu_to_be16(ETH_P_TDLS)) {
+ tdls_mgmgt_frame = true;
+ }
+ if ((ecrnx_vif->tdls_status == TDLS_LINK_ACTIVE) &&
+ (ecrnx_vif->sta.tdls_sta != NULL) &&
+ (memcmp(eth->h_dest, ecrnx_vif->sta.tdls_sta->mac_addr, ETH_ALEN) == 0))
+ sta = ecrnx_vif->sta.tdls_sta;
+ else
+ sta = ecrnx_vif->sta.ap;
+ break;
+ }
+ case NL80211_IFTYPE_AP_VLAN:
+ {
+ struct ecrnx_sta *cur;
+ struct ethhdr *eth = (struct ethhdr *)skb->data;
+
+ if (ecrnx_vif->ap_vlan.sta_4a) {
+ sta = ecrnx_vif->ap_vlan.sta_4a;
+ break;
+ }
+
+ /* AP_VLAN interface is not used for a 4A STA,
+ fallback searching sta amongs all AP's clients */
+ ecrnx_vif = ecrnx_vif->ap_vlan.master;
+
+ if (is_multicast_ether_addr(eth->h_dest)) {
+ sta = &ecrnx_hw->sta_table[ecrnx_vif->ap.bcmc_index];
+ } else {
+ list_for_each_entry(cur, &ecrnx_vif->ap.sta_list, list) {
+ if (!memcmp(cur->mac_addr, eth->h_dest, ETH_ALEN)) {
+ sta = cur;
+ break;
+ }
+ }
+ }
+
+ break;
+ }
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_P2P_GO:
+ {
+ struct ecrnx_sta *cur;
+ struct ethhdr *eth = (struct ethhdr *)skb->data;
+
+ if (is_multicast_ether_addr(eth->h_dest)) {
+ sta = &ecrnx_hw->sta_table[ecrnx_vif->ap.bcmc_index];
+ } else {
+ list_for_each_entry(cur, &ecrnx_vif->ap.sta_list, list) {
+ if (!memcmp(cur->mac_addr, eth->h_dest, ETH_ALEN)) {
+ sta = cur;
+ break;
+ }
+ }
+ }
+
+ break;
+ }
+ case NL80211_IFTYPE_MESH_POINT:
+ {
+ struct ethhdr *eth = (struct ethhdr *)skb->data;
+
+ if (!ecrnx_vif->is_resending) {
+ /*
+ * If ethernet source address is not the address of a mesh wireless interface, we are proxy for
+ * this address and have to inform the HW
+ */
+ if (memcmp(&eth->h_source[0], &ecrnx_vif->ndev->perm_addr[0], ETH_ALEN)) {
+ /* Check if LMAC is already informed */
+ if (!ecrnx_get_mesh_proxy_info(ecrnx_vif, (u8 *)&eth->h_source, true)) {
+ ecrnx_send_mesh_proxy_add_req(ecrnx_hw, ecrnx_vif, (u8 *)&eth->h_source);
+ }
+ }
+ }
+
+ if (is_multicast_ether_addr(eth->h_dest)) {
+ sta = &ecrnx_hw->sta_table[ecrnx_vif->ap.bcmc_index];
+ } else {
+ /* Path to be used */
+ struct ecrnx_mesh_path *p_mesh_path = NULL;
+ struct ecrnx_mesh_path *p_cur_path;
+ /* Check if destination is proxied by a peer Mesh STA */
+ struct ecrnx_mesh_proxy *p_mesh_proxy = ecrnx_get_mesh_proxy_info(ecrnx_vif, (u8 *)&eth->h_dest, false);
+ /* Mesh Target address */
+ struct mac_addr *p_tgt_mac_addr;
+
+ if (p_mesh_proxy) {
+ p_tgt_mac_addr = &p_mesh_proxy->proxy_addr;
+ } else {
+ p_tgt_mac_addr = (struct mac_addr *)&eth->h_dest;
+ }
+
+ /* Look for path with provided target address */
+ list_for_each_entry(p_cur_path, &ecrnx_vif->ap.mpath_list, list) {
+ if (!memcmp(&p_cur_path->tgt_mac_addr, p_tgt_mac_addr, ETH_ALEN)) {
+ p_mesh_path = p_cur_path;
+ break;
+ }
+ }
+
+ if (p_mesh_path) {
+ sta = p_mesh_path->nhop_sta;
+ } else {
+ ecrnx_send_mesh_path_create_req(ecrnx_hw, ecrnx_vif, (u8 *)p_tgt_mac_addr);
+ }
+ }
+
+ break;
+ }
+ default:
+ break;
+ }
+
+ if (sta && sta->qos)
+ {
+ if (tdls_mgmgt_frame) {
+ skb_set_queue_mapping(skb, NX_STA_NDEV_IDX(skb->priority, sta->sta_idx));
+ } else {
+ /* use the data classifier to determine what 802.1d tag the
+ * data frame has */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 14, 0)
+ skb->priority = cfg80211_classify8021d(skb) & IEEE80211_QOS_CTL_TAG1D_MASK;
+#else
+ skb->priority = cfg80211_classify8021d(skb, NULL) & IEEE80211_QOS_CTL_TAG1D_MASK;
+#endif
+ }
+ if (sta->acm)
+ ecrnx_downgrade_ac(sta, skb);
+
+ txq = ecrnx_txq_sta_get(sta, skb->priority, ecrnx_hw);
+ netdev_queue = txq->ndev_idx;
+ }
+ else if (sta)
+ {
+ skb->priority = 0xFF;
+ txq = ecrnx_txq_sta_get(sta, 0, ecrnx_hw);
+ netdev_queue = txq->ndev_idx;
+ }
+ else
+ {
+ /* This packet will be dropped in xmit function, still need to select
+ an active queue for xmit to be called. As it most likely to happen
+ for AP interface, select BCMC queue
+ (TODO: select another queue if BCMC queue is stopped) */
+ skb->priority = PRIO_STA_NULL;
+ netdev_queue = NX_BCMC_TXQ_NDEV_IDX;
+ }
+
+ BUG_ON(netdev_queue >= NX_NB_NDEV_TXQ);
+
+ return netdev_queue;
+}
+
+/**
+ * ecrnx_set_more_data_flag - Update MORE_DATA flag in tx sw desc
+ *
+ * @ecrnx_hw: Driver main data
+ * @sw_txhdr: Header for pkt to be pushed
+ *
+ * If STA is in PS mode
+ * - Set EOSP in case the packet is the last of the UAPSD service period
+ * - Set MORE_DATA flag if more pkt are ready for this sta
+ * - Update TIM if this is the last pkt buffered for this sta
+ *
+ * note: tx_lock already taken.
+ */
+static inline void ecrnx_set_more_data_flag(struct ecrnx_hw *ecrnx_hw,
+ struct ecrnx_sw_txhdr *sw_txhdr)
+{
+ struct ecrnx_sta *sta = sw_txhdr->ecrnx_sta;
+ struct ecrnx_vif *vif = sw_txhdr->ecrnx_vif;
+ struct ecrnx_txq *txq = sw_txhdr->txq;
+
+ if (unlikely(sta->ps.active)) {
+ sta->ps.pkt_ready[txq->ps_id]--;
+ sta->ps.sp_cnt[txq->ps_id]--;
+
+ trace_ps_push(sta);
+
+ if (((txq->ps_id == UAPSD_ID) || (vif->wdev.iftype == NL80211_IFTYPE_MESH_POINT) || (sta->tdls.active))
+ && !sta->ps.sp_cnt[txq->ps_id]) {
+ sw_txhdr->desc.host.flags |= TXU_CNTRL_EOSP;
+ }
+
+ if (sta->ps.pkt_ready[txq->ps_id]) {
+ sw_txhdr->desc.host.flags |= TXU_CNTRL_MORE_DATA;
+ } else {
+ ecrnx_set_traffic_status(ecrnx_hw, sta, false, txq->ps_id);
+ }
+ }
+}
+
+/**
+ * ecrnx_get_tx_info - Get STA and tid for one skb
+ *
+ * @ecrnx_vif: vif ptr
+ * @skb: skb
+ * @tid: pointer updated with the tid to use for this skb
+ *
+ * @return: pointer on the destination STA (may be NULL)
+ *
+ * skb has already been parsed in ecrnx_select_queue function
+ * simply re-read information form skb.
+ */
+static struct ecrnx_sta *ecrnx_get_tx_info(struct ecrnx_vif *ecrnx_vif,
+ struct sk_buff *skb,
+ u8 *tid)
+{
+ struct ecrnx_hw *ecrnx_hw = ecrnx_vif->ecrnx_hw;
+ struct ecrnx_sta *sta;
+ int sta_idx;
+
+ *tid = skb->priority;
+ if (unlikely(skb->priority == PRIO_STA_NULL)) {
+ return NULL;
+ } else {
+ int ndev_idx = skb_get_queue_mapping(skb);
+
+ if (ndev_idx == NX_BCMC_TXQ_NDEV_IDX)
+ sta_idx = NX_REMOTE_STA_MAX + master_vif_idx(ecrnx_vif);
+ else
+ sta_idx = ndev_idx / NX_NB_TID_PER_STA;
+
+ sta = &ecrnx_hw->sta_table[sta_idx];
+ }
+
+ return sta;
+}
+
+#ifndef CONFIG_ECRNX_ESWIN
+/**
+ * ecrnx_prep_tx - Prepare buffer for DMA transmission
+ *
+ * @ecrnx_hw: Driver main data
+ * @txhdr: Tx descriptor
+ *
+ * Maps hw_txhdr and buffer data for transmission via DMA.
+ * - Data buffer with be downloaded by embebded side.
+ * - hw_txhdr will be uploaded by embedded side when buffer has been
+ * transmitted over the air.
+ */
+static int ecrnx_prep_dma_tx(struct ecrnx_hw *ecrnx_hw, struct ecrnx_txhdr *txhdr, bool eth_hdr)
+{
+ struct ecrnx_sw_txhdr *sw_txhdr = txhdr->sw_hdr;
+ struct ecrnx_hw_txhdr *hw_txhdr = &txhdr->hw_hdr;
+ struct txdesc_api *desc = &sw_txhdr->desc;
+ dma_addr_t dma_addr;
+
+ txhdr->hw_hdr.cfm.status.value = 0;
+ /* MAP (and sync) memory for DMA */
+ dma_addr = dma_map_single(ecrnx_hw->dev, hw_txhdr,
+ sw_txhdr->map_len, DMA_BIDIRECTIONAL);
+ if (WARN_ON(dma_mapping_error(ecrnx_hw->dev, dma_addr)))
+ return -1;
+
+ sw_txhdr->dma_addr = dma_addr;
+
+ desc->host.status_desc_addr = dma_addr;
+ dma_addr += ECRNX_TX_DATA_OFT(sw_txhdr);
+ if (eth_hdr)
+ dma_addr += sizeof(struct ethhdr);
+#ifdef CONFIG_ECRNX_SPLIT_TX_BUF
+ desc->host.packet_len[0] = sw_txhdr->frame_len;
+ desc->host.packet_addr[0] = dma_addr;
+ desc->host.packet_cnt = 1;
+#else
+ desc->host.packet_len = sw_txhdr->frame_len;
+ desc->host.packet_addr = dma_addr;
+#endif
+ return 0;
+}
+#endif
+
+/**
+ * ecrnx_tx_push - Push one packet to fw
+ *
+ * @ecrnx_hw: Driver main data
+ * @txhdr: tx desc of the buffer to push
+ * @flags: push flags (see @ecrnx_push_flags)
+ *
+ * Push one packet to fw. Sw desc of the packet has already been updated.
+ * Only MORE_DATA flag will be set if needed.
+ */
+void ecrnx_tx_push(struct ecrnx_hw *ecrnx_hw, struct ecrnx_txhdr *txhdr, int flags)
+{
+ struct ecrnx_sw_txhdr *sw_txhdr = txhdr->sw_hdr;
+ struct sk_buff *skb = sw_txhdr->skb;
+ struct ecrnx_txq *txq = sw_txhdr->txq;
+ u16 hw_queue = txq->hwq->id;
+ int user = 0;
+
+ lockdep_assert_held(&ecrnx_hw->tx_lock);
+
+ /* RETRY flag is not always set so retest here */
+ if (txq->nb_retry) {
+ flags |= ECRNX_PUSH_RETRY;
+ txq->nb_retry--;
+ if (txq->nb_retry == 0) {
+ WARN(skb != txq->last_retry_skb,
+ "last retry buffer is not the expected one");
+ txq->last_retry_skb = NULL;
+ }
+ } else if (!(flags & ECRNX_PUSH_RETRY)) {
+ txq->pkt_sent++;
+ }
+
+#ifdef CONFIG_ECRNX_AMSDUS_TX
+ if (txq->amsdu == sw_txhdr) {
+ WARN((flags & ECRNX_PUSH_RETRY), "End A-MSDU on a retry");
+ ecrnx_hw->stats.amsdus[sw_txhdr->amsdu.nb - 1].done++;
+ txq->amsdu = NULL;
+ } else if (!(flags & ECRNX_PUSH_RETRY) &&
+ !(sw_txhdr->desc.host.flags & TXU_CNTRL_AMSDU)) {
+ ecrnx_hw->stats.amsdus[0].done++;
+ }
+#endif /* CONFIG_ECRNX_AMSDUS_TX */
+
+ /* Wait here to update hw_queue, as for multicast STA hwq may change
+ between queue and push (because of PS) */
+ sw_txhdr->hw_queue = hw_queue;
+
+#ifdef CONFIG_ECRNX_MUMIMO_TX
+ /* MU group is only selected during hwq processing */
+ sw_txhdr->desc.host.mumimo_info = txq->mumimo_info;
+ user = ECRNX_TXQ_POS_ID(txq);
+#endif /* CONFIG_ECRNX_MUMIMO_TX */
+
+ if (sw_txhdr->ecrnx_sta) {
+ /* only for AP mode */
+ ecrnx_set_more_data_flag(ecrnx_hw, sw_txhdr);
+ }
+
+ trace_push_desc(skb, sw_txhdr, flags);
+ txq->credits--;
+ txq->pkt_pushed[user]++;
+ if (txq->credits <= 0){
+ ECRNX_DBG("%s-%d:ecrnx_txq_stop,reaosn:0x%x \n", __func__, __LINE__, ECRNX_TXQ_STOP_FULL);
+ ecrnx_txq_stop(txq, ECRNX_TXQ_STOP_FULL);
+ }
+
+ if (txq->push_limit)
+ txq->push_limit--;
+
+ ecrnx_ipc_txdesc_push(ecrnx_hw, &sw_txhdr->desc, skb, hw_queue, user);
+ txq->hwq->credits[user]--;
+ ecrnx_hw->stats.cfm_balance[hw_queue]++;
+}
+
+
+
+/**
+ * ecrnx_tx_retry - Push an AMPDU pkt that need to be retried
+ *
+ * @ecrnx_hw: Driver main data
+ * @skb: pkt to re-push
+ * @txhdr: tx desc of the pkt to re-push
+ * @sw_retry: Indicates if fw decide to retry this buffer
+ * (i.e. it has never been transmitted over the air)
+ *
+ * Called when a packet needs to be repushed to the firmware.
+ * First update sw descriptor and then queue it in the retry list.
+ */
+static void ecrnx_tx_retry(struct ecrnx_hw *ecrnx_hw, struct sk_buff *skb,
+ struct ecrnx_txhdr *txhdr, bool sw_retry)
+{
+ struct ecrnx_sw_txhdr *sw_txhdr = txhdr->sw_hdr;
+ struct tx_cfm_tag *cfm = &txhdr->hw_hdr.cfm;
+ struct ecrnx_txq *txq = sw_txhdr->txq;
+#ifndef CONFIG_ECRNX_ESWIN
+ dma_addr_t cfm_dma_addr;
+#endif
+
+ if (!sw_retry) {
+ /* update sw desc */
+ sw_txhdr->desc.host.sn = cfm->sn;
+ sw_txhdr->desc.host.pn[0] = cfm->pn[0];
+ sw_txhdr->desc.host.pn[1] = cfm->pn[1];
+ sw_txhdr->desc.host.pn[2] = cfm->pn[2];
+ sw_txhdr->desc.host.pn[3] = cfm->pn[3];
+ sw_txhdr->desc.host.timestamp = cfm->timestamp;
+ sw_txhdr->desc.host.flags |= TXU_CNTRL_RETRY;
+
+ #ifdef CONFIG_ECRNX_AMSDUS_TX
+ if (sw_txhdr->desc.host.flags & TXU_CNTRL_AMSDU)
+ ecrnx_hw->stats.amsdus[sw_txhdr->amsdu.nb - 1].failed++;
+ #endif
+ }
+
+ /* MORE_DATA will be re-set if needed when pkt will be repushed */
+ sw_txhdr->desc.host.flags &= ~TXU_CNTRL_MORE_DATA;
+
+ cfm->status.value = 0;
+//TODO:need to check here.
+#ifndef CONFIG_ECRNX_ESWIN
+ cfm_dma_addr = (ptr_addr)sw_txhdr->desc.host.status_desc_addr;
+ dma_sync_single_for_device(ecrnx_hw->dev, cfm_dma_addr, sizeof(cfm), DMA_BIDIRECTIONAL);
+#endif
+ txq->credits++;
+ if (txq->credits > 0){
+ ecrnx_txq_start(txq, ECRNX_TXQ_STOP_FULL);
+ ECRNX_DBG("%s-%d:ecrnx_txq_start,reaosn:0x%x \n", __func__, __LINE__, ECRNX_TXQ_STOP_FULL);
+ }
+ /* Queue the buffer */
+ if (ecrnx_txq_queue_skb(skb, txq, ecrnx_hw, true, NULL))
+ {
+ /* baoyong:we need to send this AMPDU retry pkt asap, so process it now */
+ ecrnx_hwq_process(ecrnx_hw, txq->hwq);
+ }
+
+ return;
+}
+
+
+#ifdef CONFIG_ECRNX_AMSDUS_TX
+/* return size of subframe (including header) */
+static inline int ecrnx_amsdu_subframe_length(struct ethhdr *eth, int eth_len)
+{
+ /* ethernet header is replaced with amdsu header that have the same size
+ Only need to check if LLC/SNAP header will be added */
+ int len = eth_len;
+
+ if (ntohs(eth->h_proto) >= ETH_P_802_3_MIN) {
+ len += sizeof(rfc1042_header) + 2;
+ }
+
+ return len;
+}
+
+static inline bool ecrnx_amsdu_is_aggregable(struct sk_buff *skb)
+{
+ /* need to add some check on buffer to see if it can be aggregated ? */
+ return true;
+}
+
+
+/**
+ * ecrnx_amsdu_del_subframe_header - remove AMSDU header
+ *
+ * amsdu_txhdr: amsdu tx descriptor
+ *
+ * Move back the ethernet header at the "beginning" of the data buffer.
+ * (which has been moved in @ecrnx_amsdu_add_subframe_header)
+ */
+static void ecrnx_amsdu_del_subframe_header(struct ecrnx_amsdu_txhdr *amsdu_txhdr)
+{
+ struct sk_buff *skb = amsdu_txhdr->skb;
+ struct ethhdr *eth;
+ u8 *pos;
+
+ pos = skb->data;
+ pos += sizeof(struct ecrnx_amsdu_txhdr);
+ eth = (struct ethhdr*)pos;
+ pos += amsdu_txhdr->pad + sizeof(struct ethhdr);
+
+ if (ntohs(eth->h_proto) >= ETH_P_802_3_MIN) {
+ pos += sizeof(rfc1042_header) + 2;
+ }
+
+ memmove(pos, eth, sizeof(*eth));
+ skb_pull(skb, (pos - skb->data));
+}
+
+/**
+ * ecrnx_amsdu_add_subframe_header - Add AMSDU header and link subframe
+ *
+ * @ecrnx_hw Driver main data
+ * @skb Buffer to aggregate
+ * @sw_txhdr Tx descriptor for the first A-MSDU subframe
+ *
+ * return 0 on sucess, -1 otherwise
+ *
+ * This functions Add A-MSDU header and LLC/SNAP header in the buffer
+ * and update sw_txhdr of the first subframe to link this buffer.
+ * If an error happens, the buffer will be queued as a normal buffer.
+ *
+ *
+ * Before After
+ * +-------------+ +-------------+
+ * | HEADROOM | | HEADROOM |
+ * | | +-------------+ <- data
+ * | | | amsdu_txhdr |
+ * | | | * pad size |
+ * | | +-------------+
+ * | | | ETH hdr | keep original eth hdr
+ * | | | | to restore it once transmitted
+ * | | +-------------+ <- packet_addr[x]
+ * | | | Pad |
+ * | | +-------------+
+ * data -> +-------------+ | AMSDU HDR |
+ * | ETH hdr | +-------------+
+ * | | | LLC/SNAP |
+ * +-------------+ +-------------+
+ * | DATA | | DATA |
+ * | | | |
+ * +-------------+ +-------------+
+ *
+ * Called with tx_lock hold
+ */
+static int ecrnx_amsdu_add_subframe_header(struct ecrnx_hw *ecrnx_hw,
+ struct sk_buff *skb,
+ struct ecrnx_sw_txhdr *sw_txhdr)
+{
+ struct ecrnx_amsdu *amsdu = &sw_txhdr->amsdu;
+ struct ecrnx_amsdu_txhdr *amsdu_txhdr;
+ struct ethhdr *amsdu_hdr, *eth = (struct ethhdr *)skb->data;
+ int headroom_need, map_len, msdu_len, amsdu_len, map_oft = 0;
+#ifndef CONFIG_ECRNX_ESWIN
+ dma_addr_t dma_addr;
+#endif
+ u8 *pos, *map_start;
+
+ map_len = ECRNX_TX_DMA_MAP_LEN(skb);
+ msdu_len = skb->len - sizeof(*eth);
+ headroom_need = sizeof(*amsdu_txhdr) + amsdu->pad +
+ sizeof(*amsdu_hdr);
+ if (ntohs(eth->h_proto) >= ETH_P_802_3_MIN) {
+ headroom_need += sizeof(rfc1042_header) + 2;
+ msdu_len += sizeof(rfc1042_header) + 2;
+ }
+ amsdu_len = msdu_len + sizeof(*amsdu_hdr) + amsdu->pad;
+
+ /* we should have enough headroom (checked in xmit) */
+ if (WARN_ON(skb_headroom(skb) < headroom_need)) {
+ return -1;
+ }
+
+ /* allocate headroom */
+ pos = skb_push(skb, headroom_need);
+ amsdu_txhdr = (struct ecrnx_amsdu_txhdr *)pos;
+ pos += sizeof(*amsdu_txhdr);
+
+ /* move eth header */
+ memmove(pos, eth, sizeof(*eth));
+ eth = (struct ethhdr *)pos;
+ pos += sizeof(*eth);
+
+ /* Add padding from previous subframe */
+ map_start = pos;
+ memset(pos, 0, amsdu->pad);
+ pos += amsdu->pad;
+
+ /* Add AMSDU hdr */
+ amsdu_hdr = (struct ethhdr *)pos;
+ memcpy(amsdu_hdr->h_dest, eth->h_dest, ETH_ALEN);
+ memcpy(amsdu_hdr->h_source, eth->h_source, ETH_ALEN);
+ amsdu_hdr->h_proto = htons(msdu_len);
+ pos += sizeof(*amsdu_hdr);
+
+ if (ntohs(eth->h_proto) >= ETH_P_802_3_MIN) {
+ memcpy(pos, rfc1042_header, sizeof(rfc1042_header));
+ pos += sizeof(rfc1042_header) + 2;
+ }
+
+ if (amsdu_len < map_len) {
+ map_oft = map_len - amsdu_len;
+ map_start -= map_oft;
+ }
+ /* MAP (and sync) memory for DMA */
+#ifndef CONFIG_ECRNX_ESWIN
+ dma_addr = dma_map_single(ecrnx_hw->dev, map_start, map_len, DMA_BIDIRECTIONAL);
+ if (WARN_ON(dma_mapping_error(ecrnx_hw->dev, dma_addr))) {
+ pos -= sizeof(*eth);
+ memmove(pos, eth, sizeof(*eth));
+ skb_pull(skb, headroom_need);
+ return -1;
+ }
+#endif
+
+ /* update amdsu_txhdr */
+ amsdu_txhdr->map_len = map_len;
+#ifdef CONFIG_ECRNX_ESWIN
+ amsdu_txhdr->send_pos = map_start;
+#else
+ amsdu_txhdr->dma_addr = dma_addr;
+#endif
+ amsdu_txhdr->skb = skb;
+ amsdu_txhdr->pad = amsdu->pad;
+ amsdu_txhdr->msdu_len = msdu_len;
+
+ /* update ecrnx_sw_txhdr (of the first subframe) */
+ BUG_ON(amsdu->nb != sw_txhdr->desc.host.packet_cnt);
+#ifdef CONFIG_ECRNX_ESWIN
+ sw_txhdr->desc.host.packet_addr[amsdu->nb] = skb;
+#else
+ sw_txhdr->desc.host.packet_addr[amsdu->nb] = dma_addr + map_oft;
+#endif
+ sw_txhdr->desc.host.packet_len[amsdu->nb] = amsdu_len;
+ sw_txhdr->desc.host.packet_cnt++;
+ amsdu->nb++;
+
+ amsdu->pad = AMSDU_PADDING(amsdu_len - amsdu->pad);
+ list_add_tail(&amsdu_txhdr->list, &amsdu->hdrs);
+ amsdu->len += amsdu_len;
+
+ ecrnx_ipc_sta_buffer(ecrnx_hw, sw_txhdr->txq->sta,
+ sw_txhdr->txq->tid, msdu_len);
+
+ trace_amsdu_subframe(sw_txhdr);
+ return 0;
+}
+
+/**
+ * ecrnx_amsdu_add_subframe - Add this buffer as an A-MSDU subframe if possible
+ *
+ * @ecrnx_hw Driver main data
+ * @skb Buffer to aggregate if possible
+ * @sta Destination STA
+ * @txq sta's txq used for this buffer
+ *
+ * Tyr to aggregate the buffer in an A-MSDU. If it succeed then the
+ * buffer is added as a new A-MSDU subframe with AMSDU and LLC/SNAP
+ * headers added (so FW won't have to modify this subframe).
+ *
+ * To be added as subframe :
+ * - sta must allow amsdu
+ * - buffer must be aggregable (to be defined)
+ * - at least one other aggregable buffer is pending in the queue
+ * or an a-msdu (with enough free space) is currently in progress
+ *
+ * returns true if buffer has been added as A-MDSP subframe, false otherwise
+ *
+ */
+static bool ecrnx_amsdu_add_subframe(struct ecrnx_hw *ecrnx_hw, struct sk_buff *skb,
+ struct ecrnx_sta *sta, struct ecrnx_txq *txq)
+{
+ bool res = false;
+ struct ethhdr *eth;
+ ecrnx_adjust_amsdu_maxnb(ecrnx_hw);
+
+ /* immediately return if amsdu are not allowed for this sta */
+ if (!txq->amsdu_len || ecrnx_hw->mod_params->amsdu_maxnb < 2 ||
+ !ecrnx_amsdu_is_aggregable(skb)
+ )
+ return false;
+
+ spin_lock_bh(&ecrnx_hw->tx_lock);
+ if (txq->amsdu) {
+ /* aggreagation already in progress, add this buffer if enough space
+ available, otherwise end the current amsdu */
+ struct ecrnx_sw_txhdr *sw_txhdr = txq->amsdu;
+ eth = (struct ethhdr *)(skb->data);
+
+ if (((sw_txhdr->amsdu.len + sw_txhdr->amsdu.pad +
+ ecrnx_amsdu_subframe_length(eth, skb->len)) > txq->amsdu_len) ||
+ ecrnx_amsdu_add_subframe_header(ecrnx_hw, skb, sw_txhdr)) {
+ txq->amsdu = NULL;
+ goto end;
+ }
+
+ if (sw_txhdr->amsdu.nb >= ecrnx_hw->mod_params->amsdu_maxnb) {
+ ecrnx_hw->stats.amsdus[sw_txhdr->amsdu.nb - 1].done++;
+ /* max number of subframes reached */
+ txq->amsdu = NULL;
+ }
+ } else {
+ /* Check if a new amsdu can be started with the previous buffer
+ (if any) and this one */
+ struct sk_buff *skb_prev = skb_peek_tail(&txq->sk_list);
+ struct ecrnx_txhdr *txhdr;
+ struct ecrnx_sw_txhdr *sw_txhdr;
+ int len1, len2;
+
+ if (!skb_prev || !ecrnx_amsdu_is_aggregable(skb_prev))
+ goto end;
+
+ txhdr = (struct ecrnx_txhdr *)skb_prev->data;
+ sw_txhdr = txhdr->sw_hdr;
+ if ((sw_txhdr->amsdu.len) ||
+ (sw_txhdr->desc.host.flags & TXU_CNTRL_RETRY))
+ /* previous buffer is already a complete amsdu or a retry */
+ goto end;
+
+ eth = (struct ethhdr *)(skb_prev->data + sw_txhdr->headroom);
+ len1 = ecrnx_amsdu_subframe_length(eth, (sw_txhdr->frame_len +
+ sizeof(struct ethhdr)));
+
+ eth = (struct ethhdr *)(skb->data);
+ len2 = ecrnx_amsdu_subframe_length(eth, skb->len);
+
+ if (len1 + AMSDU_PADDING(len1) + len2 > txq->amsdu_len)
+ /* not enough space to aggregate those two buffers */
+ goto end;
+
+ /* Add subframe header.
+ Note: Fw will take care of adding AMDSU header for the first
+ subframe while generating 802.11 MAC header */
+ INIT_LIST_HEAD(&sw_txhdr->amsdu.hdrs);
+ sw_txhdr->amsdu.len = len1;
+ sw_txhdr->amsdu.nb = 1;
+ sw_txhdr->amsdu.pad = AMSDU_PADDING(len1);
+ if (ecrnx_amsdu_add_subframe_header(ecrnx_hw, skb, sw_txhdr))
+ goto end;
+
+ sw_txhdr->desc.host.flags |= TXU_CNTRL_AMSDU;
+
+ if (sw_txhdr->amsdu.nb < ecrnx_hw->mod_params->amsdu_maxnb)
+ txq->amsdu = sw_txhdr;
+ else
+ ecrnx_hw->stats.amsdus[sw_txhdr->amsdu.nb - 1].done++;
+ }
+
+ res = true;
+
+ end:
+ spin_unlock_bh(&ecrnx_hw->tx_lock);
+ return res;
+}
+/**
+ * ecrnx_amsdu_dismantle - Dismantle an already formatted A-MSDU
+ *
+ * @ecrnx_hw Driver main data
+ * @sw_txhdr_main Software descriptor of the A-MSDU to dismantle.
+ *
+ * The a-mdsu is always fully dismantled (i.e don't try to reduce it's size to
+ * fit the new limit).
+ * The DMA mapping can be re-used as ecrnx_amsdu_add_subframe_header ensure that
+ * enough data in the skb bufer are 'DMA mapped'.
+ * It would have been slightly simple to unmap/re-map but it is a little faster like this
+ * and not that much more complicated to read.
+ */
+static void ecrnx_amsdu_dismantle(struct ecrnx_hw *ecrnx_hw, struct ecrnx_sw_txhdr *sw_txhdr_main)
+{
+ struct ecrnx_amsdu_txhdr *amsdu_txhdr, *next;
+ struct sk_buff *skb_prev = sw_txhdr_main->skb;
+ struct ecrnx_txq *txq = sw_txhdr_main->txq;
+ trace_amsdu_dismantle(sw_txhdr_main);
+ ecrnx_hw->stats.amsdus[sw_txhdr_main->amsdu.nb - 1].done--;
+ sw_txhdr_main->amsdu.len = 0;
+ sw_txhdr_main->amsdu.nb = 0;
+ sw_txhdr_main->desc.host.flags &= ~TXU_CNTRL_AMSDU;
+ sw_txhdr_main->desc.host.packet_cnt = 1;
+ list_for_each_entry_safe(amsdu_txhdr, next, &sw_txhdr_main->amsdu.hdrs, list) {
+ struct ecrnx_txhdr *txhdr;
+ struct ecrnx_sw_txhdr *sw_txhdr;
+ dma_addr_t dma_addr = amsdu_txhdr->dma_addr;
+ size_t map_len = amsdu_txhdr->map_len;
+ size_t tx_map_len;
+ size_t data_oft, cfm_oft = 0;
+ struct sk_buff *skb = amsdu_txhdr->skb;
+ int headroom;
+ list_del(&amsdu_txhdr->list);
+ ecrnx_ipc_sta_buffer(ecrnx_hw, txq->sta, txq->tid, -amsdu_txhdr->msdu_len);
+ ecrnx_amsdu_del_subframe_header(amsdu_txhdr);
+ headroom = ECRNX_TX_HEADROOM(skb);
+ tx_map_len = ECRNX_TX_DMA_MAP_LEN(skb);
+ sw_txhdr = kmem_cache_alloc(ecrnx_hw->sw_txhdr_cache, GFP_ATOMIC);
+ if (unlikely((skb_headroom(skb) < headroom) ||
+ (sw_txhdr == NULL) || (tx_map_len > map_len))) {
+ if (sw_txhdr)
+ kmem_cache_free(ecrnx_hw->sw_txhdr_cache, sw_txhdr);
+ dma_unmap_single(ecrnx_hw->dev, dma_addr, map_len, DMA_TO_DEVICE);
+ dev_kfree_skb_any(skb);
+ continue;
+ }
+ sw_txhdr->headroom = headroom;
+ cfm_oft = map_len - tx_map_len;
+ data_oft = sizeof(struct ethhdr) + ECRNX_TX_DATA_OFT(sw_txhdr) + cfm_oft;
+ txhdr = skb_push(skb, headroom);
+ txhdr->sw_hdr = sw_txhdr;
+ memcpy(sw_txhdr, sw_txhdr_main, sizeof(*sw_txhdr));
+ sw_txhdr->frame_len = map_len - data_oft;
+ sw_txhdr->skb = skb;
+ sw_txhdr->headroom = headroom;
+ txhdr->hw_hdr.cfm.status.value = 0;
+ sw_txhdr->map_len = map_len;
+ sw_txhdr->dma_addr = dma_addr;
+ sw_txhdr->desc.host.packet_addr[0] = dma_addr + data_oft;
+ sw_txhdr->desc.host.status_desc_addr = dma_addr + cfm_oft;
+ sw_txhdr->desc.host.packet_len[0] = sw_txhdr->frame_len;
+ sw_txhdr->desc.host.packet_cnt = 1;
+ ecrnx_txq_queue_skb(skb, sw_txhdr->txq, ecrnx_hw, false, skb_prev);
+ skb_prev = skb;
+ }
+}
+/**
+ * ecrnx_amsdu_update_len - Update length allowed for A-MSDU on a TXQ
+ *
+ * @ecrnx_hw Driver main data.
+ * @txq The TXQ.
+ * @amsdu_len New length allowed ofr A-MSDU.
+ *
+ * If this is a TXQ linked to a STA and the allowed A-MSDU size is reduced it is
+ * then necessary to disassemble all A-MSDU currently queued on all STA' txq that
+ * are larger than this new limit.
+ * Does nothing if the A-MSDU limit increase or stay the same.
+ */
+static void ecrnx_amsdu_update_len(struct ecrnx_hw *ecrnx_hw, struct ecrnx_txq *txq,
+ u16 amsdu_len)
+{
+ struct ecrnx_sta *sta = txq->sta;
+ int tid;
+
+ if (amsdu_len != txq->amsdu_len)
+ trace_amsdu_len_update(txq->sta, amsdu_len);
+
+ if (amsdu_len >= txq->amsdu_len) {
+ txq->amsdu_len = amsdu_len;
+ return;
+ }
+
+ if (!sta) {
+ netdev_err(txq->ndev, "Non STA txq(%d) with a-amsdu len %d\n",
+ txq->idx, amsdu_len);
+ txq->amsdu_len = 0;
+ return;
+ }
+
+ /* A-MSDU size has been reduced by the firmware, need to dismantle all
+ queued a-msdu that are too large. Need to do this for all txq of the STA. */
+ foreach_sta_txq(sta, txq, tid, ecrnx_hw) {
+ struct sk_buff *skb, *skb_next;
+
+ if (txq->amsdu_len <= amsdu_len)
+ continue;
+
+ if (txq->last_retry_skb)
+ skb = txq->last_retry_skb->next;
+ else
+ skb = txq->sk_list.next;
+
+ skb_queue_walk_from_safe(&txq->sk_list, skb, skb_next) {
+ struct ecrnx_txhdr *txhdr = (struct ecrnx_txhdr *)skb->data;
+ struct ecrnx_sw_txhdr *sw_txhdr = txhdr->sw_hdr;
+ if ((sw_txhdr->desc.host.flags & TXU_CNTRL_AMSDU) &&
+ (sw_txhdr->amsdu.len > amsdu_len))
+ ecrnx_amsdu_dismantle(ecrnx_hw, sw_txhdr);
+
+ if (txq->amsdu == sw_txhdr)
+ txq->amsdu = NULL;
+ }
+
+ txq->amsdu_len = amsdu_len;
+ }
+}
+#endif /* CONFIG_ECRNX_AMSDUS_TX */
+
+/**
+ * netdev_tx_t (*ndo_start_xmit)(struct sk_buff *skb,
+ * struct net_device *dev);
+ * Called when a packet needs to be transmitted.
+ * Must return NETDEV_TX_OK , NETDEV_TX_BUSY.
+ * (can also return NETDEV_TX_LOCKED if NETIF_F_LLTX)
+ *
+ * - Initialize the desciptor for this pkt (stored in skb before data)
+ * - Push the pkt in the corresponding Txq
+ * - If possible (i.e. credit available and not in PS) the pkt is pushed
+ * to fw
+ */
+netdev_tx_t ecrnx_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct ecrnx_vif *ecrnx_vif = netdev_priv(dev);
+ struct ecrnx_hw *ecrnx_hw = ecrnx_vif->ecrnx_hw;
+ struct ecrnx_txhdr *txhdr;
+ struct ecrnx_sw_txhdr *sw_txhdr = NULL;
+ struct ethhdr *eth;
+ struct txdesc_api *desc;
+ struct ecrnx_sta *sta;
+ struct ecrnx_txq *txq;
+ int headroom = 0, hdr_pads = 0;
+ u16 frame_len;
+ u64_l skb_addr;
+
+ u8 tid;
+
+ sk_pacing_shift_update(skb->sk, ecrnx_hw->tcp_pacing_shift);
+
+ /* check whether the current skb can be used */
+ if (skb_shared(skb) || (skb_headroom(skb) < ECRNX_TX_MAX_HEADROOM) ||
+ (skb_cloned(skb) && (dev->priv_flags & IFF_BRIDGE_PORT))) {
+ struct sk_buff *newskb = skb_copy_expand(skb, ECRNX_TX_MAX_HEADROOM, 0, GFP_ATOMIC);
+ if (unlikely(newskb == NULL))
+ goto free;
+
+ dev_kfree_skb_any(skb);
+
+ skb = newskb;
+ }
+
+ /* Get the STA id and TID information */
+ sta = ecrnx_get_tx_info(ecrnx_vif, skb, &tid);
+ if (!sta)
+ goto free;
+
+ txq = ecrnx_txq_sta_get(sta, tid, ecrnx_hw);
+ ECRNX_DBG("%s-%d:sta:0x%p,sta_idx:%d, sta_mac:%pM, tid:%d, ecrnx_hw:0x%p, txq:0x%p \n", __func__, __LINE__, sta, sta->sta_idx, sta->mac_addr, tid, ecrnx_hw, txq);
+ if (txq->idx == TXQ_INACTIVE)
+ goto free;
+
+#ifdef CONFIG_ECRNX_AMSDUS_TX
+ if (ecrnx_amsdu_add_subframe(ecrnx_hw, skb, sta, txq))
+ return NETDEV_TX_OK;
+#endif
+
+ sw_txhdr = kmem_cache_alloc(ecrnx_hw->sw_txhdr_cache, GFP_ATOMIC);
+ if (unlikely(sw_txhdr == NULL))
+ goto free;
+
+ /* Retrieve the pointer to the Ethernet data */
+ eth = (struct ethhdr *)skb->data;
+
+
+#if 0
+ if ((skb->data[0] & 0x1) && (skb->data[0] != 0xff)) {
+ printk("drop mc pkt 0x%x\n", skb->data[0]);
+ goto free;
+ }
+#endif
+
+#if 1
+ if (0xDD86 == eth->h_proto) { //ipv6
+ //printk("%s-%d: eapol\n", __func__, __LINE__);
+ goto free;
+ //dump_xxx_buf(skb->data, skb->len);
+ }
+#endif
+#if 0
+ if (0x8e88 == eth->h_proto) { //icmp
+ printk("%s-%d: eapol\n", __func__, __LINE__);
+ //dump_xxx_buf(skb->data, skb->len);
+ }
+#endif
+
+#if 0
+
+ if (8 == eth->h_proto && 0x1 == skb->data[23]) { //icmp
+ memset(skb->data + 14, 0xff, skb->len - 14);
+ }
+ if (8 == eth->h_proto && 0x11 == skb->data[23]) {
+ printk("---drop udp pkt\n");
+ goto free;
+ }
+#endif
+ //no_encrypt = check_eapol_dont_encrypt(skb);
+
+ hdr_pads = ECRNX_SWTXHDR_ALIGN_PADS((long)eth);
+ /* Use headroom to store struct ecrnx_txhdr */
+ headroom = ECRNX_TX_HEADROOM(skb);
+
+ txhdr = (struct ecrnx_txhdr *)skb_push(skb, headroom);
+ txhdr->sw_hdr = sw_txhdr;
+ frame_len = (u16)skb->len - headroom - sizeof(*eth);
+
+ sw_txhdr->txq = txq;
+ sw_txhdr->frame_len = frame_len;
+ sw_txhdr->ecrnx_sta = sta;
+ sw_txhdr->ecrnx_vif = ecrnx_vif;
+ sw_txhdr->skb = skb;
+ sw_txhdr->headroom = headroom;
+#ifdef CONFIG_ECRNX_ESWIN
+ sw_txhdr->offset = headroom + sizeof(*eth);
+#else
+ sw_txhdr->map_len = skb->len - offsetof(struct ecrnx_txhdr, hw_hdr);
+#endif
+ sw_txhdr->jiffies = jiffies;
+#ifdef CONFIG_ECRNX_AMSDUS_TX
+ sw_txhdr->amsdu.len = 0;
+ sw_txhdr->amsdu.nb = 0;
+#endif
+ // Fill-in the descriptor
+ desc = &sw_txhdr->desc;
+ memcpy(&desc->host.eth_dest_addr, eth->h_dest, ETH_ALEN);
+ memcpy(&desc->host.eth_src_addr, eth->h_source, ETH_ALEN);
+ desc->host.ethertype = eth->h_proto;
+ desc->host.staid = sta->sta_idx;
+ desc->host.tid = tid;
+ if (unlikely(ecrnx_vif->wdev.iftype == NL80211_IFTYPE_AP_VLAN))
+ desc->host.vif_idx = ecrnx_vif->ap_vlan.master->vif_index;
+ else
+ desc->host.vif_idx = ecrnx_vif->vif_index;
+ desc->host.flags = 0;
+
+ if (ecrnx_vif->use_4addr && (sta->sta_idx < NX_REMOTE_STA_MAX))
+ desc->host.flags |= TXU_CNTRL_USE_4ADDR;
+
+ if ((ecrnx_vif->tdls_status == TDLS_LINK_ACTIVE) &&
+ ecrnx_vif->sta.tdls_sta &&
+ (memcmp(desc->host.eth_dest_addr.array, ecrnx_vif->sta.tdls_sta->mac_addr, ETH_ALEN) == 0)) {
+ desc->host.flags |= TXU_CNTRL_TDLS;
+ ecrnx_vif->sta.tdls_sta->tdls.last_tid = desc->host.tid;
+ ecrnx_vif->sta.tdls_sta->tdls.last_sn = desc->host.sn;
+ }
+
+ if ((ecrnx_vif->wdev.iftype == NL80211_IFTYPE_MESH_POINT) &&
+ (ecrnx_vif->is_resending))
+ desc->host.flags |= TXU_CNTRL_MESH_FWD;
+
+#ifdef CONFIG_ECRNX_SPLIT_TX_BUF
+ desc->host.packet_len[0] = frame_len;
+#else
+ desc->host.packet_len = frame_len;
+#endif
+
+ txhdr->hw_hdr.cfm.status.value = 0;
+
+#ifdef CONFIG_ECRNX_ESWIN
+ skb_addr = (ptr_addr)skb;
+#ifdef CONFIG_ECRNX_SPLIT_TX_BUF
+ desc->host.packet_addr[0] = (u64_l)skb;
+ desc->host.packet_cnt = 1;
+#else
+ //desc->host.packet_addr = (u64_l)skb;
+ desc->host.packet_addr[0] = (u32_l)skb_addr;
+ desc->host.packet_addr[1] = (u32_l)(skb_addr >> 32);
+#endif
+ //desc->host.status_desc_addr = (u64_l)skb;
+ desc->host.status_desc_addr[0] = (u32_l)skb_addr;
+ desc->host.status_desc_addr[1] = (u32_l)(skb_addr >> 32);
+
+ /*if (no_encrypt) {
+ desc->host.flags |= TXU_CNTRL_NO_ENCRYPT;
+ }*/
+#else //CONFIG_ECRNX_ESWIN_SDIO
+ if (unlikely(ecrnx_prep_dma_tx(ecrnx_hw, txhdr, true)))
+ goto free;
+#endif //CONFIG_ECRNX_ESWIN_SDIO
+ //ECRNX_DBG("%s:desc:0x%08x, vif_idx:%d, skb:0x%08x, headroom:%d !!! \n", __func__, desc, desc->host.vif_idx, skb, headroom);
+ spin_lock_bh(&ecrnx_hw->tx_lock);
+ if (ecrnx_txq_queue_skb(skb, txq, ecrnx_hw, false, NULL))
+ {
+ ECRNX_DBG("%s-%d:txdesc:0x%x, skb:0x%08x, skb->len:%d \n", __func__, __LINE__, desc, skb, skb->len);
+ ecrnx_hwq_process(ecrnx_hw, txq->hwq);
+ }
+ else
+ {
+ ECRNX_DBG("%s-%d: delay send(put txq), txq:0x%p, queue status 0x%x, skb:0x%08x, skb->len:%d !!! \n", __func__, __LINE__, txq, txq->status, skb, skb->len);
+ }
+ spin_unlock_bh(&ecrnx_hw->tx_lock);
+
+ return NETDEV_TX_OK;
+
+free:
+ if (sw_txhdr)
+ kmem_cache_free(ecrnx_hw->sw_txhdr_cache, sw_txhdr);
+ if (headroom)
+ skb_pull(skb, headroom);
+ dev_kfree_skb_any(skb);
+
+ return NETDEV_TX_OK;
+}
+
+/**
+ * ecrnx_start_mgmt_xmit - Transmit a management frame
+ *
+ * @vif: Vif that send the frame
+ * @sta: Destination of the frame. May be NULL if the destiantion is unknown
+ * to the AP.
+ * @params: Mgmt frame parameters
+ * @offchan: Indicate whether the frame must be send via the offchan TXQ.
+ * (is is redundant with params->offchan ?)
+ * @cookie: updated with a unique value to identify the frame with upper layer
+ *
+ */
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0))
+int ecrnx_start_mgmt_xmit(struct ecrnx_vif *vif, struct ecrnx_sta *sta,
+ struct cfg80211_mgmt_tx_params *params, bool offchan,
+ u64 *cookie)
+#else
+int ecrnx_start_mgmt_xmit(struct ecrnx_vif *vif, struct ecrnx_sta *sta,
+ struct ieee80211_channel *channel, bool offchan,
+ unsigned int wait, const u8* buf, size_t len,
+ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0))
+ bool no_cck,
+ #endif
+ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 3, 0))
+ bool dont_wait_for_ack,
+ #endif
+ u64 *cookie)
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) */
+{
+ struct ecrnx_hw *ecrnx_hw = vif->ecrnx_hw;
+ struct ecrnx_txhdr *txhdr;
+ struct ecrnx_sw_txhdr *sw_txhdr;
+ struct txdesc_api *desc;
+ struct sk_buff *skb;
+ u16 frame_len, headroom;
+ u8 *data;
+ struct ecrnx_txq *txq;
+ bool robust;
+ u64_l skb_addr;
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0))
+ const u8 *buf = params->buf;
+ size_t len = params->len;
+ bool no_cck = params->no_cck;
+#endif
+
+
+#ifdef CONFIG_ECRNX_ESWIN
+ headroom = sizeof(struct ecrnx_txhdr) + ECRNX_TX_TXDESC_API_ALIGN;
+#else
+ headroom = sizeof(struct ecrnx_txhdr);
+#endif
+ frame_len = len;
+
+ //----------------------------------------------------------------------
+
+ /* Set TID and Queues indexes */
+ if (sta) {
+ txq = ecrnx_txq_sta_get(sta, 8, ecrnx_hw);
+ } else {
+ if (offchan)
+ txq = &ecrnx_hw->txq[NX_OFF_CHAN_TXQ_IDX];
+ else
+ txq = ecrnx_txq_vif_get(vif, NX_UNK_TXQ_TYPE);
+ }
+
+ /* Ensure that TXQ is active */
+ if (txq->idx == TXQ_INACTIVE) {
+ if(sta){
+ netdev_dbg(vif->ndev, "TXQ inactive\n");
+ return -EBUSY;
+ }else{
+ return 0;
+ }
+ }
+
+ /*
+ * Create a SK Buff object that will contain the provided data
+ */
+ skb = dev_alloc_skb(headroom + frame_len);
+
+ if (!skb) {
+ return -ENOMEM;
+ }
+
+ *cookie = (unsigned long)skb;
+
+ sw_txhdr = kmem_cache_alloc(ecrnx_hw->sw_txhdr_cache, GFP_ATOMIC);
+ if (unlikely(sw_txhdr == NULL)) {
+ dev_kfree_skb(skb);
+ return -ENOMEM;
+ }
+ /*
+ * Move skb->data pointer in order to reserve room for ecrnx_txhdr
+ * headroom value will be equal to sizeof(struct ecrnx_txhdr)
+ */
+ skb_reserve(skb, headroom);
+
+ /*
+ * Extend the buffer data area in order to contain the provided packet
+ * len value (for skb) will be equal to param->len
+ */
+ data = skb_put(skb, frame_len);
+ /* Copy the provided data */
+ memcpy(data, buf, frame_len);
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 15, 0))
+ robust = ieee80211_is_robust_mgmt_frame(skb);
+#else
+ if (skb->len < 25){
+ robust = false;
+ }
+ robust = ieee80211_is_robust_mgmt_frame((void *)skb->data);
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 15, 0) */
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0))
+ /* Update CSA counter if present */
+ if (unlikely(params->n_csa_offsets) &&
+ vif->wdev.iftype == NL80211_IFTYPE_AP &&
+ vif->ap.csa) {
+ int i;
+
+ data = skb->data;
+ for (i = 0; i < params->n_csa_offsets ; i++) {
+ data[params->csa_offsets[i]] = vif->ap.csa->count;
+ }
+ }
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0) */
+
+ /*
+ * Go back to the beginning of the allocated data area
+ * skb->data pointer will move backward
+ */
+ txhdr = (struct ecrnx_txhdr *)skb_push(skb, headroom);
+
+ //----------------------------------------------------------------------
+
+ /* Fill the TX Header */
+
+
+ //----------------------------------------------------------------------
+
+ /* Fill the SW TX Header */
+ txhdr->sw_hdr = sw_txhdr;
+
+ sw_txhdr->txq = txq;
+ sw_txhdr->frame_len = frame_len;
+ sw_txhdr->ecrnx_sta = sta;
+ sw_txhdr->ecrnx_vif = vif;
+ sw_txhdr->skb = skb;
+ sw_txhdr->headroom = headroom;
+#ifdef CONFIG_ECRNX_ESWIN
+ sw_txhdr->offset = headroom; //sizeof(struct ecrnx_txhdr) + sizeof(struct txdesc_api);
+#else
+ sw_txhdr->map_len = skb->len - offsetof(struct ecrnx_txhdr, hw_hdr);
+#endif
+ sw_txhdr->jiffies = jiffies;
+#ifdef CONFIG_ECRNX_AMSDUS_TX
+ sw_txhdr->amsdu.len = 0;
+ sw_txhdr->amsdu.nb = 0;
+#endif
+ //----------------------------------------------------------------------
+
+ /* Fill the Descriptor to be provided to the MAC SW */
+ desc = &sw_txhdr->desc;
+
+ desc->host.staid = (sta) ? sta->sta_idx : 0xFF;
+ desc->host.vif_idx = vif->vif_index;
+ desc->host.tid = 0xFF;
+ desc->host.flags = TXU_CNTRL_MGMT;
+ if (robust)
+ desc->host.flags |= TXU_CNTRL_MGMT_ROBUST;
+
+
+ if (no_cck) {
+ desc->host.flags |= TXU_CNTRL_MGMT_NO_CCK;
+ }
+ /* baoyong */
+#ifdef CONFIG_RWNX_SPLIT_TX_BUF
+ desc->host.packet_len[0] = frame_len;
+#else
+ desc->host.packet_len = frame_len;
+#endif
+
+ txhdr->hw_hdr.cfm.status.value = 0;
+#ifdef CONFIG_ECRNX_ESWIN
+ skb_addr = (ptr_addr)skb;
+#ifdef CONFIG_ECRNX_SPLIT_TX_BUF
+ desc->host.packet_addr[0] = skb;
+ desc->host.packet_cnt = 1;
+#else
+ //desc->host.packet_addr = (u64_l)skb;
+ desc->host.packet_addr[0] = (u32_l)skb_addr;
+ desc->host.packet_addr[1] = (u32_l)(skb_addr >> 32);
+#endif
+ //desc->host.status_desc_addr = (u64_l)skb;
+ desc->host.packet_addr[0] = (u32_l)skb_addr;
+ desc->host.packet_addr[1] = (u32_l)(skb_addr >> 32);
+#else //CONFIG_ECRNX_ESWIN_SDIO
+
+ /* Get DMA Address */
+ if (unlikely(ecrnx_prep_dma_tx(ecrnx_hw, txhdr, false))) {
+ kmem_cache_free(ecrnx_hw->sw_txhdr_cache, sw_txhdr);
+ dev_kfree_skb(skb);
+ return -EBUSY;
+ }
+#endif
+#ifdef CONFIG_ECRNX_SPLIT_TX_BUF
+#endif
+
+ //----------------------------------------------------------------------
+
+ spin_lock_bh(&ecrnx_hw->tx_lock);
+ if (ecrnx_txq_queue_skb(skb, txq, ecrnx_hw, false, NULL)) {
+ ECRNX_DBG("%s-%d:txdesc:0x%x, skb:0x%08x, skb->len:%d \n", __func__, __LINE__, desc, skb, skb->len);
+ ecrnx_hwq_process(ecrnx_hw, txq->hwq);
+ } else {
+ ECRNX_DBG("%s-%d: delay send(put txq), queue status 0x%x, skb:0x%08x, skb->len:%d !!! \n", __func__, __LINE__, txq->status, skb, skb->len);
+ }
+ spin_unlock_bh(&ecrnx_hw->tx_lock);
+
+ return 0;
+}
+
+int ecrnx_handle_tx_datacfm(void *priv, void *host_id)
+{
+ struct ecrnx_hw *ecrnx_hw = (struct ecrnx_hw *)priv;
+ struct sk_buff *skb = host_id;
+ struct ecrnx_hwq *hwq;
+ struct ecrnx_txq *txq;
+ struct ecrnx_sta *sta;
+ struct ecrnx_txhdr *txhdr;
+
+#if defined(CONFIG_ECRNX_ESWIN_USB)
+ txhdr = (struct ecrnx_txhdr *)(*((ptr_addr*)skb->data - 1));
+#else
+ txhdr = (struct ecrnx_txhdr *)skb->data;
+#endif
+
+ struct ecrnx_sw_txhdr *sw_txhdr = txhdr->sw_hdr;
+
+ /* Check status in the header. If status is null, it means that the buffer
+ * was not transmitted and we have to return immediately */
+ ECRNX_DBG("%s:hostid(tx_skb):0x%08x\n", __func__, skb);
+
+ txq = sw_txhdr->txq;
+ /* don't use txq->hwq as it may have changed between push and confirm */
+ hwq = &ecrnx_hw->hwq[sw_txhdr->hw_queue];
+ ecrnx_txq_confirm_any(ecrnx_hw, txq, hwq, sw_txhdr);
+
+ if (txq->idx != TXQ_INACTIVE) {
+
+ txq->credits += 1;
+ //printk("finish_cfm: txq->credits %d 0x%08x\n", txq->credits,skb);
+ if (txq->credits <= 0){
+ ecrnx_txq_stop(txq, ECRNX_TXQ_STOP_FULL);
+ }
+ else if (txq->credits > 0)
+ {
+ ecrnx_txq_start(txq, ECRNX_TXQ_STOP_FULL);
+ /* baoyong:handle the pkts in sk_list right now */
+ if (txq->idx != TXQ_INACTIVE && !skb_queue_empty(&txq->sk_list))
+ {
+ ecrnx_hwq_process(ecrnx_hw, txq->hwq);
+ }
+ }
+
+ /* continue service period */
+ if (unlikely(txq->push_limit && !ecrnx_txq_is_full(txq))) {
+ ecrnx_txq_add_to_hw_list(txq);
+ }
+ }
+
+ /* Update statistics */
+ sw_txhdr->ecrnx_vif->net_stats.tx_packets++;
+ sw_txhdr->ecrnx_vif->net_stats.tx_bytes += sw_txhdr->frame_len;
+
+ sta = txq->sta;
+ if(sta)
+ {
+ sta = txq->sta;
+ sta->stats.tx_pkts ++;
+ sta->stats.tx_bytes += sw_txhdr->frame_len;
+ sta->stats.last_act = ecrnx_hw->stats.last_tx;
+ }
+ //printk("sta->stats.tx_pkts=%d sta->stats.tx_bytes =%d\n", sta->stats.tx_pkts, sta->stats.tx_bytes);
+
+ kmem_cache_free(ecrnx_hw->sw_txhdr_cache, sw_txhdr);
+ skb_pull(skb, sw_txhdr->headroom);
+ consume_skb(skb);
+ return 0;
+}
+
+/**
+ * ecrnx_txdatacfm - FW callback for TX confirmation
+ *
+ * called with tx_lock hold
+ */
+int ecrnx_txdatacfm(void *pthis, void *host_id)
+{
+ struct ecrnx_hw *ecrnx_hw = (struct ecrnx_hw *)pthis;
+ struct sk_buff *skb = host_id;
+ struct ecrnx_txhdr *txhdr;
+ union ecrnx_hw_txstatus ecrnx_txst;
+ struct ecrnx_sw_txhdr *sw_txhdr;
+ struct ecrnx_hwq *hwq;
+ struct ecrnx_txq *txq;
+#ifndef CONFIG_ECRNX_ESWIN
+ dma_addr_t cfm_dma_addr;
+#endif
+ size_t cfm_len;
+
+#if defined(CONFIG_ECRNX_ESWIN_USB)
+ txhdr = (struct ecrnx_txhdr *)host_id;
+ skb = txhdr->sw_hdr->skb;
+ skb_push(skb, sizeof(struct ecrnx_txhdr) - sizeof(u32_l));
+#else
+ txhdr = (struct ecrnx_txhdr *)skb->data;
+#endif
+ sw_txhdr = txhdr->sw_hdr;
+ cfm_len = sizeof(txhdr->hw_hdr.cfm);
+
+ //ECRNX_DBG("%s-%d: skb:0x%08x, skb->len:%d \n", __func__, __LINE__, skb, skb->len);
+#ifndef CONFIG_ECRNX_ESWIN
+ cfm_dma_addr = (ptr_addr)sw_txhdr->desc.host.status_desc_addr;
+ dma_sync_single_for_cpu(ecrnx_hw->dev, cfm_dma_addr, cfm_len, DMA_FROM_DEVICE);
+#endif
+ /* Read status in the TX control header */
+ ecrnx_txst = txhdr->hw_hdr.cfm.status;
+
+ /* Check status in the header. If status is null, it means that the buffer
+ * was not transmitted and we have to return immediately */
+ if (ecrnx_txst.value == 0) {
+#ifndef CONFIG_ECRNX_ESWIN
+ dma_sync_single_for_device(ecrnx_hw->dev, cfm_dma_addr, cfm_len, DMA_FROM_DEVICE);
+#endif
+ return -1;
+ }
+
+ txq = sw_txhdr->txq;
+ /* don't use txq->hwq as it may have changed between push and confirm */
+ hwq = &ecrnx_hw->hwq[sw_txhdr->hw_queue];
+ ecrnx_txq_confirm_any(ecrnx_hw, txq, hwq, sw_txhdr);
+
+ /* Update txq and HW queue credits */
+ if (sw_txhdr->desc.host.flags & TXU_CNTRL_MGMT) {
+ trace_mgmt_cfm(sw_txhdr->ecrnx_vif->vif_index,
+ (sw_txhdr->ecrnx_sta) ? sw_txhdr->ecrnx_sta->sta_idx : 0xFF,
+ ecrnx_txst.acknowledged);
+
+ /* Confirm transmission to CFG80211 */
+ cfg80211_mgmt_tx_status(&sw_txhdr->ecrnx_vif->wdev,
+ (unsigned long)skb,
+ (skb->data + sw_txhdr->headroom),
+ sw_txhdr->frame_len,
+ ecrnx_txst.acknowledged,
+ GFP_ATOMIC);
+ } else if ((txq->idx != TXQ_INACTIVE) &&
+ (ecrnx_txst.retry_required || ecrnx_txst.sw_retry_required)) {
+ bool sw_retry = (ecrnx_txst.sw_retry_required) ? true : false;
+
+ /* Reset the status */
+ txhdr->hw_hdr.cfm.status.value = 0;
+
+ /* The confirmed packet was part of an AMPDU and not acked
+ * correctly, so reinject it in the TX path to be retried */
+ ecrnx_tx_retry(ecrnx_hw, skb, txhdr, sw_retry);
+ return 0;
+ }
+
+ trace_skb_confirm(skb, txq, hwq, &txhdr->hw_hdr.cfm);
+
+ /* STA may have disconnect (and txq stopped) when buffers were stored
+ in fw. In this case do nothing when they're returned */
+ if (txq->idx != TXQ_INACTIVE) {
+ if (txhdr->hw_hdr.cfm.credits) {
+ txq->credits += txhdr->hw_hdr.cfm.credits;
+ if (txq->credits <= 0)
+ ecrnx_txq_stop(txq, ECRNX_TXQ_STOP_FULL);
+ else if (txq->credits > 0)
+ {
+ ecrnx_txq_start(txq, ECRNX_TXQ_STOP_FULL);
+ /* baoyong:handle the pkts in sk_list right now */
+ if (txq->idx != TXQ_INACTIVE && !skb_queue_empty(&txq->sk_list))
+ {
+ ecrnx_hwq_process(ecrnx_hw, txq->hwq);
+ }
+
+ }
+ }
+
+ /* continue service period */
+ if (unlikely(txq->push_limit && !ecrnx_txq_is_full(txq))) {
+ ecrnx_txq_add_to_hw_list(txq);
+ }
+ }
+
+ if (txhdr->hw_hdr.cfm.ampdu_size &&
+ txhdr->hw_hdr.cfm.ampdu_size < IEEE80211_MAX_AMPDU_BUF)
+ ecrnx_hw->stats.ampdus_tx[txhdr->hw_hdr.cfm.ampdu_size - 1]++;
+
+#ifdef CONFIG_ECRNX_AMSDUS_TX
+ ecrnx_amsdu_update_len(ecrnx_hw, txq, txhdr->hw_hdr.cfm.amsdu_size);
+#endif
+
+ /* Update statistics */
+ sw_txhdr->ecrnx_vif->net_stats.tx_packets++;
+ sw_txhdr->ecrnx_vif->net_stats.tx_bytes += sw_txhdr->frame_len;
+
+ /* Release SKBs */
+#ifdef CONFIG_ECRNX_AMSDUS_TX
+ if (sw_txhdr->desc.host.flags & TXU_CNTRL_AMSDU) {
+ struct ecrnx_amsdu_txhdr *amsdu_txhdr;
+ list_for_each_entry(amsdu_txhdr, &sw_txhdr->amsdu.hdrs, list) {
+ ecrnx_amsdu_del_subframe_header(amsdu_txhdr);
+#ifndef CONFIG_ECRNX_ESWIN
+ dma_unmap_single(ecrnx_hw->dev, amsdu_txhdr->dma_addr,
+ amsdu_txhdr->map_len, DMA_TO_DEVICE);
+#endif
+ ecrnx_ipc_sta_buffer(ecrnx_hw, txq->sta, txq->tid,
+ -amsdu_txhdr->msdu_len);
+ ecrnx_tx_statistic(ecrnx_hw, txq, ecrnx_txst, amsdu_txhdr->msdu_len);
+ consume_skb(amsdu_txhdr->skb);
+ }
+ }
+#endif /* CONFIG_ECRNX_AMSDUS_TX */
+
+#ifndef CONFIG_ECRNX_ESWIN
+ /* unmap with the least costly DMA_TO_DEVICE since we don't need to inval */
+ dma_unmap_single(ecrnx_hw->dev, sw_txhdr->dma_addr, sw_txhdr->map_len,
+ DMA_TO_DEVICE);
+#endif
+ ecrnx_ipc_sta_buffer(ecrnx_hw, txq->sta, txq->tid, -sw_txhdr->frame_len);
+ ecrnx_tx_statistic(ecrnx_hw, txq, ecrnx_txst, sw_txhdr->frame_len);
+
+ kmem_cache_free(ecrnx_hw->sw_txhdr_cache, sw_txhdr);
+ skb_pull(skb, sw_txhdr->headroom);
+ consume_skb(skb);
+
+ return 0;
+}
+
+/**
+ * ecrnx_txq_credit_update - Update credit for one txq
+ *
+ * @ecrnx_hw: Driver main data
+ * @sta_idx: STA idx
+ * @tid: TID
+ * @update: offset to apply in txq credits
+ *
+ * Called when fw send ME_TX_CREDITS_UPDATE_IND message.
+ * Apply @update to txq credits, and stop/start the txq if needed
+ */
+void ecrnx_txq_credit_update(struct ecrnx_hw *ecrnx_hw, int sta_idx, u8 tid, s8 update)
+{
+#ifndef CONFIG_ECRNX_ESWIN
+ struct ecrnx_sta *sta = &ecrnx_hw->sta_table[sta_idx];
+ struct ecrnx_txq *txq;
+
+ txq = ecrnx_txq_sta_get(sta, tid, ecrnx_hw);
+
+ spin_lock_bh(&ecrnx_hw->tx_lock);
+
+ if (txq->idx != TXQ_INACTIVE) {
+ txq->credits += update;
+ trace_credit_update(txq, update);
+ if (txq->credits <= 0){
+ ECRNX_DBG("%s-%d:ecrnx_txq_stop,reaosn:0x%x \n", __func__, __LINE__, ECRNX_TXQ_STOP_FULL);
+ ecrnx_txq_stop(txq, ECRNX_TXQ_STOP_FULL);
+ }
+ else{
+ ecrnx_txq_start(txq, ECRNX_TXQ_STOP_FULL);
+ ECRNX_DBG("%s-%d:ecrnx_txq_start,reaosn:0x%x \n", __func__, __LINE__, ECRNX_TXQ_STOP_FULL);
+ }
+ }
+
+// Drop all the retry packets of a BA that was deleted
+ if (update < NX_TXQ_INITIAL_CREDITS) {
+ int packet;
+
+ for (packet = 0; packet < txq->nb_retry; packet++) {
+ ecrnx_txq_drop_skb(txq, skb_peek(&txq->sk_list), ecrnx_hw, true);
+ }
+ }
+
+ spin_unlock_bh(&ecrnx_hw->tx_lock);
+#endif
+}
+
+
+#ifdef CONFIG_ECRNX_ESWIN_SDIO
+void ecrnx_tx_retry_sdio(struct ecrnx_hw *ecrnx_hw, struct sk_buff *skb,
+ struct ecrnx_txhdr *txhdr, bool sw_retry)
+{
+ ecrnx_tx_retry(ecrnx_hw, skb, txhdr, sw_retry);
+}
+
+
+#ifdef CONFIG_ECRNX_AMSDUS_TX
+void ecrnx_amsdu_del_subframe_header_sdio(struct ecrnx_amsdu_txhdr *amsdu_txhdr)
+{
+ ecrnx_amsdu_del_subframe_header(amsdu_txhdr);
+}
+#endif
+#endif
+
+#ifdef CONFIG_ECRNX_ESWIN_USB
+void ecrnx_tx_retry_usb(struct ecrnx_hw *ecrnx_hw, struct sk_buff *skb,
+ struct ecrnx_txhdr *txhdr, bool sw_retry)
+{
+ ecrnx_tx_retry(ecrnx_hw, skb, txhdr, sw_retry);
+}
+
+
+#ifdef CONFIG_ECRNX_AMSDUS_TX
+void ecrnx_amsdu_del_subframe_header_sdio(struct ecrnx_amsdu_txhdr *amsdu_txhdr)
+{
+ ecrnx_amsdu_del_subframe_header(amsdu_txhdr);
+}
+#endif
+#endif
+
diff --git a/drivers/net/wireless/eswin/fullmac/ecrnx_tx.h b/drivers/net/wireless/eswin/fullmac/ecrnx_tx.h
new file mode 100644
index 000000000000..03d1177114bb
--- /dev/null
+++ b/drivers/net/wireless/eswin/fullmac/ecrnx_tx.h
@@ -0,0 +1,285 @@
+/**
+ ******************************************************************************
+ *
+ * @file ecrnx_tx.h
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+#ifndef _ECRNX_TX_H_
+#define _ECRNX_TX_H_
+
+#include <linux/ieee80211.h>
+#include <net/cfg80211.h>
+#include <linux/netdevice.h>
+#include "lmac_types.h"
+#include "ipc_shared.h"
+#include "ecrnx_txq.h"
+#include "hal_desc.h"
+
+#define ECRNX_HWQ_BK 0
+#define ECRNX_HWQ_BE 1
+#define ECRNX_HWQ_VI 2
+#define ECRNX_HWQ_VO 3
+#define ECRNX_HWQ_BCMC 4
+#define ECRNX_HWQ_NB NX_TXQ_CNT
+#define ECRNX_HWQ_ALL_ACS (ECRNX_HWQ_BK | ECRNX_HWQ_BE | ECRNX_HWQ_VI | ECRNX_HWQ_VO)
+#define ECRNX_HWQ_ALL_ACS_BIT ( BIT(ECRNX_HWQ_BK) | BIT(ECRNX_HWQ_BE) | \
+ BIT(ECRNX_HWQ_VI) | BIT(ECRNX_HWQ_VO) )
+
+#define ECRNX_TX_LIFETIME_MS 100
+#define ECRNX_TX_MAX_RATES NX_TX_MAX_RATES
+
+
+#define AMSDU_PADDING(x) ((4 - ((x) & 0x3)) & 0x3)
+
+#define TXU_CNTRL_RETRY BIT(0)
+#define TXU_CNTRL_MORE_DATA BIT(2)
+#define TXU_CNTRL_MGMT BIT(3)
+#define TXU_CNTRL_MGMT_NO_CCK BIT(4)
+#define TXU_CNTRL_AMSDU BIT(6)
+#define TXU_CNTRL_MGMT_ROBUST BIT(7)
+#define TXU_CNTRL_USE_4ADDR BIT(8)
+#define TXU_CNTRL_EOSP BIT(9)
+#define TXU_CNTRL_MESH_FWD BIT(10)
+#define TXU_CNTRL_TDLS BIT(11)
+#define TXU_CNTRL_NO_ENCRYPT BIT(15)
+
+extern const int ecrnx_tid2hwq[IEEE80211_NUM_TIDS];
+
+/**
+ * struct ecrnx_amsdu_txhdr - Structure added in skb headroom (instead of
+ * ecrnx_txhdr) for amsdu subframe buffer (except for the first subframe
+ * that has a normal ecrnx_txhdr)
+ *
+ * @list List of other amsdu subframe (ecrnx_sw_txhdr.amsdu.hdrs)
+ * @map_len Length to be downloaded for this subframe
+ * @dma_addr Buffer address form embedded point of view
+ * @skb skb
+ * @pad padding added before this subframe
+ * (only use when amsdu must be dismantled)
+ * @msdu_len Size, in bytes, of the MSDU (without padding nor amsdu header)
+ */
+struct ecrnx_amsdu_txhdr {
+ struct list_head list;
+ size_t map_len;
+#ifdef CONFIG_ECRNX_ESWIN
+ u8 *send_pos; // offset from skb->data for send to slave.
+#else
+ dma_addr_t dma_addr;
+#endif
+ struct sk_buff *skb;
+ u16 pad;
+ u16 msdu_len;
+};
+
+/**
+ * struct ecrnx_amsdu - Structure to manage creation of an A-MSDU, updated
+ * only In the first subframe of an A-MSDU
+ *
+ * @hdrs List of subframe of ecrnx_amsdu_txhdr
+ * @len Current size for this A-MDSU (doesn't take padding into account)
+ * 0 means that no amsdu is in progress
+ * @nb Number of subframe in the amsdu
+ * @pad Padding to add before adding a new subframe
+ */
+struct ecrnx_amsdu {
+ struct list_head hdrs;
+ u16 len;
+ u8 nb;
+ u8 pad;
+};
+
+/**
+ * struct ecrnx_sw_txhdr - Software part of tx header
+ *
+ * @ecrnx_sta sta to which this buffer is addressed
+ * @ecrnx_vif vif that send the buffer
+ * @txq pointer to TXQ used to send the buffer
+ * @hw_queue Index of the HWQ used to push the buffer.
+ * May be different than txq->hwq->id on confirmation.
+ * @frame_len Size of the frame (doesn't not include mac header)
+ * (Only used to update stat, can't we use skb->len instead ?)
+ * @headroom Headroom added in skb to add ecrnx_txhdr
+ * (Only used to remove it before freeing skb, is it needed ?)
+ * @amsdu Description of amsdu whose first subframe is this buffer
+ * (amsdu.nb = 0 means this buffer is not part of amsdu)
+ * @skb skb received from transmission
+ * @map_len Length mapped for DMA (only ecrnx_hw_txhdr and data are mapped)
+ * @dma_addr DMA address after mapping
+ * @desc Buffer description that will be copied in shared mem for FW
+ */
+struct ecrnx_sw_txhdr {
+ struct ecrnx_sta *ecrnx_sta;
+ struct ecrnx_vif *ecrnx_vif;
+ struct ecrnx_txq *txq;
+ u8 hw_queue;
+ u16 frame_len;
+ u16 headroom;
+#ifdef CONFIG_ECRNX_AMSDUS_TX
+ struct ecrnx_amsdu amsdu;
+#endif
+ struct sk_buff *skb;
+
+#ifdef CONFIG_ECRNX_ESWIN
+ u32 offset; // offset from skb->data for send to slave.
+#else
+ size_t map_len;
+ dma_addr_t dma_addr;
+#endif
+ struct txdesc_api desc;
+ unsigned long jiffies;
+};
+
+/**
+ * struct ecrnx_txhdr - Stucture to control transimission of packet
+ * (Added in skb headroom)
+ *
+ * @sw_hdr: Information from driver
+ * @cache_guard:
+ * @hw_hdr: Information for/from hardware
+ */
+struct ecrnx_txhdr {
+ struct ecrnx_sw_txhdr *sw_hdr;
+ char cache_guard[L1_CACHE_BYTES];
+ struct ecrnx_hw_txhdr hw_hdr;
+};
+#define ECRNX_TX_ALIGN_SIZE 4
+#define ECRNX_TX_ALIGN_MASK (ECRNX_TX_ALIGN_SIZE - 1)
+#define ECRNX_SWTXHDR_ALIGN_PADS(x) \
+ ((ECRNX_TX_ALIGN_SIZE - ((x) & ECRNX_TX_ALIGN_MASK)) \
+ & ECRNX_TX_ALIGN_MASK)
+
+#define ECRNX_TX_TXDESC_API_ALIGN ((sizeof(struct txdesc_api) + 3) & (~ECRNX_TX_ALIGN_MASK))
+/**
+ * ECRNX_TX_MAX_HEADROOM - Maximum size needed in skb headroom to prepare a buffer
+ * for transmission
+ * The headroom is used to store the 'struct ecrnx_txhdr' and moreover the part that is used
+ * by the firmware to provide tx status (i.e. struct ecrnx_hw_txhdr) must be aligned on
+ * 32bits because firmware used DMA to update it.
+ */
+#ifdef CONFIG_ECRNX_ESWIN
+#define ECRNX_TX_MAX_HEADROOM (ECRNX_TX_TXDESC_API_ALIGN + sizeof(struct ecrnx_txhdr) + ECRNX_TX_ALIGN_SIZE)
+#else
+#define ECRNX_TX_MAX_HEADROOM (sizeof(struct ecrnx_txhdr) + ECRNX_TX_ALIGN_SIZE)
+#endif
+
+/**
+ * ECRNX_TX_HEADROOM - Headroom to use to store struct ecrnx_txhdr
+ *
+ * Takes into account current aligment of data buffer to ensure that struct ecrnx_txhdr
+ * (and as a consequence its field hw_hdr) will be aligned on 32bits boundary.
+ */
+#ifdef CONFIG_ECRNX_ESWIN
+#define ECRNX_TX_HEADROOM(skb) (ECRNX_TX_TXDESC_API_ALIGN + sizeof(struct ecrnx_txhdr) + ((long)skb->data & ECRNX_TX_ALIGN_MASK))
+#else
+#define ECRNX_TX_HEADROOM(skb) (sizeof(struct ecrnx_txhdr) + ((long)skb->data & ECRNX_TX_ALIGN_MASK))
+#endif
+
+/**
+ * ECRNX_TX_DMA_MAP_LEN - Length, in bytes, to map for DMA transfer
+ * To be called with skb BEFORE reserving headroom to store struct ecrnx_txhdr.
+ */
+#define ECRNX_TX_DMA_MAP_LEN(skb) ( \
+ (sizeof(struct ecrnx_txhdr) - offsetof(struct ecrnx_txhdr, hw_hdr)) + \
+ ((long)skb->data & ECRNX_TX_ALIGN_MASK) + skb->len)
+
+/**
+ * ECRNX_TX_DATA_OFT - Offset, in bytes, between the location of the 'struct ecrnx_hw_txhdr'
+ * and the beginning of frame (i.e. ethernet of 802.11 header). Cannot simply use
+ * sizeof(struct ecrnx_hw_txhdr) because of padding added by compiler to align fields
+ * in structure.
+ */
+#define ECRNX_TX_DATA_OFT(sw_txhdr) ( \
+ (sizeof(struct ecrnx_txhdr) - offsetof(struct ecrnx_txhdr, hw_hdr)) \
+ + (sw_txhdr->headroom & ECRNX_TX_ALIGN_MASK))
+
+/**
+ * SKB buffer format before it is pushed to MACSW
+ *
+ * For DATA frame
+ * |--------------------|
+ * | headroom |
+ * skb->data ----> |--------------------| <------ skb->data
+ * | struct ecrnx_txhdr |
+ * | * ecrnx_sw_txhdr * |
+ * | * [L1 guard] |
+ * +--> | * ecrnx_hw_txhdr | <---- desc.host.status_desc_addr
+ * : | |
+ * memory : |--------------------|
+ * mapped : | padding (optional) |
+ * for DMA : |--------------------|
+ * : | Ethernet Header |
+ * : |--------------------| <---- desc.host.packet_addr[0]
+ * : | Data |
+ * : | |
+ * : | |
+ * : | |
+ * +--> |--------------------|
+ * | tailroom |
+ * |--------------------|
+ *
+ *
+ * For MGMT frame (skb is created by the driver so buffer is always aligned
+ * with no headroom/tailroom)
+ *
+ * skb->data ----> |--------------------| <------ skb->data
+ * | struct ecrnx_txhdr |
+ * | * ecrnx_sw_txhdr * |
+ * | * [L1 guard] |
+ * +--> | * ecrnx_hw_txhdr | <---- desc.host.status_desc_addr
+ * memory : | |
+ * mapped : |--------------------| <---- desc.host.packet_addr[0]
+ * for DMA : | 802.11 HDR |
+ * : |--------------------|
+ * : | Data |
+ * : | |
+ * +--> |--------------------|
+ *
+ */
+
+u16 ecrnx_select_txq(struct ecrnx_vif *ecrnx_vif, struct sk_buff *skb);
+int ecrnx_start_xmit(struct sk_buff *skb, struct net_device *dev);
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0))
+int ecrnx_start_mgmt_xmit(struct ecrnx_vif *vif, struct ecrnx_sta *sta,
+ struct cfg80211_mgmt_tx_params *params, bool offchan,
+ u64 *cookie);
+#else
+int ecrnx_start_mgmt_xmit(struct ecrnx_vif *vif, struct ecrnx_sta *sta,
+ struct ieee80211_channel *channel, bool offchan,
+ unsigned int wait, const u8* buf, size_t len,
+ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0))
+ bool no_cck,
+ #endif
+ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 3, 0))
+ bool dont_wait_for_ack,
+ #endif
+ u64 *cookie);
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) */
+
+int ecrnx_txdatacfm(void *pthis, void *host_id);
+int ecrnx_handle_tx_datacfm(void *priv, void *host_id);
+
+struct ecrnx_hw;
+struct ecrnx_sta;
+void ecrnx_set_traffic_status(struct ecrnx_hw *ecrnx_hw,
+ struct ecrnx_sta *sta,
+ bool available,
+ u8 ps_id);
+void ecrnx_ps_bh_enable(struct ecrnx_hw *ecrnx_hw, struct ecrnx_sta *sta,
+ bool enable);
+void ecrnx_ps_bh_traffic_req(struct ecrnx_hw *ecrnx_hw, struct ecrnx_sta *sta,
+ u16 pkt_req, u8 ps_id);
+
+void ecrnx_switch_vif_sta_txq(struct ecrnx_sta *sta, struct ecrnx_vif *old_vif,
+ struct ecrnx_vif *new_vif);
+
+int ecrnx_dbgfs_print_sta(char *buf, size_t size, struct ecrnx_sta *sta,
+ struct ecrnx_hw *ecrnx_hw);
+void ecrnx_txq_credit_update(struct ecrnx_hw *ecrnx_hw, int sta_idx, u8 tid,
+ s8 update);
+void ecrnx_tx_push(struct ecrnx_hw *ecrnx_hw, struct ecrnx_txhdr *txhdr, int flags);
+
+#endif /* _ECRNX_TX_H_ */
diff --git a/drivers/net/wireless/eswin/fw_head_check.c b/drivers/net/wireless/eswin/fw_head_check.c
new file mode 100644
index 000000000000..739b0a121f5a
--- /dev/null
+++ b/drivers/net/wireless/eswin/fw_head_check.c
@@ -0,0 +1,258 @@
+/**
+******************************************************************************
+*
+* @file fw_head_check.c
+*
+* @brief ecrnx usb firmware validity check functions
+*
+* Copyright (C) ESWIN 2015-2020
+*
+******************************************************************************
+*/
+
+#include <linux/firmware.h>
+#include <linux/version.h>
+#include "core.h"
+#include "fw_head_check.h"
+#include "ecrnx_defs.h"
+
+extern char *fw_name;
+
+#define MSB_MODE 1
+#define LSB_MODE 0
+#define DSE_FIRST (2039)
+#define SECONDS_PER_DAY (86400)
+
+bin_head_data head = {0};
+unsigned int offset = 0;
+
+static const crc32_Table_TypeDef CRC32_Table[]={
+ {0xFFFFFFFF, 0x04C11DB7, 0xFFFFFFFF, LSB_MODE, "CRC32"},
+ {0xFFFFFFFF, 0x04C11DB7, 0x00000000, MSB_MODE, "CRC32_MPEG-2"}
+};
+
+static const u_int16_t days_since_year[] = {
+ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334,
+};
+
+static const u_int16_t days_since_leapyear[] = {
+ 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335,
+};
+
+static const u_int16_t days_since_epoch[] = {
+ /* 2039 - 2030 */
+ 25202, 24837, 24472, 24106, 23741, 23376, 23011, 22645, 22280, 21915,
+ /* 2029 - 2020 */
+ 21550, 21184, 20819, 20454, 20089, 19723, 19358, 18993, 18628, 18262,
+ /* 2019 - 2010 */
+ 17897, 17532, 17167, 16801, 16436, 16071, 15706, 15340, 14975, 14610,
+ /* 2009 - 2000 */
+ 14245, 13879, 13514, 13149, 12784, 12418, 12053, 11688, 11323, 10957,
+ /* 1999 - 1990 */
+ 10592, 10227, 9862, 9496, 9131, 8766, 8401, 8035, 7670, 7305,
+ /* 1989 - 1980 */
+ 6940, 6574, 6209, 5844, 5479, 5113, 4748, 4383, 4018, 3652,
+ /* 1979 - 1970 */
+ 3287, 2922, 2557, 2191, 1826, 1461, 1096, 730, 365, 0,
+};
+
+static inline bool is_leap(unsigned int y)
+{
+ return y % 4 == 0 && (y % 100 != 0 || y % 400 == 0);
+}
+
+void localtime(struct tm *stTm, unsigned int time)
+{
+ unsigned int year, transition_value, i, days = time / SECONDS_PER_DAY,secs = time % SECONDS_PER_DAY;
+
+ stTm->tm_wday = (4 + days - 1) % 7 + 1;
+ stTm->tm_sec = secs % 60;
+ transition_value = secs / 60;
+ stTm->tm_min = transition_value % 60;
+ stTm->tm_hour = transition_value / 60;
+
+ for (i = 0, year = DSE_FIRST; days_since_epoch[i] > days; ++i, --year);
+
+ days -= days_since_epoch[i];
+ stTm->tm_year = DSE_FIRST - i;
+ stTm->tm_yday = days;
+ if (is_leap(year))
+ {
+ for (i = ARRAY_SIZE(days_since_leapyear) - 1;i > 0 && days_since_leapyear[i] > days; --i);
+ stTm->tm_mday = days - days_since_leapyear[i] + 1;
+ }
+ else
+ {
+ for (i = ARRAY_SIZE(days_since_year) - 1;i > 0 && days_since_year[i] > days; --i);
+ stTm->tm_mday = days - days_since_year[i] + 1;
+ }
+
+ stTm->tm_mon = i + 1;
+}
+
+static void _InvertU8(uint8_t *dBuf, uint8_t *srcBuf)
+{
+ uint8_t tmp[4] = {0};
+ uint8_t i=0;
+ for(i=0;i<8;i++)
+ {
+ if(srcBuf[0] & (1<<i))
+ {
+ tmp[0] |= 1<<(7-i);
+ }
+ }
+ dBuf[0] = tmp[0];
+}
+
+static void _InvertU32(uint32_t *dBuf, uint32_t *srcBuf)
+{
+ uint32_t tmp[4] = {0};
+ uint8_t i=0;
+ for(i=0;i<32;i++)
+ {
+ if(srcBuf[0] & (1<<i))
+ {
+ tmp[0] |= 1<<(31-i);
+ }
+ }
+ dBuf[0] = tmp[0];
+}
+
+uint32_t calc_crc32(uint8_t Mode, uint8_t *pMsg, uint32_t Len)
+{
+ if(Mode > sizeof(CRC32_Table)/sizeof(crc32_Table_TypeDef))
+ return 0;
+
+ uint32_t CRCin = CRC32_Table[Mode].initVal;
+ uint32_t tmp = 0;
+ uint32_t i=0;
+ uint8_t j=0;
+
+ for(i=0;i<Len;i++)
+ {
+ tmp = *(pMsg++);
+ if(CRC32_Table[Mode].bits == LSB_MODE)
+ {
+ _InvertU8((uint8_t*)&tmp, (uint8_t*)&tmp);
+ }
+ CRCin ^= (tmp <<24);
+ for(j=0;j<8;j++)
+ {
+ if(CRCin & 0x80000000)
+ {
+ CRCin = (CRCin << 1) ^ CRC32_Table[Mode].POLY;
+ }
+ else
+ {
+ CRCin <<= 1;
+ }
+ }
+ }
+ if(CRC32_Table[Mode].bits == LSB_MODE)
+ {
+ _InvertU32(&CRCin, &CRCin);
+ }
+ return (CRCin ^ CRC32_Table[Mode].sub);
+}
+
+
+unsigned long long parse_data(struct eswin *tr,unsigned char size)
+{
+ unsigned long long value = 0;
+ if(size == 1)
+ {
+ value = * (unsigned char*)(tr->fw->data + offset);
+ }
+ else if(size == 2)
+ {
+ value = * (unsigned short*)(tr->fw->data + offset);
+ }
+ else if(size == 4)
+ {
+ value = * (unsigned int*)(tr->fw->data + offset);
+ }
+ else if(size == 8)
+ {
+ value = * (unsigned long long*)(tr->fw->data + offset);
+ }
+ else
+ {
+ return value;
+ }
+ offset += size;
+ return value;
+}
+
+uint8_t parse_fw_info(struct eswin *tr, bin_head_data *phead)
+{
+ unsigned int len = INFO_SIZE;
+
+ phead->fw_Info = vmalloc(len+1);
+ memcpy(phead->fw_Info,(unsigned char*)(tr->fw->data + offset),len);
+
+ offset += len;
+ return len;
+}
+
+bool fw_crc_check(struct eswin *tr,bin_head_data head)
+{
+ unsigned int now_crc32 = 0;
+ unsigned char crc_shift = ((unsigned char)((void *)(&head.head_crc32) - (void *)(&head))) + sizeof(unsigned int);
+ now_crc32 = calc_crc32(LSB_MODE, (unsigned char *)(tr->fw->data + crc_shift), HEAD_SIZE - crc_shift);
+ if(now_crc32 == head.head_crc32)
+ {
+ return true;
+ }
+ else
+ {
+ ECRNX_PRINT("%s,firmware CRC check error , (%s) ,crc total size(%d)\n", __func__, fw_name,HEAD_SIZE - crc_shift);
+ print_hex_dump(KERN_ERR, DBG_PREFIX_CRC_CHECK, DUMP_PREFIX_ADDRESS, 32, 1, &(head.crc32), 4, false);
+ print_hex_dump(KERN_ERR, DBG_PREFIX_CRC_CHECK, DUMP_PREFIX_ADDRESS, 32, 1, &now_crc32, 4, false);
+ return false;
+ }
+}
+bool fw_magic_check(struct eswin *tr,bin_head_data head)
+{
+ if(head.magic == 0xEF40)
+ {
+ return true;
+ }
+ else if(head.magic == 0xCE56)
+ {
+ return true;
+ }
+ else
+ {
+ ECRNX_PRINT("%s,firmware magic check error , (%s) ,magic(%x)\n", __func__, fw_name,head.magic);
+ return false;
+ }
+}
+
+bool fw_check_head(struct eswin *tr)
+{
+ struct tm timenow = {0};
+
+ head.head_crc32 = parse_data(tr, sizeof(unsigned int));
+ head.crc32 = parse_data(tr, sizeof(unsigned int));
+ head.magic = parse_data(tr, sizeof(unsigned int));
+ head.UTC_time = parse_data(tr, sizeof(unsigned int));
+
+ if(fw_magic_check(tr,head) == false)
+ {
+ return false;
+ }
+ if(fw_crc_check(tr,head) == false)
+ {
+ return false;
+ }
+
+ parse_fw_info(tr, &head);
+
+ localtime(&timenow, head.UTC_time);
+ ECRNX_PRINT("%s,firmware build time: %04d-%02d-%02d %02d:%02d:%02d\n", __func__,(int)timenow.tm_year,timenow.tm_mon,timenow.tm_mday,timenow.tm_hour,timenow.tm_min,timenow.tm_sec);
+ if(head.fw_Info != NULL)
+ {
+ ECRNX_PRINT("%s,firmware information: (%s)\n", __func__, head.fw_Info);
+ }
+ return true;
+}
diff --git a/drivers/net/wireless/eswin/fw_head_check.h b/drivers/net/wireless/eswin/fw_head_check.h
new file mode 100644
index 000000000000..65a60c7d506c
--- /dev/null
+++ b/drivers/net/wireless/eswin/fw_head_check.h
@@ -0,0 +1,43 @@
+/**
+******************************************************************************
+*
+* @file fw_head_check.h
+*
+* @brief ecrnx usb firmware validity check functions
+*
+* Copyright (C) ESWIN 2015-2020
+*
+******************************************************************************
+*/
+
+#ifndef _FW_HEAD_CHECK_H_
+#define _FW_HEAD_CHECK_H_
+
+#include "core.h"
+
+#define HEAD_SIZE (64)
+#define INFO_SIZE (48)
+
+typedef struct _bin_head_data {
+ unsigned int head_crc32;
+ unsigned int crc32;
+ unsigned int magic;
+ unsigned int UTC_time;
+ unsigned char *fw_Info;
+}bin_head_data;
+
+typedef struct{
+ uint32_t initVal;
+ uint32_t POLY;
+ uint32_t sub;
+ uint8_t bits;
+ char *funcName;
+}crc32_Table_TypeDef;
+
+
+extern unsigned int offset;
+
+void localtime(struct tm *stTm, unsigned int time);
+bool fw_check_head(struct eswin *tr);
+
+#endif
diff --git a/drivers/net/wireless/eswin/hal_desc.c b/drivers/net/wireless/eswin/hal_desc.c
new file mode 100644
index 000000000000..6f040fa147ae
--- /dev/null
+++ b/drivers/net/wireless/eswin/hal_desc.c
@@ -0,0 +1,189 @@
+/**
+ *****************************************************************************
+ *
+ * @file hal_desc.c
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ *****************************************************************************
+ */
+
+#include <linux/string.h>
+#include "hal_desc.h"
+
+const struct ecrnx_legrate legrates_lut[] = {
+ [0] = { .idx = 0, .rate = 10 },
+ [1] = { .idx = 1, .rate = 20 },
+ [2] = { .idx = 2, .rate = 55 },
+ [3] = { .idx = 3, .rate = 110 },
+ [4] = { .idx = -1, .rate = 0 },
+ [5] = { .idx = -1, .rate = 0 },
+ [6] = { .idx = -1, .rate = 0 },
+ [7] = { .idx = -1, .rate = 0 },
+ [8] = { .idx = 10, .rate = 480 },
+ [9] = { .idx = 8, .rate = 240 },
+ [10] = { .idx = 6, .rate = 120 },
+ [11] = { .idx = 4, .rate = 60 },
+ [12] = { .idx = 11, .rate = 540 },
+ [13] = { .idx = 9, .rate = 360 },
+ [14] = { .idx = 7, .rate = 180 },
+ [15] = { .idx = 5, .rate = 90 },
+};
+
+/**
+ * ecrnx_machw_type - Return type (NX or HE) MAC HW is used
+ *
+ */
+int ecrnx_machw_type(uint32_t machw_version_2)
+{
+ uint32_t machw_um_ver_maj = (machw_version_2 >> 4) & 0x7;
+
+ if (machw_um_ver_maj >= 4)
+ return ECRNX_MACHW_HE;
+ else
+ return ECRNX_MACHW_NX;
+}
+
+/**
+ * ecrnx_rx_vector_convert - Convert in place a RX vector from NX hardware into
+ * a RX vector formatted by HE hardware.
+ *
+ * @machw_type: Type of MACHW in use.
+ * @rx_vect1: Rx vector 1 descriptor of the received frame.
+ * @rx_vect2: Rx vector 2 descriptor of the received frame.
+ */
+void ecrnx_rx_vector_convert(int machw_type,
+ struct rx_vector_1 *rx_vect1,
+ struct rx_vector_2 *rx_vect2)
+{
+ struct rx_vector_1_nx rx_vect1_nx;
+ struct rx_vector_2_nx rx_vect2_nx;
+
+ // Check if we need to do the conversion. Only if old modem is used
+ if (machw_type == ECRNX_MACHW_HE) {
+ rx_vect1->rssi1 = rx_vect1->rssi_leg;
+ return;
+ }
+
+ // Copy the received vector locally
+ memcpy(&rx_vect1_nx, rx_vect1, sizeof(struct rx_vector_1_nx));
+
+ // Reset it
+ memset(rx_vect1, 0, sizeof(struct rx_vector_1));
+
+ // Perform the conversion
+ rx_vect1->format_mod = rx_vect1_nx.format_mod;
+ rx_vect1->ch_bw = rx_vect1_nx.ch_bw;
+ rx_vect1->pre_type = rx_vect1_nx.pre_type;
+ rx_vect1->antenna_set = rx_vect1_nx.antenna_set;
+ rx_vect1->leg_length = rx_vect1_nx.leg_length;
+ rx_vect1->leg_rate = rx_vect1_nx.leg_rate;
+ rx_vect1->rssi1 = rx_vect1_nx.rssi1;
+
+ switch (rx_vect1->format_mod) {
+ case FORMATMOD_NON_HT:
+ case FORMATMOD_NON_HT_DUP_OFDM:
+ rx_vect1->leg.dyn_bw_in_non_ht = rx_vect1_nx.dyn_bw;
+ rx_vect1->leg.chn_bw_in_non_ht = rx_vect1_nx.ch_bw;
+ rx_vect1->leg.lsig_valid = rx_vect1_nx.lsig_valid;
+ break;
+ case FORMATMOD_HT_MF:
+ case FORMATMOD_HT_GF:
+ rx_vect1->ht.sounding = rx_vect1_nx.sounding;
+ rx_vect1->ht.smoothing = rx_vect1_nx.smoothing;
+ rx_vect1->ht.short_gi = rx_vect1_nx.short_gi;
+ rx_vect1->ht.aggregation = rx_vect1_nx.aggregation;
+ rx_vect1->ht.stbc = rx_vect1_nx.stbc;
+ rx_vect1->ht.num_extn_ss = rx_vect1_nx.num_extn_ss;
+ rx_vect1->ht.lsig_valid = rx_vect1_nx.lsig_valid;
+ rx_vect1->ht.mcs = rx_vect1_nx.mcs;
+ rx_vect1->ht.fec = rx_vect1_nx.fec_coding;
+ rx_vect1->ht.length = rx_vect1_nx.ht_length;
+ break;
+ case FORMATMOD_VHT:
+ rx_vect1->vht.sounding = rx_vect1_nx.sounding;
+ rx_vect1->vht.beamformed = !rx_vect1_nx.smoothing;
+ rx_vect1->vht.short_gi = rx_vect1_nx.short_gi;
+ rx_vect1->vht.stbc = rx_vect1_nx.stbc;
+ rx_vect1->vht.doze_not_allowed = rx_vect1_nx.doze_not_allowed;
+ rx_vect1->vht.first_user = rx_vect1_nx.first_user;
+ rx_vect1->vht.partial_aid = rx_vect1_nx.partial_aid;
+ rx_vect1->vht.group_id = rx_vect1_nx.group_id;
+ rx_vect1->vht.mcs = rx_vect1_nx.mcs;
+ rx_vect1->vht.nss = rx_vect1_nx.stbc ? rx_vect1_nx.n_sts/2 : rx_vect1_nx.n_sts;
+ rx_vect1->vht.fec = rx_vect1_nx.fec_coding;
+ rx_vect1->vht.length = (rx_vect1_nx._ht_length << 16) | rx_vect1_nx.ht_length;
+ break;
+ }
+
+ if (!rx_vect2)
+ return;
+
+ // Copy the received vector 2 locally
+ memcpy(&rx_vect2_nx, rx_vect2, sizeof(struct rx_vector_2_nx));
+
+ // Reset it
+ memset(rx_vect2, 0, sizeof(struct rx_vector_2));
+
+ rx_vect2->rcpi1 = rx_vect2_nx.rcpi;
+ rx_vect2->rcpi2 = rx_vect2_nx.rcpi;
+ rx_vect2->rcpi3 = rx_vect2_nx.rcpi;
+ rx_vect2->rcpi4 = rx_vect2_nx.rcpi;
+
+ rx_vect2->evm1 = rx_vect2_nx.evm1;
+ rx_vect2->evm2 = rx_vect2_nx.evm2;
+ rx_vect2->evm3 = rx_vect2_nx.evm3;
+ rx_vect2->evm4 = rx_vect2_nx.evm4;
+}
+
+
+/**
+ * ecrnx_rx_status_convert - Convert in place a legacy MPDU status from NX hardware
+ * into a MPDU status formatted by HE hardware.
+ *
+ * @machw_type: Type of MACHW in use.
+ * @status: Rx MPDU status of the received frame.
+ */
+void ecrnx_rx_status_convert(int machw_type, struct mpdu_status *status)
+{
+ struct mpdu_status_nx *status_nx;
+
+ if (machw_type == ECRNX_MACHW_HE)
+ return;
+
+ status_nx = (struct mpdu_status_nx *)status;
+ status->undef_err = status_nx->undef_err;
+
+ switch (status_nx->decr_status) {
+ case ECRNX_RX_HD_NX_DECR_UNENC:
+ status->decr_type = ECRNX_RX_HD_DECR_UNENC;
+ status->decr_err = 0;
+ break;
+ case ECRNX_RX_HD_NX_DECR_ICVFAIL:
+ status->decr_type = ECRNX_RX_HD_DECR_WEP;
+ status->decr_err = 1;
+ break;
+ case ECRNX_RX_HD_NX_DECR_CCMPFAIL:
+ case ECRNX_RX_HD_NX_DECR_AMSDUDISCARD:
+ status->decr_type = ECRNX_RX_HD_DECR_CCMP128;
+ status->decr_err = 1;
+ break;
+ case ECRNX_RX_HD_NX_DECR_NULLKEY:
+ status->decr_type = ECRNX_RX_HD_DECR_NULLKEY;
+ status->decr_err = 1;
+ break;
+ case ECRNX_RX_HD_NX_DECR_WEPSUCCESS:
+ status->decr_type = ECRNX_RX_HD_DECR_WEP;
+ status->decr_err = 0;
+ break;
+ case ECRNX_RX_HD_NX_DECR_TKIPSUCCESS:
+ status->decr_type = ECRNX_RX_HD_DECR_TKIP;
+ status->decr_err = 0;
+ break;
+ case ECRNX_RX_HD_NX_DECR_CCMPSUCCESS:
+ status->decr_type = ECRNX_RX_HD_DECR_CCMP128;
+ status->decr_err = 0;
+ break;
+ }
+}
+
diff --git a/drivers/net/wireless/eswin/hal_desc.h b/drivers/net/wireless/eswin/hal_desc.h
new file mode 100644
index 000000000000..647961a5311a
--- /dev/null
+++ b/drivers/net/wireless/eswin/hal_desc.h
@@ -0,0 +1,670 @@
+/**
+ ******************************************************************************
+ *
+ * @file hal_desc.h
+ *
+ * @brief File containing the definition of HW descriptors.
+ *
+ * Contains the definition and structures used by HW
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+
+#ifndef _HAL_DESC_H_
+#define _HAL_DESC_H_
+
+#include "lmac_types.h"
+
+#define ECRNX_MACHW_NX 1
+#define ECRNX_MACHW_HE 2
+/* Rate and policy table */
+
+#define N_CCK 8
+#define N_OFDM 8
+#define N_HT (8 * 2 * 2 * 4)
+#define N_VHT (10 * 4 * 2 * 8)
+#define N_HE_SU (12 * 4 * 3 * 8)
+#define N_HE_MU (12 * 6 * 3 * 8)
+
+/* conversion table from NL80211 to MACHW enum */
+extern const int chnl2bw[];
+
+/* conversion table from MACHW to NL80211 enum */
+extern const int bw2chnl[];
+
+struct ecrnx_legrate {
+ s16 idx;
+ u16 rate; // in 100Kbps
+};
+extern const struct ecrnx_legrate legrates_lut[];
+/* Values for formatModTx */
+#define FORMATMOD_NON_HT 0
+#define FORMATMOD_NON_HT_DUP_OFDM 1
+#define FORMATMOD_HT_MF 2
+#define FORMATMOD_HT_GF 3
+#define FORMATMOD_VHT 4
+#define FORMATMOD_HE_SU 5
+#define FORMATMOD_HE_MU 6
+#define FORMATMOD_HE_ER 7
+#define FORMATMOD_HE_TB 8
+
+/* Values for navProtFrmEx */
+#define NAV_PROT_NO_PROT_BIT 0
+#define NAV_PROT_SELF_CTS_BIT 1
+#define NAV_PROT_RTS_CTS_BIT 2
+#define NAV_PROT_RTS_CTS_WITH_QAP_BIT 3
+#define NAV_PROT_STBC_BIT 4
+
+/* THD MACCTRLINFO2 fields, used in struct umacdesc umac.flags */
+/// WhichDescriptor definition - contains aMPDU bit and position value
+/// Offset of WhichDescriptor field in the MAC CONTROL INFO 2 word
+#define WHICHDESC_OFT 19
+/// Mask of the WhichDescriptor field
+#define WHICHDESC_MSK (0x07 << WHICHDESC_OFT)
+/// Only 1 THD possible, describing an unfragmented MSDU
+#define WHICHDESC_UNFRAGMENTED_MSDU (0x00 << WHICHDESC_OFT)
+/// THD describing the first MPDU of a fragmented MSDU
+#define WHICHDESC_FRAGMENTED_MSDU_FIRST (0x01 << WHICHDESC_OFT)
+/// THD describing intermediate MPDUs of a fragmented MSDU
+#define WHICHDESC_FRAGMENTED_MSDU_INT (0x02 << WHICHDESC_OFT)
+/// THD describing the last MPDU of a fragmented MSDU
+#define WHICHDESC_FRAGMENTED_MSDU_LAST (0x03 << WHICHDESC_OFT)
+/// THD for extra descriptor starting an AMPDU
+#define WHICHDESC_AMPDU_EXTRA (0x04 << WHICHDESC_OFT)
+/// THD describing the first MPDU of an A-MPDU
+#define WHICHDESC_AMPDU_FIRST (0x05 << WHICHDESC_OFT)
+/// THD describing intermediate MPDUs of an A-MPDU
+#define WHICHDESC_AMPDU_INT (0x06 << WHICHDESC_OFT)
+/// THD describing the last MPDU of an A-MPDU
+#define WHICHDESC_AMPDU_LAST (0x07 << WHICHDESC_OFT)
+
+/// aMPDU bit offset
+#define AMPDU_OFT 21
+/// aMPDU bit
+#define AMPDU_BIT CO_BIT(AMPDU_OFT)
+
+union ecrnx_mcs_index {
+ struct {
+ u32 mcs : 3;
+ u32 nss : 2;
+ } ht;
+ struct {
+ u32 mcs : 4;
+ u32 nss : 3;
+ } vht;
+ struct {
+ u32 mcs : 4;
+ u32 nss : 3;
+ } he;
+ u32 legacy : 7;
+};
+
+union ecrnx_rate_ctrl_info {
+ struct {
+ u32 mcsIndexTx : 7;
+ u32 bwTx : 2;
+ u32 giAndPreTypeTx : 2;
+ u32 formatModTx : 3;
+ #ifdef CONFIG_ECRNX_FULLMAC
+ u32 dcmTx : 1;
+ #else
+ u32 navProtFrmEx : 3;
+ u32 mcsIndexProtTx : 7;
+ u32 bwProtTx : 2;
+ u32 formatModProtTx : 3;
+ u32 nRetry : 3;
+ #endif
+ };
+ u32 value;
+};
+
+struct ecrnx_power_ctrl_info {
+ u32 txPwrLevelPT : 8;
+ u32 txPwrLevelProtPT : 8;
+ u32 reserved :16;
+};
+
+union ecrnx_pol_phy_ctrl_info_1 {
+ struct {
+ u32 rsvd1 : 3;
+ u32 bfFrmEx : 1;
+ u32 numExtnSS : 2;
+ u32 fecCoding : 1;
+ u32 stbc : 2;
+ u32 rsvd2 : 5;
+ u32 nTx : 3;
+ u32 nTxProt : 3;
+ };
+ u32 value;
+};
+
+union ecrnx_pol_phy_ctrl_info_2 {
+ struct {
+ u32 antennaSet : 8;
+ u32 smmIndex : 8;
+ u32 beamFormed : 1;
+ };
+ u32 value;
+};
+
+union ecrnx_pol_mac_ctrl_info_1 {
+ struct {
+ u32 keySRamIndex : 10;
+ u32 keySRamIndexRA : 10;
+ };
+ u32 value;
+};
+
+union ecrnx_pol_mac_ctrl_info_2 {
+ struct {
+ u32 longRetryLimit : 8;
+ u32 shortRetryLimit : 8;
+ u32 rtsThreshold : 12;
+ };
+ u32 value;
+};
+
+#define POLICY_TABLE_PATTERN 0xBADCAB1E
+
+struct tx_policy_tbl {
+ /* Unique Pattern at the start of Policy Table */
+ u32 upatterntx;
+ /* PHY Control 1 Information used by MAC HW */
+ union ecrnx_pol_phy_ctrl_info_1 phyctrlinfo_1;
+ /* PHY Control 2 Information used by MAC HW */
+ union ecrnx_pol_phy_ctrl_info_2 phyctrlinfo_2;
+ /* MAC Control 1 Information used by MAC HW */
+ union ecrnx_pol_mac_ctrl_info_1 macctrlinfo_1;
+ /* MAC Control 2 Information used by MAC HW */
+ union ecrnx_pol_mac_ctrl_info_2 macctrlinfo_2;
+
+ union ecrnx_rate_ctrl_info ratectrlinfos[NX_TX_MAX_RATES];
+ struct ecrnx_power_ctrl_info powerctrlinfos[NX_TX_MAX_RATES];
+};
+
+#ifdef CONFIG_ECRNX_SOFTMAC
+
+union ecrnx_hw_txstatus {
+ struct {
+ u32 num_rts_retries : 8;
+ u32 num_mpdu_retries : 8;
+ u32 retry_limit_reached : 1;
+ u32 lifetime_expired : 1;
+ u32 baFrameReceived : 1;
+ u32 reserved2 : 4;
+ u32 frm_successful_tx : 1;
+ u32 transmission_bw : 2;
+ u32 which_descriptor_sw : 4;
+ u32 descriptor_done_swtx : 1;
+ u32 descriptor_done_hwtx : 1;
+ };
+ u32 value;
+};
+
+// WhichDescriptor for AMPDUs (_under BA Policy_)
+#define __WD_AMPDU_BAPOL 0xC
+#define __WD_AMPDU_EXTRA 0xC
+#define __WD_AMPDU_FIRST 0xD
+#define __WD_AMPDU_INT 0xE
+#define __WD_AMPDU_LAST 0xF
+
+#define ECRNX_WD_IS_AMPDU(whichdesc) \
+ (((whichdesc) & __WD_AMPDU_BAPOL) == __WD_AMPDU_BAPOL)
+#define ECRNX_WD_IS_FIRST(whichdesc) \
+ ((whichdesc) == __WD_AMPDU_FIRST)
+#define ECRNX_WD_IS_LAST(whichdesc) \
+ ((whichdesc) == __WD_AMPDU_LAST)
+
+union ecrnx_thd_phy_ctrl_info {
+ struct {
+ u32 soundingTx : 1;
+ u32 smoothingTx : 1;
+ u32 smoothingProtTx : 1;
+ u32 useBWSignalingTx : 1;
+ u32 dynBWTx : 1;
+ u32 dozeNotAllowedTx : 1;
+ u32 continuousTx : 1;
+ u32 rsvd : 1;
+ u32 PTITx : 4;
+ u32 userPositionTx : 2;
+ u32 useRIFSTx : 1;
+ u32 muMIMOTx : 1;
+ u32 groupIDTx : 6;
+ u32 partialAIDTx : 9;
+ };
+ u32 value;
+};
+
+#define EXPECTED_ACK_NO_ACK 0
+#define EXPECTED_ACK_NORMAL_ACK 1
+#define EXPECTED_ACK_BLOCK_ACK 2
+#define EXPECTED_ACK_COMPRESSED_BLOCK_ACK 3
+
+union ecrnx_thd_mac_ctrl_info_1 {
+ struct {
+ u32 rsvd1 : 9;
+ u32 expectedAck : 2;
+ u32 lstp : 1;
+ u32 lstpProt : 1;
+ u32 lowRateRetry : 1;
+ u32 writeACK : 1;
+ u32 rsvd2 : 1;
+ u32 protFrmDur : 16;
+ };
+ u32 value;
+};
+
+/**
+ * struct ecrnx_hw_txhdr - Hardware part of tx header
+ *
+ * @policy: Policy table to use for transmission
+ * @mac_ctrl_info: MAC configuration to use for transmission
+ * @phy_ctrl_info: PHY configuration to use for transmission
+ *
+ * @status: Status updated by fw/hardware after transmission
+ */
+struct ecrnx_hw_txhdr {
+ struct tx_policy_tbl policy;
+ union ecrnx_thd_mac_ctrl_info_1 mac_ctrl_info;
+ union ecrnx_thd_phy_ctrl_info phy_ctrl_info;
+ union ecrnx_hw_txstatus status;
+};
+
+#else /* !CONFIG_ECRNX_SOFTMAC */
+
+/**
+ * struct ecrnx_hw_txstatus - Bitfield of confirmation status
+ *
+ * @tx_done: packet has been processed by the firmware.
+ * @retry_required: packet has been transmitted but not acknoledged.
+ * Driver must repush it.
+ * @sw_retry_required: packet has not been transmitted (FW wasn't able to push
+ * it when it received it: not active channel ...). Driver must repush it.
+ * @acknowledged: packet has been acknowledged by peer
+ */
+union ecrnx_hw_txstatus {
+ struct {
+ u32 tx_done : 1;
+ u32 retry_required : 1;
+ u32 sw_retry_required : 1;
+ u32 acknowledged : 1;
+ u32 reserved :28;
+ };
+ u32 value;
+};
+
+/**
+ * struct tx_cfm_tag - Structure indicating the status and other
+ * information about the transmission
+ *
+ * @pn: PN that was used for the transmission
+ * @sn: Sequence number of the packet
+ * @timestamp: Timestamp of first transmission of this MPDU
+ * @credits: Number of credits to be reallocated for the txq that push this
+ * buffer (can be 0 or 1)
+ * @ampdu_size: Size of the ampdu in which the frame has been transmitted if
+ * this was the last frame of the a-mpdu, and 0 if the frame is not the last
+ * frame on a a-mdpu.
+ * 1 means that the frame has been transmitted as a singleton.
+ * @amsdu_size: Size, in bytes, allowed to create a-msdu.
+ * @status: transmission status
+ */
+struct tx_cfm_tag
+{
+ u16_l pn[4];
+ u16_l sn;
+ u16_l timestamp;
+ s8_l credits;
+ u8_l ampdu_size;
+#ifdef CONFIG_ECRNX_SPLIT_TX_BUF
+ u16_l amsdu_size;
+#endif
+
+#ifdef CONFIG_ECRNX_ESWIN
+ uint32_t hostid[2];
+#endif
+
+ union ecrnx_hw_txstatus status;
+};
+
+/**
+ * struct ecrnx_hw_txhdr - Hardware part of tx header
+ *
+ * @cfm: Information updated by fw/hardware after sending a frame
+ */
+struct ecrnx_hw_txhdr {
+ struct tx_cfm_tag cfm;
+};
+
+#endif /* CONFIG_ECRNX_SOFTMAC */
+
+#define ECRNX_RX_HD_NX_DECR_UNENC 0 // Frame unencrypted
+#define ECRNX_RX_HD_NX_DECR_ICVFAIL 1 // WEP/TKIP ICV failure
+#define ECRNX_RX_HD_NX_DECR_CCMPFAIL 2 // CCMP failure
+#define ECRNX_RX_HD_NX_DECR_AMSDUDISCARD 3 // A-MSDU discarded at HW
+#define ECRNX_RX_HD_NX_DECR_NULLKEY 4 // NULL key found
+#define ECRNX_RX_HD_NX_DECR_WEPSUCCESS 5 // Security type WEP
+#define ECRNX_RX_HD_NX_DECR_TKIPSUCCESS 6 // Security type TKIP
+#define ECRNX_RX_HD_NX_DECR_CCMPSUCCESS 7 // Security type CCMP (or WPI)
+#define ECRNX_RX_HD_DECR_UNENC 0 // Frame unencrypted
+#define ECRNX_RX_HD_DECR_WEP 1 // Security type WEP
+#define ECRNX_RX_HD_DECR_TKIP 2 // Security type TKIP
+#define ECRNX_RX_HD_DECR_CCMP128 3 // Security type CCMP (128 bits)
+#define ECRNX_RX_HD_DECR_CCMP256 4 // Security type CCMP (256 bits)
+#define ECRNX_RX_HD_DECR_GCMP128 5 // Security type GCMP (128 bits)
+#define ECRNX_RX_HD_DECR_GCMP256 6 // Security type GCMP (256 bits)
+#define ECRNX_RX_HD_DECR_WAPI 7 // Security type WAPI
+#define ECRNX_RX_HD_DECR_NULLKEY 15 // NULL key found
+struct rx_vector_1_nx {
+ u32 leg_length :12;
+ u32 leg_rate : 4;
+ u32 ht_length :16;
+ u32 _ht_length : 4; // FIXME
+ u32 short_gi : 1;
+ u32 stbc : 2;
+ u32 smoothing : 1;
+ u32 mcs : 7;
+ u32 pre_type : 1;
+ u32 format_mod : 3;
+ u32 ch_bw : 2;
+ u32 n_sts : 3;
+ u32 lsig_valid : 1;
+ u32 sounding : 1;
+ u32 num_extn_ss : 2;
+ u32 aggregation : 1;
+ u32 fec_coding : 1;
+ u32 dyn_bw : 1;
+ u32 doze_not_allowed : 1;
+ u32 antenna_set : 8;
+ u32 partial_aid : 9;
+ u32 group_id : 6;
+ u32 first_user : 1;
+ s32 rssi1 : 8;
+ s32 rssi2 : 8;
+ s32 rssi3 : 8;
+ s32 rssi4 : 8;
+ u32 reserved_1d : 8;
+};
+struct rx_vector_2_nx {
+ u32 rcpi : 8;
+ u32 evm1 : 8;
+ u32 evm2 : 8;
+ u32 evm3 : 8;
+ u32 evm4 : 8;
+ u32 reserved2b_1 : 8;
+ u32 reserved2b_2 : 8;
+ u32 reserved2b_3 : 8;
+};
+struct mpdu_status_nx {
+ u32 rx_vect2_valid : 1;
+ u32 resp_frame : 1;
+ u32 decr_status : 3;
+ u32 rx_fifo_oflow : 1;
+ u32 undef_err : 1;
+ u32 phy_err : 1;
+ u32 fcs_err : 1;
+ u32 addr_mismatch : 1;
+ u32 ga_frame : 1;
+ u32 current_ac : 2;
+ u32 frm_successful_rx : 1;
+ u32 desc_done_rx : 1;
+ u32 key_sram_index : 10;
+ u32 key_sram_valid : 1;
+ u32 type : 2;
+ u32 subtype : 4;
+};
+struct rx_leg_vect
+{
+ u8 dyn_bw_in_non_ht : 1;
+ u8 chn_bw_in_non_ht : 2;
+ u8 rsvd_nht : 4;
+ u8 lsig_valid : 1;
+} __packed;
+struct rx_ht_vect
+{
+ u16 sounding : 1;
+ u16 smoothing : 1;
+ u16 short_gi : 1;
+ u16 aggregation : 1;
+ u16 stbc : 1;
+ u16 num_extn_ss : 2;
+ u16 lsig_valid : 1;
+ u16 mcs : 7;
+ u16 fec : 1;
+ u16 length :16;
+} __packed;
+struct rx_vht_vect
+{
+ u8 sounding : 1;
+ u8 beamformed : 1;
+ u8 short_gi : 1;
+ u8 rsvd_vht1 : 1;
+ u8 stbc : 1;
+ u8 doze_not_allowed : 1;
+ u8 first_user : 1;
+ u8 rsvd_vht2 : 1;
+ u16 partial_aid : 9;
+ u16 group_id : 6;
+ u16 rsvd_vht3 : 1;
+ u32 mcs : 4;
+ u32 nss : 3;
+ u32 fec : 1;
+ u32 length :20;
+ u32 rsvd_vht4 : 4;
+} __packed;
+struct rx_he_vect
+{
+ u8 sounding : 1;
+ u8 beamformed : 1;
+ u8 gi_type : 2;
+ u8 stbc : 1;
+ u8 rsvd_he1 : 3;
+ u8 uplink_flag : 1;
+ u8 beam_change : 1;
+ u8 dcm : 1;
+ u8 he_ltf_type : 2;
+ u8 doppler : 1;
+ u8 rsvd_he2 : 2;
+ u8 bss_color : 6;
+ u8 rsvd_he3 : 2;
+ u8 txop_duration : 7;
+ u8 rsvd_he4 : 1;
+ u8 pe_duration : 4;
+ u8 spatial_reuse : 4;
+ u8 sig_b_comp_mode : 1;
+ u8 dcm_sig_b : 1;
+ u8 mcs_sig_b : 3;
+ u8 ru_size : 3;
+ u32 mcs : 4;
+ u32 nss : 3;
+ u32 fec : 1;
+ u32 length :20;
+ u32 rsvd_he6 : 4;
+} __packed;
+struct rx_vector_1 {
+ u8 format_mod : 4;
+ u8 ch_bw : 3;
+ u8 pre_type : 1;
+ u8 antenna_set : 8;
+ s32 rssi_leg : 8;
+ u32 leg_length :12;
+ u32 leg_rate : 4;
+ s32 rssi1 : 8;
+ union
+ {
+ struct rx_leg_vect leg;
+ struct rx_ht_vect ht;
+ struct rx_vht_vect vht;
+ struct rx_he_vect he;
+ };
+} __packed;
+struct rx_vector_2 {
+ u32 rcpi1 : 8;
+ u32 rcpi2 : 8;
+ u32 rcpi3 : 8;
+ u32 rcpi4 : 8;
+ u32 evm1 : 8;
+ u32 evm2 : 8;
+ u32 evm3 : 8;
+ u32 evm4 : 8;
+};
+struct mpdu_status {
+ u32 rx_vect2_valid : 1;
+ u32 resp_frame : 1;
+ u32 decr_type : 4;
+ u32 decr_err : 1;
+ u32 undef_err : 1;
+ u32 fcs_err : 1;
+ u32 addr_mismatch : 1;
+ u32 ga_frame : 1;
+ u32 current_ac : 2;
+ u32 frm_successful_rx : 1;
+ u32 desc_done_rx : 1;
+ u32 key_sram_index : 10;
+ u32 key_sram_v : 1;
+ u32 type : 2;
+ u32 subtype : 4;
+};
+struct hw_vect {
+ u32 len :16;
+ u32 reserved : 8;
+ u32 mpdu_cnt : 6;
+ u32 ampdu_cnt : 2;
+ __le32 tsf_lo;
+ /** TSF High */
+ __le32 tsf_hi;
+
+ /** Receive Vector 1 */
+ struct rx_vector_1 rx_vect1;
+ /** Receive Vector 2 */
+ struct rx_vector_2 rx_vect2;
+
+ /** MPDU status information */
+ struct mpdu_status status;
+};
+
+struct phy_channel_info_desc {
+ /** PHY channel information 1 */
+ u32 phy_band : 8;
+ u32 phy_channel_type : 8;
+ u32 phy_prim20_freq : 16;
+
+ /** PHY channel information 2 */
+ u32 phy_center1_freq : 16;
+ u32 phy_center2_freq : 16;
+};
+
+int ecrnx_machw_type(uint32_t machw_version_2);
+void ecrnx_rx_vector_convert(int machw_type, struct rx_vector_1 *rx_vect1,
+ struct rx_vector_2 *rx_vect2);
+void ecrnx_rx_status_convert(int machw_type, struct mpdu_status *status);
+
+/******************************************************************************
+ * Modem
+ ******************************************************************************/
+#define MDM_PHY_CONFIG_TRIDENT 0
+#define MDM_PHY_CONFIG_CATAXIA 1
+#define MDM_PHY_CONFIG_KARST 2
+
+// MODEM features (from reg_mdm_stat.h)
+/// MUMIMOTX field bit
+#define MDM_MUMIMOTX_BIT ((u32)0x80000000)
+/// MUMIMOTX field position
+#define MDM_MUMIMOTX_POS 31
+/// MUMIMORX field bit
+#define MDM_MUMIMORX_BIT ((u32)0x40000000)
+/// MUMIMORX field position
+#define MDM_MUMIMORX_POS 30
+/// BFMER field bit
+#define MDM_BFMER_BIT ((u32)0x20000000)
+/// BFMER field position
+#define MDM_BFMER_POS 29
+/// BFMEE field bit
+#define MDM_BFMEE_BIT ((u32)0x10000000)
+/// BFMEE field position
+#define MDM_BFMEE_POS 28
+/// LDPCDEC field bit
+#define MDM_LDPCDEC_BIT ((u32)0x08000000)
+/// LDPCDEC field position
+#define MDM_LDPCDEC_POS 27
+/// LDPCENC field bit
+#define MDM_LDPCENC_BIT ((u32)0x04000000)
+/// LDPCENC field position
+#define MDM_LDPCENC_POS 26
+/// CHBW field mask
+#define MDM_CHBW_MASK ((u32)0x03000000)
+/// CHBW field LSB position
+#define MDM_CHBW_LSB 24
+/// CHBW field width
+#define MDM_CHBW_WIDTH ((u32)0x00000002)
+/// DSSSCCK field bit
+#define MDM_DSSSCCK_BIT ((u32)0x00800000)
+/// DSSSCCK field position
+#define MDM_DSSSCCK_POS 23
+/// VHT field bit
+#define MDM_VHT_BIT ((u32)0x00400000)
+/// VHT field position
+#define MDM_VHT_POS 22
+/// HE field bit
+#define MDM_HE_BIT ((u32)0x00200000)
+/// HE field position
+#define MDM_HE_POS 21
+/// ESS field bit
+#define MDM_ESS_BIT ((u32)0x00100000)
+/// ESS field position
+#define MDM_ESS_POS 20
+/// RFMODE field mask
+#define MDM_RFMODE_MASK ((u32)0x000F0000)
+/// RFMODE field LSB position
+#define MDM_RFMODE_LSB 16
+/// RFMODE field width
+#define MDM_RFMODE_WIDTH ((u32)0x00000004)
+/// NSTS field mask
+#define MDM_NSTS_MASK ((u32)0x0000F000)
+/// NSTS field LSB position
+#define MDM_NSTS_LSB 12
+/// NSTS field width
+#define MDM_NSTS_WIDTH ((u32)0x00000004)
+/// NSS field mask
+#define MDM_NSS_MASK ((u32)0x00000F00)
+/// NSS field LSB position
+#define MDM_NSS_LSB 8
+/// NSS field width
+#define MDM_NSS_WIDTH ((u32)0x00000004)
+/// NTX field mask
+#define MDM_NTX_MASK ((u32)0x000000F0)
+/// NTX field LSB position
+#define MDM_NTX_LSB 4
+/// NTX field width
+#define MDM_NTX_WIDTH ((u32)0x00000004)
+/// NRX field mask
+#define MDM_NRX_MASK ((u32)0x0000000F)
+/// NRX field LSB position
+#define MDM_NRX_LSB 0
+/// NRX field width
+#define MDM_NRX_WIDTH ((u32)0x00000004)
+
+#define __MDM_PHYCFG_FROM_VERS(v) (((v) & MDM_RFMODE_MASK) >> MDM_RFMODE_LSB)
+
+#define RIU_FCU_PRESENT_MASK ((u32)0xFF000000)
+#define RIU_FCU_PRESENT_LSB 24
+
+#define __RIU_FCU_PRESENT(v) (((v) & RIU_FCU_PRESENT_MASK) >> RIU_FCU_PRESENT_LSB == 5)
+
+/// AGC load version field mask
+#define RIU_AGC_LOAD_MASK ((u32)0x00C00000)
+/// AGC load version field LSB position
+#define RIU_AGC_LOAD_LSB 22
+
+#define __RIU_AGCLOAD_FROM_VERS(v) (((v) & RIU_AGC_LOAD_MASK) >> RIU_AGC_LOAD_LSB)
+
+#define __FPGA_TYPE(v) (((v) & 0xFFFF0000) >> 16)
+
+#define __MDM_MAJOR_VERSION(v) (((v) & 0xFF000000) >> 24)
+#define __MDM_MINOR_VERSION(v) (((v) & 0x00FF0000) >> 16)
+#define __MDM_VERSION(v) ((__MDM_MAJOR_VERSION(v) + 2) * 10 + __MDM_MINOR_VERSION(v))
+
+
+#endif // _HAL_DESC_H_
diff --git a/drivers/net/wireless/eswin/ipc_compat.h b/drivers/net/wireless/eswin/ipc_compat.h
new file mode 100644
index 000000000000..3d0e71a31c35
--- /dev/null
+++ b/drivers/net/wireless/eswin/ipc_compat.h
@@ -0,0 +1,25 @@
+/**
+ ****************************************************************************************
+ *
+ * @file ipc_compat.h
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ****************************************************************************************
+ */
+
+#ifndef _IPC_H_
+#define _IPC_H_
+
+#define __INLINE static __attribute__((__always_inline__)) inline
+
+#define __ALIGN4 __aligned(4)
+
+#define ASSERT_ERR(condition) \
+ do { \
+ if (unlikely(!(condition))) { \
+ printk(DBG_PREFIX KERN_ERR "%s:%d:ASSERT_ERR(" #condition ")\n", __FILE__, __LINE__); \
+ } \
+ } while(0)
+
+#endif /* _IPC_H_ */
diff --git a/drivers/net/wireless/eswin/ipc_host.c b/drivers/net/wireless/eswin/ipc_host.c
new file mode 100644
index 000000000000..17ef5c150041
--- /dev/null
+++ b/drivers/net/wireless/eswin/ipc_host.c
@@ -0,0 +1,784 @@
+/**
+ ******************************************************************************
+ *
+ * @file ipc_host.c
+ *
+ * @brief IPC module.
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+
+/*
+ * INCLUDE FILES
+ ******************************************************************************
+ */
+#ifndef __KERNEL__
+#include <stdio.h>
+#define REG_SW_SET_PROFILING(env, value) do{ }while(0)
+#define REG_SW_CLEAR_PROFILING(env, value) do{ }while(0)
+#define REG_SW_CLEAR_HOSTBUF_IDX_PROFILING(env) do{ }while(0)
+#define REG_SW_SET_HOSTBUF_IDX_PROFILING(env, val) do{ }while(0)
+#else
+#include <linux/spinlock.h>
+#include "ecrnx_defs.h"
+#include "ecrnx_prof.h"
+#endif
+
+#include "reg_ipc_app.h"
+#include "ipc_host.h"
+
+#ifdef CONFIG_ECRNX_ESWIN
+#include "eswin_utils.h"
+#endif
+
+/*
+ * TYPES DEFINITION
+ ******************************************************************************
+ */
+
+const int nx_txdesc_cnt[] =
+{
+ NX_TXDESC_CNT0,
+ NX_TXDESC_CNT1,
+ NX_TXDESC_CNT2,
+ NX_TXDESC_CNT3,
+ #if NX_TXQ_CNT == 5
+ NX_TXDESC_CNT4,
+ #endif
+};
+
+const int nx_txdesc_cnt_msk[] =
+{
+ NX_TXDESC_CNT0 - 1,
+ NX_TXDESC_CNT1 - 1,
+ NX_TXDESC_CNT2 - 1,
+ NX_TXDESC_CNT3 - 1,
+ #if NX_TXQ_CNT == 5
+ NX_TXDESC_CNT4 - 1,
+ #endif
+};
+
+const int nx_txuser_cnt[] =
+{
+ CONFIG_USER_MAX,
+ CONFIG_USER_MAX,
+ CONFIG_USER_MAX,
+ CONFIG_USER_MAX,
+ #if NX_TXQ_CNT == 5
+ 1,
+ #endif
+};
+
+
+/*
+ * FUNCTIONS DEFINITIONS
+ ******************************************************************************
+ */
+/**
+ * ipc_host_rxdesc_handler() - Handle the reception of a Rx Descriptor
+ *
+ * @env: pointer to the IPC Host environment
+ *
+ * Called from general IRQ handler when status %IPC_IRQ_E2A_RXDESC is set
+ */
+static void ipc_host_rxdesc_handler(struct ipc_host_env_tag *env)
+{
+ // For profiling
+ REG_SW_SET_PROFILING(env->pthis, SW_PROF_IRQ_E2A_RXDESC);
+
+ // LMAC has triggered an IT saying that a reception has occurred.
+ // Then we first need to check the validity of the current hostbuf, and the validity
+ // of the next hostbufs too, because it is likely that several hostbufs have been
+ // filled within the time needed for this irq handling
+ do {
+ #ifdef CONFIG_ECRNX_FULLMAC
+ // call the external function to indicate that a RX descriptor is received
+ if (env->cb.recv_data_ind(env->pthis,
+ env->ipc_host_rxdesc_array[env->ipc_host_rxdesc_idx].hostid) != 0)
+ #else
+ // call the external function to indicate that a RX packet is received
+ if (env->cb.recv_data_ind(env->pthis,
+ env->ipc_host_rxbuf_array[env->ipc_host_rxbuf_idx].hostid) != 0)
+ #endif //(CONFIG_ECRNX_FULLMAC)
+ break;
+
+ }while(1);
+
+ // For profiling
+ REG_SW_CLEAR_PROFILING(env->pthis, SW_PROF_IRQ_E2A_RXDESC);
+}
+
+/**
+ * ipc_host_radar_handler() - Handle the reception of radar events
+ *
+ * @env: pointer to the IPC Host environment
+ *
+ * Called from general IRQ handler when status %IPC_IRQ_E2A_RADAR is set
+ */
+static void ipc_host_radar_handler(struct ipc_host_env_tag *env)
+{
+#ifdef CONFIG_ECRNX_RADAR
+ // LMAC has triggered an IT saying that a radar event has been sent to upper layer.
+ // Then we first need to check the validity of the current msg buf, and the validity
+ // of the next buffers too, because it is likely that several buffers have been
+ // filled within the time needed for this irq handling
+ // call the external function to indicate that a RX packet is received
+ spin_lock(&((struct ecrnx_hw *)env->pthis)->radar.lock);
+ while (env->cb.recv_radar_ind(env->pthis,
+ env->ipc_host_radarbuf_array[env->ipc_host_radarbuf_idx].hostid) == 0)
+ ;
+ spin_unlock(&((struct ecrnx_hw *)env->pthis)->radar.lock);
+#endif /* CONFIG_ECRNX_RADAR */
+}
+
+/**
+ * ipc_host_unsup_rx_vec_handler() - Handle the reception of unsupported rx vector
+ *
+ * @env: pointer to the IPC Host environment
+ *
+ * Called from general IRQ handler when status %IPC_IRQ_E2A_UNSUP_RX_VEC is set
+ */
+static void ipc_host_unsup_rx_vec_handler(struct ipc_host_env_tag *env)
+{
+ while (env->cb.recv_unsup_rx_vec_ind(env->pthis,
+ env->ipc_host_unsuprxvecbuf_array[env->ipc_host_unsuprxvecbuf_idx].hostid) == 0)
+ ;
+}
+
+/**
+ * ipc_host_msg_handler() - Handler for firmware message
+ *
+ * @env: pointer to the IPC Host environment
+ *
+ * Called from general IRQ handler when status %IPC_IRQ_E2A_MSG is set
+ */
+static void ipc_host_msg_handler(struct ipc_host_env_tag *env)
+{
+ // For profiling
+ REG_SW_SET_PROFILING(env->pthis, SW_PROF_IRQ_E2A_MSG);
+
+ // LMAC has triggered an IT saying that a message has been sent to upper layer.
+ // Then we first need to check the validity of the current msg buf, and the validity
+ // of the next buffers too, because it is likely that several buffers have been
+ // filled within the time needed for this irq handling
+ // call the external function to indicate that a RX packet is received
+ while (env->cb.recv_msg_ind(env->pthis,
+ env->ipc_host_msgbuf_array[env->ipc_host_msge2a_idx].hostid) == 0)
+ ;
+
+
+ // For profiling
+ REG_SW_CLEAR_PROFILING(env->pthis, SW_PROF_IRQ_E2A_MSG);
+
+}
+
+/**
+ * ipc_host_msgack_handler() - Handle the reception of message acknowledgement
+ *
+ * @env: pointer to the IPC Host environment
+ *
+ * Called from general IRQ handler when status %IPC_IRQ_E2A_MSG_ACK is set
+ */
+static void ipc_host_msgack_handler(struct ipc_host_env_tag *env)
+{
+ void *hostid = env->msga2e_hostid;
+
+ ASSERT_ERR(hostid);
+ ASSERT_ERR(env->msga2e_cnt == (((struct lmac_msg *)(&env->shared->msg_a2e_buf.msg))->src_id & 0xFF));
+
+ env->msga2e_hostid = NULL;
+ env->msga2e_cnt++;
+ env->cb.recv_msgack_ind(env->pthis, hostid);
+}
+
+/**
+ * ipc_host_dbg_handler() - Handle the reception of Debug event
+ *
+ * @env: pointer to the IPC Host environment
+ *
+ * Called from general IRQ handler when status %IPC_IRQ_E2A_DBG is set
+ */
+static void ipc_host_dbg_handler(struct ipc_host_env_tag *env)
+{
+ // For profiling
+ REG_SW_SET_PROFILING(env->pthis, SW_PROF_IRQ_E2A_DBG);
+
+ // LMAC has triggered an IT saying that a DBG message has been sent to upper layer.
+ // Then we first need to check the validity of the current buffer, and the validity
+ // of the next buffers too, because it is likely that several buffers have been
+ // filled within the time needed for this irq handling
+ // call the external function to indicate that a RX packet is received
+ while(env->cb.recv_dbg_ind(env->pthis,
+ env->ipc_host_dbgbuf_array[env->ipc_host_dbg_idx].hostid) == 0)
+ ;
+
+ // For profiling
+ REG_SW_CLEAR_PROFILING(env->pthis, SW_PROF_IRQ_E2A_DBG);
+}
+
+/**
+ * ipc_host_tx_cfm_handler() - Handle the reception of TX confirmation
+ *
+ * @env: pointer to the IPC Host environment
+ * @queue_idx: index of the hardware on which the confirmation has been received
+ * @user_pos: index of the user position
+ *
+ * Called from general IRQ handler when status %IPC_IRQ_E2A_TXCFM is set
+ */
+static void ipc_host_tx_cfm_handler(struct ipc_host_env_tag *env,
+ const int queue_idx, const int user_pos)
+{
+ // TX confirmation descriptors have been received
+ REG_SW_SET_PROFILING(env->pthis, SW_PROF_IRQ_E2A_TXCFM);
+ while (1)
+ {
+ // Get the used index and increase it. We do the increase before knowing if the
+ // current buffer is confirmed because the callback function may call the
+ // ipc_host_txdesc_get() in case flow control was enabled and the index has to be
+ // already at the good value to ensure that the test of FIFO full is correct
+ uint32_t used_idx = env->txdesc_used_idx[queue_idx][user_pos]++;
+ uint32_t used_idx_mod = used_idx & nx_txdesc_cnt_msk[queue_idx];
+ void *host_id = env->tx_host_id[queue_idx][user_pos][used_idx_mod];
+
+ // Reset the host id in the array
+ env->tx_host_id[queue_idx][user_pos][used_idx_mod] = 0;
+
+ // call the external function to indicate that a TX packet is freed
+ if (host_id == 0)
+ {
+ // No more confirmations, so put back the used index at its initial value
+ env->txdesc_used_idx[queue_idx][user_pos] = used_idx;
+ break;
+ }
+
+ if (env->cb.send_data_cfm(env->pthis, host_id) != 0)
+ {
+ // No more confirmations, so put back the used index at its initial value
+ env->txdesc_used_idx[queue_idx][user_pos] = used_idx;
+ env->tx_host_id[queue_idx][user_pos][used_idx_mod] = host_id;
+ // and exit the loop
+ break;
+ }
+
+ REG_SW_SET_PROFILING_CHAN(env->pthis, SW_PROF_CHAN_CTXT_CFM_HDL_BIT);
+ REG_SW_CLEAR_PROFILING_CHAN(env->pthis, SW_PROF_CHAN_CTXT_CFM_HDL_BIT);
+ }
+
+ REG_SW_CLEAR_PROFILING(env->pthis, SW_PROF_IRQ_E2A_TXCFM);
+}
+
+/**
+ ******************************************************************************
+ */
+bool ipc_host_tx_frames_pending(struct ipc_host_env_tag *env)
+{
+ int i, j;
+ bool tx_frames_pending = false;
+
+ for (i = 0; (i < IPC_TXQUEUE_CNT) && !tx_frames_pending; i++)
+ {
+ for (j = 0; j < nx_txuser_cnt[i]; j++)
+ {
+ uint32_t used_idx = env->txdesc_used_idx[i][j];
+ uint32_t free_idx = env->txdesc_free_idx[i][j];
+
+ // Check if this queue is empty or not
+ if (used_idx != free_idx)
+ {
+ // The queue is not empty, update the flag and exit
+ tx_frames_pending = true;
+ break;
+ }
+ }
+ }
+
+ return (tx_frames_pending);
+}
+
+/**
+ ******************************************************************************
+ */
+void *ipc_host_tx_flush(struct ipc_host_env_tag *env, const int queue_idx, const int user_pos)
+{
+ uint32_t used_idx = env->txdesc_used_idx[queue_idx][user_pos];
+ void *host_id = env->tx_host_id[queue_idx][user_pos][used_idx & nx_txdesc_cnt_msk[queue_idx]];
+
+ // call the external function to indicate that a TX packet is freed
+ if (host_id != 0)
+ {
+ // Reset the host id in the array
+ env->tx_host_id[queue_idx][user_pos][used_idx & nx_txdesc_cnt_msk[queue_idx]] = 0;
+
+ // Increment the used index
+ env->txdesc_used_idx[queue_idx][user_pos]++;
+ }
+
+ return (host_id);
+}
+
+/**
+ ******************************************************************************
+ */
+void ipc_host_init(struct ipc_host_env_tag *env,
+ struct ipc_host_cb_tag *cb,
+ struct ipc_shared_env_tag *shared_env_ptr,
+ void *pthis)
+{
+ unsigned int i;
+ unsigned int size;
+ unsigned int * dst;
+
+ // Reset the environments
+ // Reset the IPC Shared memory
+#if 0
+ /* check potential platform bug on multiple stores */
+ memset(shared_env_ptr, 0, sizeof(struct ipc_shared_env_tag));
+#else
+ dst = (unsigned int *)shared_env_ptr;
+ size = (unsigned int)sizeof(struct ipc_shared_env_tag);
+ for (i=0; i < size; i+=4)
+ {
+ *dst++ = 0;
+ }
+#endif
+ // Reset the IPC Host environment
+ memset(env, 0, sizeof(struct ipc_host_env_tag));
+
+ // Initialize the shared environment pointer
+ env->shared = shared_env_ptr;
+
+ // Save the callbacks in our own environment
+ env->cb = *cb;
+
+ // Save the pointer to the register base
+ env->pthis = pthis;
+
+ // Initialize buffers numbers and buffers sizes needed for DMA Receptions
+ env->rx_bufnb = IPC_RXBUF_CNT;
+ #ifdef CONFIG_ECRNX_FULLMAC
+ env->rxdesc_nb = IPC_RXDESC_CNT;
+ #endif //(CONFIG_ECRNX_FULLMAC)
+ env->radar_bufnb = IPC_RADARBUF_CNT;
+ env->radar_bufsz = sizeof(struct radar_pulse_array_desc);
+ env->unsuprxvec_bufnb = IPC_UNSUPRXVECBUF_CNT;
+ env->unsuprxvec_bufsz = max(sizeof(struct rx_vector_desc), (size_t) RADIOTAP_HDR_MAX_LEN) +
+ RADIOTAP_HDR_VEND_MAX_LEN + UNSUP_RX_VEC_DATA_LEN;
+ env->ipc_e2amsg_bufnb = IPC_MSGE2A_BUF_CNT;
+ env->ipc_e2amsg_bufsz = sizeof(struct ipc_e2a_msg);
+ env->ipc_dbg_bufnb = IPC_DBGBUF_CNT;
+ env->ipc_dbg_bufsz = sizeof(struct ipc_dbg_msg);
+
+ for (i = 0; i < CONFIG_USER_MAX; i++)
+ {
+ // Initialize the pointers to the hostid arrays
+ env->tx_host_id[0][i] = env->tx_host_id0[i];
+ env->tx_host_id[1][i] = env->tx_host_id1[i];
+ env->tx_host_id[2][i] = env->tx_host_id2[i];
+ env->tx_host_id[3][i] = env->tx_host_id3[i];
+ #if NX_TXQ_CNT == 5
+ env->tx_host_id[4][i] = NULL;
+ #endif
+
+ // Initialize the pointers to the TX descriptor arrays
+ env->txdesc[0][i] = shared_env_ptr->txdesc0[i];
+ env->txdesc[1][i] = shared_env_ptr->txdesc1[i];
+ env->txdesc[2][i] = shared_env_ptr->txdesc2[i];
+ env->txdesc[3][i] = shared_env_ptr->txdesc3[i];
+ #if NX_TXQ_CNT == 5
+ env->txdesc[4][i] = NULL;
+ #endif
+ }
+
+ #if NX_TXQ_CNT == 5
+ env->tx_host_id[4][0] = env->tx_host_id4[0];
+ env->txdesc[4][0] = shared_env_ptr->txdesc4[0];
+ #endif
+}
+
+/**
+ ******************************************************************************
+ */
+void ipc_host_patt_addr_push(struct ipc_host_env_tag *env, uint32_t addr)
+{
+ struct ipc_shared_env_tag *shared_env_ptr = env->shared;
+
+ // Copy the address
+ shared_env_ptr->pattern_addr = addr;
+}
+
+/**
+ ******************************************************************************
+ */
+int ipc_host_rxbuf_push(struct ipc_host_env_tag *env,
+#ifdef CONFIG_ECRNX_SOFTMAC
+ void *hostid,
+#else
+ uint32_t hostid,
+#endif
+ uint32_t hostbuf)
+{
+ struct ipc_shared_env_tag *shared_env_ptr = env->shared;
+
+ REG_SW_CLEAR_HOSTBUF_IDX_PROFILING(env->pthis);
+ REG_SW_SET_HOSTBUF_IDX_PROFILING(env->pthis, env->ipc_host_rxbuf_idx);
+
+#ifdef CONFIG_ECRNX_FULLMAC
+ // Copy the hostbuf (DMA address) in the ipc shared memory
+ shared_env_ptr->host_rxbuf[env->ipc_host_rxbuf_idx].hostid = hostid;
+ shared_env_ptr->host_rxbuf[env->ipc_host_rxbuf_idx].dma_addr = hostbuf;
+#else
+ // Save the hostid and the hostbuf in global array
+ env->ipc_host_rxbuf_array[env->ipc_host_rxbuf_idx].hostid = hostid;
+ env->ipc_host_rxbuf_array[env->ipc_host_rxbuf_idx].dma_addr = hostbuf;
+
+ shared_env_ptr->host_rxbuf[env->ipc_host_rxbuf_idx] = hostbuf;
+#endif //(CONFIG_ECRNX_FULLMAC)
+
+ // Signal to the embedded CPU that at least one buffer is available
+ ipc_app2emb_trigger_set(shared_env_ptr, IPC_IRQ_A2E_RXBUF_BACK);
+
+ // Increment the array index
+ env->ipc_host_rxbuf_idx = (env->ipc_host_rxbuf_idx +1)%IPC_RXBUF_CNT;
+
+ return (0);
+}
+
+#ifdef CONFIG_ECRNX_FULLMAC
+/**
+ ******************************************************************************
+ */
+int ipc_host_rxdesc_push(struct ipc_host_env_tag *env, void *hostid,
+ uint32_t hostbuf)
+{
+ struct ipc_shared_env_tag *shared_env_ptr = env->shared;
+
+ // Reset the RX Descriptor DMA Address and increment the counter
+ env->ipc_host_rxdesc_array[env->ipc_host_rxdesc_idx].dma_addr = hostbuf;
+ env->ipc_host_rxdesc_array[env->ipc_host_rxdesc_idx].hostid = hostid;
+
+ shared_env_ptr->host_rxdesc[env->ipc_host_rxdesc_idx].dma_addr = hostbuf;
+
+ // Signal to the embedded CPU that at least one descriptor is available
+ ipc_app2emb_trigger_set(shared_env_ptr, IPC_IRQ_A2E_RXDESC_BACK);
+
+ env->ipc_host_rxdesc_idx = (env->ipc_host_rxdesc_idx + 1) % IPC_RXDESC_CNT;
+
+ return (0);
+}
+#endif /* CONFIG_ECRNX_FULLMAC */
+
+/**
+ ******************************************************************************
+ */
+int ipc_host_radarbuf_push(struct ipc_host_env_tag *env, void *hostid,
+ uint32_t hostbuf)
+{
+ struct ipc_shared_env_tag *shared_env_ptr = env->shared;
+
+ // Save the hostid and the hostbuf in global array
+ env->ipc_host_radarbuf_array[env->ipc_host_radarbuf_idx].hostid = hostid;
+ env->ipc_host_radarbuf_array[env->ipc_host_radarbuf_idx].dma_addr = hostbuf;
+
+ // Copy the hostbuf (DMA address) in the ipc shared memory
+ shared_env_ptr->radarbuf_hostbuf[env->ipc_host_radarbuf_idx] = hostbuf;
+
+ // Increment the array index
+ env->ipc_host_radarbuf_idx = (env->ipc_host_radarbuf_idx +1)%IPC_RADARBUF_CNT;
+
+ return (0);
+}
+
+/**
+ ******************************************************************************
+ */
+
+int ipc_host_unsup_rx_vec_buf_push(struct ipc_host_env_tag *env,
+ void *hostid,
+ uint32_t hostbuf)
+{
+ struct ipc_shared_env_tag *shared_env_ptr = env->shared;
+
+ env->ipc_host_unsuprxvecbuf_array[env->ipc_host_unsuprxvecbuf_idx].hostid = hostid;
+ env->ipc_host_unsuprxvecbuf_array[env->ipc_host_unsuprxvecbuf_idx].dma_addr = hostbuf;
+
+ // Copy the hostbuf (DMA address) in the ipc shared memory
+ shared_env_ptr->unsuprxvecbuf_hostbuf[env->ipc_host_unsuprxvecbuf_idx] = hostbuf;
+
+ // Increment the array index
+ env->ipc_host_unsuprxvecbuf_idx = (env->ipc_host_unsuprxvecbuf_idx + 1)%IPC_UNSUPRXVECBUF_CNT;
+
+ return (0);
+}
+
+/**
+ ******************************************************************************
+ */
+int ipc_host_msgbuf_push(struct ipc_host_env_tag *env, void *hostid,
+ uint64_t hostbuf)
+{
+ struct ipc_shared_env_tag *shared_env_ptr = env->shared;
+
+ // Save the hostid and the hostbuf in global array
+ env->ipc_host_msgbuf_array[env->ipc_host_msge2a_idx].hostid = hostid;
+ env->ipc_host_msgbuf_array[env->ipc_host_msge2a_idx].dma_addr = hostbuf;
+
+ // Copy the hostbuf (DMA address) in the ipc shared memory
+ shared_env_ptr->msg_e2a_hostbuf_addr[env->ipc_host_msge2a_idx] = hostbuf;
+
+ // Increment the array index
+ env->ipc_host_msge2a_idx = (env->ipc_host_msge2a_idx +1)%IPC_MSGE2A_BUF_CNT;
+
+ return (0);
+}
+
+/**
+ ******************************************************************************
+ */
+int ipc_host_dbgbuf_push(struct ipc_host_env_tag *env, void *hostid,
+ uint64_t hostbuf)
+{
+ struct ipc_shared_env_tag *shared_env_ptr = env->shared;
+
+ // Save the hostid and the hostbuf in global array
+ env->ipc_host_dbgbuf_array[env->ipc_host_dbg_idx].hostid = hostid;
+ env->ipc_host_dbgbuf_array[env->ipc_host_dbg_idx].dma_addr = hostbuf;
+
+ // Copy the hostbuf (DMA address) in the ipc shared memory
+ shared_env_ptr->dbg_hostbuf_addr[env->ipc_host_dbg_idx] = hostbuf;
+
+ // Increment the array index
+ env->ipc_host_dbg_idx = (env->ipc_host_dbg_idx +1)%IPC_DBGBUF_CNT;
+
+ return (0);
+}
+
+/**
+ ******************************************************************************
+ */
+void ipc_host_dbginfobuf_push(struct ipc_host_env_tag *env, uint32_t infobuf)
+{
+ struct ipc_shared_env_tag *shared_env_ptr = env->shared;
+
+ // Copy the hostbuf (DMA address) in the ipc shared memory
+ shared_env_ptr->la_dbginfo_addr = infobuf;
+}
+
+/**
+ ******************************************************************************
+ */
+volatile struct txdesc_host *ipc_host_txdesc_get(struct ipc_host_env_tag *env, const int queue_idx, const int user_pos)
+{
+ volatile struct txdesc_host *txdesc_free;
+ uint32_t used_idx = env->txdesc_used_idx[queue_idx][user_pos];
+ uint32_t free_idx = env->txdesc_free_idx[queue_idx][user_pos];
+
+ ASSERT_ERR(queue_idx < IPC_TXQUEUE_CNT);
+ ASSERT_ERR((free_idx - used_idx) <= nx_txdesc_cnt[queue_idx]);
+
+ // Check if a free descriptor is available
+ if (free_idx != (used_idx + nx_txdesc_cnt[queue_idx]))
+ {
+ // Get the pointer to the first free descriptor
+ txdesc_free = env->txdesc[queue_idx][user_pos] + (free_idx & nx_txdesc_cnt_msk[queue_idx]);
+ }
+ else
+ {
+ txdesc_free = NULL;
+ }
+
+ return txdesc_free;
+}
+
+/**
+ ******************************************************************************
+ */
+void ipc_host_txdesc_push(struct ipc_host_env_tag *env, const int queue_idx,
+ const int user_pos, void *host_id)
+{
+ uint32_t free_idx = env->txdesc_free_idx[queue_idx][user_pos] & nx_txdesc_cnt_msk[queue_idx];
+ volatile struct txdesc_host *txdesc_pushed = env->txdesc[queue_idx][user_pos] + free_idx;
+
+
+ // Descriptor is now ready
+ txdesc_pushed->ready = 0xFFFFFFFF;
+
+ // Save the host id in the environment
+ env->tx_host_id[queue_idx][user_pos][free_idx] = host_id;
+
+ // Increment the index
+ env->txdesc_free_idx[queue_idx][user_pos]++;
+
+ // trigger interrupt!!!
+ //REG_SW_SET_PROFILING(env->pthis, CO_BIT(queue_idx+SW_PROF_IRQ_A2E_TXDESC_FIRSTBIT));
+ ipc_app2emb_trigger_setf(env->shared, CO_BIT(user_pos + queue_idx * CONFIG_USER_MAX +
+ IPC_IRQ_A2E_TXDESC_FIRSTBIT));
+}
+
+/**
+ ******************************************************************************
+ */
+void ipc_host_irq(struct ipc_host_env_tag *env, uint32_t status)
+{
+ // Acknowledge the pending interrupts
+ ipc_emb2app_ack_clear(env->shared, status);
+ // And re-read the status, just to be sure that the acknowledgment is
+ // effective when we start the interrupt handling
+ ipc_emb2app_status_get(env->shared);
+
+ // Optimized for only one IRQ at a time
+ if (status & IPC_IRQ_E2A_RXDESC)
+ {
+ // handle the RX descriptor reception
+ ipc_host_rxdesc_handler(env);
+ }
+ if (status & IPC_IRQ_E2A_MSG_ACK)
+ {
+ ipc_host_msgack_handler(env);
+ }
+ if (status & IPC_IRQ_E2A_MSG)
+ {
+ ipc_host_msg_handler(env);
+ }
+ if (status & IPC_IRQ_E2A_TXCFM)
+ {
+ int i;
+
+#ifdef __KERNEL__
+ spin_lock(&((struct ecrnx_hw *)env->pthis)->tx_lock);
+#endif
+ // handle the TX confirmation reception
+ for (i = 0; i < IPC_TXQUEUE_CNT; i++)
+ {
+ int j = 0;
+#ifdef CONFIG_ECRNX_MUMIMO_TX
+ for (; j < nx_txuser_cnt[i]; j++)
+#endif
+ {
+ uint32_t q_bit = CO_BIT(j + i * CONFIG_USER_MAX + IPC_IRQ_E2A_TXCFM_POS);
+ if (status & q_bit)
+ {
+ // handle the confirmation
+ ipc_host_tx_cfm_handler(env, i, j);
+ }
+ }
+ }
+#ifdef __KERNEL__
+ spin_unlock(&((struct ecrnx_hw *)env->pthis)->tx_lock);
+#endif
+ }
+ if (status & IPC_IRQ_E2A_RADAR)
+ {
+ // handle the radar event reception
+ ipc_host_radar_handler(env);
+ }
+
+ if (status & IPC_IRQ_E2A_UNSUP_RX_VEC)
+ {
+ // handle the unsupported rx vector reception
+ ipc_host_unsup_rx_vec_handler(env);
+ }
+
+ if (status & IPC_IRQ_E2A_DBG)
+ {
+ ipc_host_dbg_handler(env);
+ }
+
+ if (status & IPC_IRQ_E2A_TBTT_PRIM)
+ {
+ env->cb.prim_tbtt_ind(env->pthis);
+ }
+
+ if (status & IPC_IRQ_E2A_TBTT_SEC)
+ {
+ env->cb.sec_tbtt_ind(env->pthis);
+ }
+}
+
+/**
+ ******************************************************************************
+ */
+int ipc_host_msg_push(struct ipc_host_env_tag *env, void *msg_buf, uint16_t len)
+{
+#ifndef CONFIG_ECRNX_ESWIN
+ int i;
+ uint32_t *src, *dst;
+#endif
+
+ REG_SW_SET_PROFILING(env->pthis, SW_PROF_IPC_MSGPUSH);
+
+ ASSERT_ERR(!env->msga2e_hostid);
+ ASSERT_ERR(round_up(len, 4) <= sizeof(env->shared->msg_a2e_buf.msg));
+
+#ifndef CONFIG_ECRNX_ESWIN
+ // Copy the message into the IPC MSG buffer
+#ifdef __KERNEL__
+ src = (uint32_t*)((struct ecrnx_cmd *)msg_buf)->a2e_msg;
+#else
+ src = (uint32_t*) msg_buf;
+#endif
+ dst = (uint32_t*)&(env->shared->msg_a2e_buf.msg);
+
+ // Copy the message in the IPC queue
+ for (i=0; i<len; i+=4)
+ {
+ *dst++ = *src++;
+ }
+
+ env->msga2e_hostid = msg_buf;
+
+ // Trigger the irq to send the message to EMB
+ ipc_app2emb_trigger_set(env->shared, IPC_IRQ_A2E_MSG);
+#else
+ env->msga2e_hostid = msg_buf;
+ ecrnx_msg_send(msg_buf, len);
+#endif
+
+ REG_SW_CLEAR_PROFILING(env->pthis, SW_PROF_IPC_MSGPUSH);
+
+ return (0);
+}
+
+/**
+ ******************************************************************************
+ */
+void ipc_host_enable_irq(struct ipc_host_env_tag *env, uint32_t value)
+{
+ // Enable the handled interrupts
+ ipc_emb2app_unmask_set(env->shared, value);
+}
+
+/**
+ ******************************************************************************
+ */
+void ipc_host_disable_irq(struct ipc_host_env_tag *env, uint32_t value)
+{
+ // Enable the handled interrupts
+ ipc_emb2app_unmask_clear(env->shared, value);
+}
+
+/**
+ ******************************************************************************
+ */
+uint32_t ipc_host_get_status(struct ipc_host_env_tag *env)
+{
+ volatile uint32_t status;
+
+ status = ipc_emb2app_status_get(env->shared);
+
+ return status;
+}
+
+/**
+ ******************************************************************************
+ */
+uint32_t ipc_host_get_rawstatus(struct ipc_host_env_tag *env)
+{
+ volatile uint32_t rawstatus;
+
+ rawstatus = ipc_emb2app_rawstatus_get(env->shared);
+
+ return rawstatus;
+}
+
diff --git a/drivers/net/wireless/eswin/ipc_host.h b/drivers/net/wireless/eswin/ipc_host.h
new file mode 100644
index 000000000000..127be2ac7036
--- /dev/null
+++ b/drivers/net/wireless/eswin/ipc_host.h
@@ -0,0 +1,517 @@
+/**
+ ******************************************************************************
+ *
+ * @file ipc_host.h
+ *
+ * @brief IPC module.
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+#ifndef _IPC_HOST_H_
+#define _IPC_HOST_H_
+
+/*
+ * INCLUDE FILES
+ ******************************************************************************
+ */
+#include "ipc_shared.h"
+#ifndef __KERNEL__
+#include "arch.h"
+#else
+#include "ipc_compat.h"
+#endif
+
+/*
+ * ENUMERATION
+ ******************************************************************************
+ */
+
+enum ipc_host_desc_status
+{
+ /// Descriptor is IDLE
+ IPC_HOST_DESC_IDLE = 0,
+ /// Data can be forwarded
+ IPC_HOST_DESC_FORWARD,
+ /// Data has to be kept in UMAC memory
+ IPC_HOST_DESC_KEEP,
+ /// Delete stored packet
+ IPC_HOST_DESC_DELETE,
+ /// Update Frame Length status
+ IPC_HOST_DESC_LEN_UPDATE,
+};
+
+/**
+ ******************************************************************************
+ * @brief This structure is used to initialize the MAC SW
+ *
+ * The WLAN device driver provides functions call-back with this structure
+ ******************************************************************************
+ */
+struct ipc_host_cb_tag
+{
+ /// WLAN driver call-back function: send_data_cfm
+ int (*send_data_cfm)(void *pthis, void *host_id);
+
+ /// WLAN driver call-back function: handle_data_cfm
+ int (*handle_data_cfm)(void *pthis, void *host_id);
+
+ /// WLAN driver call-back function: recv_data_ind
+ uint8_t (*recv_data_ind)(void *pthis, void *host_id);
+
+ /// WLAN driver call-back function: recv_radar_ind
+ uint8_t (*recv_radar_ind)(void *pthis, void *host_id);
+
+ /// WLAN driver call-back function: recv_unsup_rx_vec_ind
+ uint8_t (*recv_unsup_rx_vec_ind)(void *pthis, void *host_id);
+
+ /// WLAN driver call-back function: recv_msg_ind
+ uint8_t (*recv_msg_ind)(void *pthis, void *host_id);
+
+ /// WLAN driver call-back function: recv_msgack_ind
+ uint8_t (*recv_msgack_ind)(void *pthis, void *host_id);
+
+ /// WLAN driver call-back function: recv_dbg_ind
+ uint8_t (*recv_dbg_ind)(void *pthis, void *host_id);
+
+ /// WLAN driver call-back function: prim_tbtt_ind
+ void (*prim_tbtt_ind)(void *pthis);
+
+ /// WLAN driver call-back function: sec_tbtt_ind
+ void (*sec_tbtt_ind)(void *pthis);
+
+};
+
+/*
+ * Struct used to store information about host buffers (DMA Address and local pointer)
+ */
+struct ipc_hostbuf
+{
+ void *hostid; ///< ptr to hostbuf client (ipc_host client) structure
+ uint64_t dma_addr; ///< ptr to real hostbuf dma address
+};
+
+/// Definition of the IPC Host environment structure.
+struct ipc_host_env_tag
+{
+ /// Structure containing the callback pointers
+ struct ipc_host_cb_tag cb;
+
+ /// Pointer to the shared environment
+ struct ipc_shared_env_tag *shared;
+
+ #ifdef CONFIG_ECRNX_FULLMAC
+ // Array used to store the descriptor addresses
+ struct ipc_hostbuf ipc_host_rxdesc_array[IPC_RXDESC_CNT];
+ // Index of the host RX descriptor array (ipc_shared environment)
+ uint8_t ipc_host_rxdesc_idx;
+ /// Store the number of RX Descriptors
+ uint8_t rxdesc_nb;
+ #endif //(CONFIG_ECRNX_FULLMAC)
+
+ /// Fields for Data Rx handling
+ #ifdef CONFIG_ECRNX_SOFTMAC
+ // Global array used to store the hostid and hostbuf addresses
+ struct ipc_hostbuf ipc_host_rxbuf_array[IPC_RXBUF_CNT];
+ #endif // CONFIG_ECRNX_SOFTMAC
+ // Index used for ipc_host_rxbuf_array to point to current buffer
+ uint8_t ipc_host_rxbuf_idx;
+ // Store the number of Rx Data buffers
+ uint32_t rx_bufnb;
+ // Store the size of the Rx Data buffers
+ uint32_t rx_bufsz;
+
+ /// Fields for Radar events handling
+ // Global array used to store the hostid and hostbuf addresses
+ struct ipc_hostbuf ipc_host_radarbuf_array[IPC_RADARBUF_CNT];
+ // Index used for ipc_host_rxbuf_array to point to current buffer
+ uint8_t ipc_host_radarbuf_idx;
+ // Store the number of radar event buffers
+ uint32_t radar_bufnb;
+ // Store the size of the radar event buffers
+ uint32_t radar_bufsz;
+
+ ///Fields for Unsupported frame handling
+ // Global array used to store the hostid and hostbuf addresses
+ struct ipc_hostbuf ipc_host_unsuprxvecbuf_array[IPC_UNSUPRXVECBUF_CNT];
+ // Index used for ipc_host_unsuprxvecbuf_array to point to current buffer
+ uint8_t ipc_host_unsuprxvecbuf_idx;
+ // Store the number of unsupported rx vector buffers
+ uint32_t unsuprxvec_bufnb;
+ // Store the size of unsupported rx vector buffers
+ uint32_t unsuprxvec_bufsz;
+
+ // Index used that points to the first free TX desc
+ uint32_t txdesc_free_idx[IPC_TXQUEUE_CNT][CONFIG_USER_MAX];
+ // Index used that points to the first used TX desc
+ uint32_t txdesc_used_idx[IPC_TXQUEUE_CNT][CONFIG_USER_MAX];
+ // Array storing the currently pushed host ids for the BK queue
+ void *tx_host_id0[CONFIG_USER_MAX][NX_TXDESC_CNT0];
+ // Array storing the currently pushed host ids for the BE queue
+ void *tx_host_id1[CONFIG_USER_MAX][NX_TXDESC_CNT1];
+ // Array storing the currently pushed host ids for the VI queue
+ void *tx_host_id2[CONFIG_USER_MAX][NX_TXDESC_CNT2];
+ // Array storing the currently pushed host ids for the VO queue
+ void *tx_host_id3[CONFIG_USER_MAX][NX_TXDESC_CNT3];
+ #if NX_TXQ_CNT == 5
+ // Array storing the currently pushed host ids for the BCN queue
+ void *tx_host_id4[1][NX_TXDESC_CNT4];
+ #endif
+ // Pointer to the different host ids arrays, per IPC queue
+ void **tx_host_id[IPC_TXQUEUE_CNT][CONFIG_USER_MAX];
+ // Pointer to the different TX descriptor arrays, per IPC queue
+ volatile struct txdesc_host *txdesc[IPC_TXQUEUE_CNT][CONFIG_USER_MAX];
+
+ /// Fields for Emb->App MSGs handling
+ // Global array used to store the hostid and hostbuf addresses for msg/ind
+ struct ipc_hostbuf ipc_host_msgbuf_array[IPC_MSGE2A_BUF_CNT];
+ // Index of the MSG E2A buffers array to point to current buffer
+ uint8_t ipc_host_msge2a_idx;
+ // Store the number of E2A MSG buffers
+ uint32_t ipc_e2amsg_bufnb;
+ // Store the size of the E2A MSG buffers
+ uint32_t ipc_e2amsg_bufsz;
+
+ /// E2A ACKs of A2E MSGs
+ uint8_t msga2e_cnt;
+ void *msga2e_hostid;
+
+ /// Fields for Debug MSGs handling
+ // Global array used to store the hostid and hostbuf addresses for Debug messages
+ struct ipc_hostbuf ipc_host_dbgbuf_array[IPC_DBGBUF_CNT];
+ // Index of the Debug messages buffers array to point to current buffer
+ uint8_t ipc_host_dbg_idx;
+ // Store the number of Debug messages buffers
+ uint32_t ipc_dbg_bufnb;
+ // Store the size of the Debug messages buffers
+ uint32_t ipc_dbg_bufsz;
+
+ /// Pointer to the attached object (used in callbacks and register accesses)
+ void *pthis;
+};
+
+extern const int nx_txdesc_cnt[];
+extern const int nx_txuser_cnt[];
+
+/**
+ ******************************************************************************
+ * @brief Returns the full/not full status of the queue the index of which is
+ * passed as parameter.
+ *
+ * @param[in] env Pointer to the IPC host environment
+ * @param[in] queue_idx Index of the queue to be checked
+ *
+ * @return true if the queue is full, false otherwise
+ *
+ ******************************************************************************
+ */
+__INLINE bool ipc_host_queue_full(struct ipc_host_env_tag *env,
+ const int queue_idx)
+{
+ return (env->txdesc_free_idx[queue_idx] ==
+ (env->txdesc_used_idx[queue_idx] + nx_txdesc_cnt[queue_idx]));
+}
+
+/**
+ ******************************************************************************
+ * @brief Initialize the IPC running on the Application CPU.
+ *
+ * This function:
+ * - initializes the IPC software environments
+ * - enables the interrupts in the IPC block
+ *
+ * @param[in] env Pointer to the IPC host environment
+ *
+ * @warning Since this function resets the IPC Shared memory, it must be called
+ * before the LMAC FW is launched because LMAC sets some init values in IPC
+ * Shared memory at boot.
+ *
+ ******************************************************************************
+ */
+void ipc_host_init(struct ipc_host_env_tag *env,
+ struct ipc_host_cb_tag *cb,
+ struct ipc_shared_env_tag *shared_env_ptr,
+ void *pthis);
+
+/** @addtogroup IPC_TX
+ * @{
+ */
+
+/**
+ ******************************************************************************
+ * @brief Retrieve a new free Tx descriptor (host side).
+ *
+ * This function returns a pointer to the next Tx descriptor available from the
+ * queue queue_idx to the host driver. The driver will have to fill it with the
+ * appropriate endianness and to send it to the
+ * emb side with ipc_host_txdesc_push().
+ *
+ * This function should only be called once until ipc_host_txdesc_push() is called.
+ *
+ * This function will return NULL if the queue is full.
+ *
+ * @param[in] env Pointer to the IPC host environment
+ * @param[in] queue_idx Queue index. The index can be inferred from the
+ * user priority of the incoming packet.
+ * @param[in] user_pos User position. If MU-MIMO is not used, this value
+ * shall be 0.
+ * @return Pointer to the next Tx descriptor free. This can
+ * point to the host memory or to shared memory,
+ * depending on IPC implementation.
+ *
+ ******************************************************************************
+ */
+volatile struct txdesc_host *ipc_host_txdesc_get(struct ipc_host_env_tag *env,
+ const int queue_idx,
+ const int user_pos);
+
+
+/**
+ ******************************************************************************
+ * @brief Push a filled Tx descriptor (host side).
+ *
+ * This function sets the next Tx descriptor available by the host side:
+ * - as used for the host side
+ * - as available for the emb side.
+ * The Tx descriptor must be correctly filled before calling this function.
+ *
+ * This function may trigger an IRQ to the emb CPU depending on the interrupt
+ * mitigation policy and on the push count.
+ *
+ * @param[in] env Pointer to the IPC host environment
+ * @param[in] queue_idx Queue index. Same value than ipc_host_txdesc_get()
+ * @param[in] user_pos User position. If MU-MIMO is not used, this value
+ * shall be 0.
+ * @param[in] host_id Parameter indicated by the IPC at TX confirmation,
+ * that allows the driver finding the buffer
+ *
+ ******************************************************************************
+ */
+void ipc_host_txdesc_push(struct ipc_host_env_tag *env, const int queue_idx,
+ const int user_pos, void *host_id);
+
+
+/**
+ ******************************************************************************
+ * @brief Check if there are TX frames pending in the TX queues.
+ *
+ * @param[in] env Pointer to the IPC host environment
+ *
+ * @return true if there are frames pending, false otherwise.
+ *
+ ******************************************************************************
+ */
+bool ipc_host_tx_frames_pending(struct ipc_host_env_tag *env);
+
+/**
+ ******************************************************************************
+ * @brief Get and flush a packet from the IPC queue passed as parameter.
+ *
+ * @param[in] env Pointer to the IPC host environment
+ * @param[in] queue_idx Index of the queue to flush
+ * @param[in] user_pos User position to flush
+ *
+ * @return The flushed hostid if there is one, 0 otherwise.
+ *
+ ******************************************************************************
+ */
+void *ipc_host_tx_flush(struct ipc_host_env_tag *env, const int queue_idx,
+ const int user_pos);
+
+/// @} IPC_TX
+
+/** @addtogroup IPC_RX
+ * @{
+ */
+void ipc_host_patt_addr_push(struct ipc_host_env_tag *env, uint32_t addr);
+
+/**
+ ******************************************************************************
+ * @brief Push a pre-allocated buffer descriptor for Rx packet (host side)
+ *
+ * This function should be called by the host IRQ handler to supply the
+ * embedded side with new empty buffer.
+ *
+ * @param[in] env Pointer to the IPC host environment
+ * @param[in] hostid Packet ID used by the host (skbuff pointer on Linux)
+ * @param[in] hostbuf Pointer to the start of the buffer payload in the
+ * host memory (this may be inferred from the skbuff?)
+ * The length of this buffer should be predefined
+ * between host and emb statically (constant needed?).
+ *
+ ******************************************************************************
+ */
+int ipc_host_rxbuf_push(struct ipc_host_env_tag *env,
+#ifdef CONFIG_ECRNX_SOFTMAC
+ void *hostid,
+#else
+ uint32_t hostid,
+#endif
+ uint32_t hostbuf);
+
+/**
+ ******************************************************************************
+ * @brief Push a pre-allocated Descriptor
+ *
+ * This function should be called by the host IRQ handler to supply the
+ * embedded side with new empty buffer.
+ *
+ * @param[in] env Pointer to the IPC host environment
+ * @param[in] hostid Address of packet for host
+ * @param[in] hostbuf Pointer to the start of the buffer payload in the
+ * host memory. The length of this buffer should be
+ * predefined between host and emb statically.
+ *
+ ******************************************************************************
+ */
+int ipc_host_rxdesc_push(struct ipc_host_env_tag *env, void *hostid,
+ uint32_t hostbuf);
+
+/**
+ ******************************************************************************
+ * @brief Push a pre-allocated radar event buffer descriptor
+ *
+ * This function is called at Init time to initialize all radar event buffers.
+ * Then each time embedded send a radar event, this function is used to push
+ * back the same buffer once it has been handled.
+ *
+ * @param[in] env Pointer to the IPC host environment
+ * @param[in] hostid Address of packet for host
+ * @param[in] hostbuf Pointer to the start of the buffer payload in the
+ * host memory. The length of this buffer should be
+ * predefined between host and emb statically.
+ *
+ ******************************************************************************
+ */
+int ipc_host_radarbuf_push(struct ipc_host_env_tag *env, void *hostid,
+ uint32_t hostbuf);
+
+/**
+ ******************************************************************************
+ * @brief Push a pre-allocated unsupported rx vector buffer descriptor
+ *
+ * This function is called at Init time to initialize all unsupported rx vector
+ * buffers. Then each time the embedded sends a unsupported rx vector, this
+ * function is used to push a new unsupported rx vector buffer.
+ *
+ * @param[in] env Pointer to the IPC host environment
+ * @param[in] hostid Address of packet for host
+ * @param[in] hostbuf Pointer to the start of the buffer payload in the
+ * host memory. The length of this buffer should be
+ * predefined between host and emb statically.
+ *
+ ******************************************************************************
+ */
+int ipc_host_unsup_rx_vec_buf_push(struct ipc_host_env_tag *env, void *hostid,
+ uint32_t hostbuf);
+
+/**
+ ******************************************************************************
+ * @brief Push a pre-allocated buffer descriptor for IPC MSGs (host side)
+ *
+ * This function is called at Init time to initialize all Emb2App messages
+ * buffers. Then each time embedded send a IPC message, this function is used
+ * to push back the same buffer once it has been handled.
+ *
+ * @param[in] env Pointer to the IPC host environment
+ * @param[in] hostid Address of buffer for host
+ * @param[in] hostbuf Address of buffer for embedded
+ * The length of this buffer should be predefined
+ * between host and emb statically.
+ *
+ ******************************************************************************
+ */
+int ipc_host_msgbuf_push(struct ipc_host_env_tag *env, void *hostid,
+ uint64_t hostbuf);
+
+/**
+ ******************************************************************************
+ * @brief Push a pre-allocated buffer descriptor for Debug messages (host side)
+ *
+ * This function is called at Init time to initialize all debug messages.
+ * Then each time embedded send a debug message, this function is used to push
+ * back the same buffer once it has been handled.
+ *
+ * @param[in] env Pointer to the IPC host environment
+ * @param[in] hostid Address of buffer for host
+ * @param[in] hostbuf Address of buffer for embedded
+ * The length of this buffer should be predefined
+ * between host and emb statically.
+ *
+ ******************************************************************************
+ */
+int ipc_host_dbgbuf_push(struct ipc_host_env_tag *env, void *hostid,
+ uint64_t hostbuf);
+
+/**
+ ******************************************************************************
+ * @brief Push the pre-allocated logic analyzer and debug information buffer
+ *
+ * @param[in] env Pointer to the IPC host environment
+ * @param[in] infobuf Address of buffer for embedded
+ * The length of this buffer should be predefined
+ * between host and emb statically.
+ *
+ ******************************************************************************
+ */
+void ipc_host_dbginfobuf_push(struct ipc_host_env_tag *env, uint32_t infobuf);
+
+/// @} IPC_RX
+
+
+
+/** @addtogroup IPC_MISC
+ * @{
+ */
+
+/**
+ ******************************************************************************
+ * @brief Handle all IPC interrupts on the host side.
+ *
+ * The following interrupts should be handled:
+ * Tx confirmation, Rx buffer requests, Rx packet ready and kernel messages
+ *
+ * @param[in] env Pointer to the IPC host environment
+ *
+ ******************************************************************************
+ */
+void ipc_host_irq(struct ipc_host_env_tag *env, uint32_t status);
+
+/**
+ ******************************************************************************
+ * @brief Send a message to the embedded side
+ *
+ * @param[in] env Pointer to the IPC host environment
+ * @param[in] msg_buf Pointer to the message buffer
+ * @param[in] msg_len Length of the message to be transmitted
+ *
+ * @return Non-null value on failure
+ *
+ ******************************************************************************
+ */
+int ipc_host_msg_push(struct ipc_host_env_tag *env, void *msg_buf, uint16_t len);
+
+/**
+ ******************************************************************************
+ * @brief Enable IPC interrupts
+ *
+ * @param[in] env Global ipc_host environment pointer
+ * @param[in] value Bitfield of the interrupts to enable
+ *
+ * @warning After calling this function, IPC interrupts can be triggered at any
+ * time. Potentially, an interrupt could happen even before returning from the
+ * function if there is a request pending from the embedded side.
+ *
+ ******************************************************************************
+ */
+void ipc_host_enable_irq(struct ipc_host_env_tag *env, uint32_t value);
+void ipc_host_disable_irq(struct ipc_host_env_tag *env, uint32_t value);
+
+uint32_t ipc_host_get_status(struct ipc_host_env_tag *env);
+uint32_t ipc_host_get_rawstatus(struct ipc_host_env_tag *env);
+
+/// @} IPC_MISC
+
+
+#endif // _IPC_HOST_H_
diff --git a/drivers/net/wireless/eswin/ipc_shared.h b/drivers/net/wireless/eswin/ipc_shared.h
new file mode 100644
index 000000000000..e6d7ebf17609
--- /dev/null
+++ b/drivers/net/wireless/eswin/ipc_shared.h
@@ -0,0 +1,836 @@
+/**
+ ****************************************************************************************
+ *
+ * @file ipc_shared.h
+ *
+ * @brief Shared data between both IPC modules.
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ****************************************************************************************
+ */
+
+#ifndef _IPC_SHARED_H_
+#define _IPC_SHARED_H_
+
+/*
+ * INCLUDE FILES
+ ****************************************************************************************
+ */
+#include "ipc_compat.h"
+#include "lmac_mac.h"
+
+/*
+ * DEFINES AND MACROS
+ ****************************************************************************************
+ */
+#define CO_BIT(pos) (1U<<(pos))
+
+#define IPC_TXQUEUE_CNT NX_TXQ_CNT
+#define NX_TXDESC_CNT0 8
+#define NX_TXDESC_CNT1 64
+#define NX_TXDESC_CNT2 64
+#define NX_TXDESC_CNT3 32
+#if NX_TXQ_CNT == 5
+#define NX_TXDESC_CNT4 8
+#endif
+
+/*
+ * Number of Host buffers available for Data Rx handling (through DMA)
+ */
+#define IPC_RXBUF_CNT 128
+
+/*
+ * Number of shared descriptors available for Data RX handling
+ */
+#define IPC_RXDESC_CNT 128
+
+/*
+ * Number of Host buffers available for Radar events handling (through DMA)
+ */
+#define IPC_RADARBUF_CNT 16
+
+/*
+ * Number of Host buffers available for unsupported Rx vectors handling (through DMA)
+ */
+#define IPC_UNSUPRXVECBUF_CNT 8
+
+/*
+ * Size of RxVector
+ */
+#define IPC_RXVEC_SIZE 16
+
+/*
+ * Number of Host buffers available for Emb->App MSGs sending (through DMA)
+ */
+#ifdef CONFIG_ECRNX_SOFTMAC
+#define IPC_MSGE2A_BUF_CNT 4
+#else
+#define IPC_MSGE2A_BUF_CNT 64
+#endif
+/*
+ * Number of Host buffers available for Debug Messages sending (through DMA)
+ */
+#define IPC_DBGBUF_CNT 32
+
+/*
+ * Length used in MSGs structures
+ */
+#define IPC_A2E_MSG_BUF_SIZE 127 // size in 4-byte words
+#ifdef CONFIG_ECRNX_SOFTMAC
+#define IPC_E2A_MSG_SIZE_BASE 15 // size in 4-byte words
+#else
+#define IPC_E2A_MSG_SIZE_BASE 256 // size in 4-byte words
+#endif
+
+#ifdef CONFIG_ECRNX_TL4
+#define IPC_E2A_MSG_PARAM_SIZE (IPC_E2A_MSG_SIZE_BASE + (IPC_E2A_MSG_SIZE_BASE / 2))
+#else
+#define IPC_E2A_MSG_PARAM_SIZE IPC_E2A_MSG_SIZE_BASE
+#endif
+
+/*
+ * Debug messages buffers size (in bytes)
+ */
+#define IPC_DBG_PARAM_SIZE 256
+
+/*
+ * Define used for Rx hostbuf validity.
+ * This value should appear only when hostbuf was used for a Reception.
+ */
+#define RX_DMA_OVER_PATTERN 0xAAAAAA00
+
+/*
+ * Define used for MSG buffers validity.
+ * This value will be written only when a MSG buffer is used for sending from Emb to App.
+ */
+#define IPC_MSGE2A_VALID_PATTERN 0xADDEDE2A
+
+/*
+ * Define used for Debug messages buffers validity.
+ * This value will be written only when a DBG buffer is used for sending from Emb to App.
+ */
+#define IPC_DBG_VALID_PATTERN 0x000CACA0
+
+/*
+ * Length of the receive vectors, in bytes
+ */
+#define DMA_HDR_PHYVECT_LEN 36
+
+/*
+ * Maximum number of payload addresses and lengths present in the descriptor
+ */
+#define NX_TX_PAYLOAD_MAX 6
+
+/*
+ * Message struct/ID API version
+ */
+#define MSG_API_VER 25
+
+/*
+ ****************************************************************************************
+ */
+// c.f LMAC/src/tx/tx_swdesc.h
+/// Descriptor filled by the Host
+struct hostdesc
+{
+#ifdef CONFIG_ECRNX_SPLIT_TX_BUF
+ /// Pointers to packet payloads
+ u32_l packet_addr[NX_TX_PAYLOAD_MAX];
+ /// Sizes of the MPDU/MSDU payloads
+ u16_l packet_len[NX_TX_PAYLOAD_MAX];
+ /// Number of payloads forming the MPDU
+ u8_l packet_cnt;
+#else
+ /// Pointer to packet payload
+ u32_l packet_addr[2];
+ /// Size of the payload
+ u16_l packet_len;
+#endif //(NX_AMSDU_TX)
+
+#ifdef CONFIG_ECRNX_FULLMAC
+ /// Address of the status descriptor in host memory (used for confirmation upload)
+ u32_l status_desc_addr[2];
+ /// Destination Address
+ struct mac_addr eth_dest_addr;
+ /// Source Address
+ struct mac_addr eth_src_addr;
+ /// Ethernet Type
+ u16_l ethertype;
+ /// Buffer containing the PN to be used for this packet
+ u16_l pn[4];
+ /// Sequence Number used for transmission of this MPDU
+ u16_l sn;
+ /// Timestamp of first transmission of this MPDU
+ u16_l timestamp;
+ /// TX flags
+ u16_l flags;
+#else /* ! CONFIG_ECRNX_FULLMAC */
+#ifdef CONFIG_ECRNX_AGG_TX
+ ///Sequence Number for AMPDU MPDUs - for quick check if it's allowed within window
+ u16_l sn;
+#endif /* CONFIG_ECRNX_AGG_TX */
+ /// Padding between the buffer control structure and the MPDU in host memory
+ u8_l padding;
+#endif /* CONFIG_ECRNX_FULLMAC */
+ /// Packet TID (0xFF if not a QoS frame)
+ u8_l tid;
+ /// Interface Id
+ u8_l vif_idx;
+ /// Station Id (0xFF if station is unknown)
+ u8_l staid;
+#ifdef CONFIG_ECRNX_MUMIMO_TX
+ /// MU-MIMO information (GroupId and User Position in the group) - The GroupId
+ /// is located on bits 0-5 and the User Position on bits 6-7. The GroupId value is set
+ /// to 63 if MU-MIMO shall not be used
+ u8_l mumimo_info;
+#endif /* CONFIG_ECRNX_MUMIMO_TX */
+};
+
+/// Descriptor filled by the UMAC
+struct umacdesc
+{
+#if defined CONFIG_ECRNX_SOFTMAC && defined CONFIG_ECRNX_BFMER
+ /**
+ * Flags from UMAC which do not fit with tx_hd.macctrlinfo2 format
+ * Bit 0 - Indicate if frame can be beamformed when sent as a singleton
+ */
+ u16_l tx_flags;
+#endif // defined CONFIG_ECRNX_SOFTMAC && defined CONFIG_ECRNX_BFMER
+#ifdef CONFIG_ECRNX_AGG_TX
+ ///First Sequence Number of the BlockAck window
+ u16_l sn_win;
+ /// Flags from UMAC (match tx_hd.macctrlinfo2 format)
+ u32_l flags;
+ /// PHY related flags field - rate, GI type, BW type - filled by driver
+ u32_l phy_flags;
+#endif //(CONFIG_ECRNX_AGG_TX)
+};
+
+struct txdesc_api
+{
+ /// Information provided by Host
+ struct hostdesc host;
+#ifdef CONFIG_ECRNX_SOFTMAC
+ /// Information provided by UMAC
+ struct umacdesc umac;
+#endif
+};
+
+
+struct txdesc_host
+{
+ u32_l ready;
+
+ /// API of the embedded part
+ struct txdesc_api api;
+};
+
+/// Comes from ipc_dma.h
+/// Element in the pool of TX DMA bridge descriptors.
+struct dma_desc
+{
+#ifdef CONFIG_ECRNX_ESWIN
+ uint8_t split;
+#endif
+
+ /** Application subsystem address which is used as source address for DMA payload
+ * transfer*/
+ u32_l src;
+ /** Points to the start of the embedded data buffer associated with this descriptor.
+ * This address acts as the destination address for the DMA payload transfer*/
+ u32_l dest;
+ /// Complete length of the buffer in memory
+ u16_l length;
+ /// Control word for the DMA engine (e.g. for interrupt generation)
+ u16_l ctrl;
+ /// Pointer to the next element of the chained list
+ u32_l next;
+};
+
+// Comes from la.h
+/// Length of the configuration data of a logic analyzer
+#define LA_CONF_LEN 10
+
+/// Structure containing the configuration data of a logic analyzer
+struct la_conf_tag
+{
+ u32_l conf[LA_CONF_LEN];
+ u32_l trace_len;
+ u32_l diag_conf;
+};
+
+/// Size of a logic analyzer memory
+#define LA_MEM_LEN (1024 * 1024)
+
+/// Type of errors
+enum
+{
+ /// Recoverable error, not requiring any action from Upper MAC
+ DBG_ERROR_RECOVERABLE = 0,
+ /// Fatal error, requiring Upper MAC to reset Lower MAC and HW and restart operation
+ DBG_ERROR_FATAL
+};
+
+/// Maximum length of the SW diag trace
+#define DBG_SW_DIAG_MAX_LEN 1024
+
+/// Maximum length of the error trace
+#define DBG_ERROR_TRACE_SIZE 256
+
+/// Number of MAC diagnostic port banks
+#define DBG_DIAGS_MAC_MAX 48
+
+/// Number of PHY diagnostic port banks
+#define DBG_DIAGS_PHY_MAX 32
+
+/// Maximum size of the RX header descriptor information in the debug dump
+#define DBG_RHD_MEM_LEN (5 * 1024)
+
+/// Maximum size of the RX buffer descriptor information in the debug dump
+#define DBG_RBD_MEM_LEN (5 * 1024)
+
+/// Maximum size of the TX header descriptor information in the debug dump
+#define DBG_THD_MEM_LEN (10 * 1024)
+
+/// Structure containing the information about the PHY channel that is used
+struct phy_channel_info
+{
+ /// PHY channel information 1
+ u32_l info1;
+ /// PHY channel information 2
+ u32_l info2;
+};
+
+/// Debug information forwarded to host when an error occurs
+struct dbg_debug_info_tag
+{
+ /// Type of error (0: recoverable, 1: fatal)
+ u32_l error_type;
+ /// Pointer to the first RX Header Descriptor chained to the MAC HW
+ u32_l rhd;
+ /// Size of the RX header descriptor buffer
+ u32_l rhd_len;
+ /// Pointer to the first RX Buffer Descriptor chained to the MAC HW
+ u32_l rbd;
+ /// Size of the RX buffer descriptor buffer
+ u32_l rbd_len;
+ /// Pointer to the first TX Header Descriptors chained to the MAC HW
+ u32_l thd[NX_TXQ_CNT];
+ /// Size of the TX header descriptor buffer
+ u32_l thd_len[NX_TXQ_CNT];
+ /// MAC HW diag configuration
+ u32_l hw_diag;
+ /// Error message
+ u32_l error[DBG_ERROR_TRACE_SIZE/4];
+ /// SW diag configuration length
+ u32_l sw_diag_len;
+ /// SW diag configuration
+ u32_l sw_diag[DBG_SW_DIAG_MAX_LEN/4];
+ /// PHY channel information
+ struct phy_channel_info chan_info;
+ /// Embedded LA configuration
+ struct la_conf_tag la_conf;
+ /// MAC diagnostic port state
+ u16_l diags_mac[DBG_DIAGS_MAC_MAX];
+ /// PHY diagnostic port state
+ u16_l diags_phy[DBG_DIAGS_PHY_MAX];
+ /// MAC HW RX Header descriptor pointer
+ u32_l rhd_hw_ptr;
+ /// MAC HW RX Buffer descriptor pointer
+ u32_l rbd_hw_ptr;
+};
+
+/// Full debug dump that is forwarded to host in case of error
+struct dbg_debug_dump_tag
+{
+ /// Debug information
+ struct dbg_debug_info_tag dbg_info;
+
+ /// RX header descriptor memory
+ u32_l rhd_mem[DBG_RHD_MEM_LEN/4];
+
+ /// RX buffer descriptor memory
+ u32_l rbd_mem[DBG_RBD_MEM_LEN/4];
+
+ /// TX header descriptor memory
+ u32_l thd_mem[NX_TXQ_CNT][DBG_THD_MEM_LEN/4];
+
+ /// Logic analyzer memory
+ u32_l la_mem[LA_MEM_LEN/4];
+};
+
+
+/// Number of pulses in a radar event structure
+#define RADAR_PULSE_MAX 4
+
+/// Definition of an array of radar pulses
+struct radar_pulse_array_desc
+{
+ /// Buffer containing the radar pulses
+ u32_l pulse[RADAR_PULSE_MAX];
+ /// Index of the radar detection chain that detected those pulses
+ u32_l idx;
+ /// Number of valid pulses in the buffer
+ u32_l cnt;
+};
+
+/// Bit mapping inside a radar pulse element
+struct radar_pulse {
+ s32_l freq:6; /** Freq (resolution is 2Mhz range is [-Fadc/4 .. Fadc/4]) */
+ u32_l fom:4; /** Figure of Merit */
+ u32_l len:6; /** Length of the current radar pulse (resolution is 2us) */
+ u32_l rep:16; /** Time interval between the previous radar event
+ and the current one (in us) */
+};
+
+/// Definition of a RX vector descriptor
+struct rx_vector_desc
+{
+ /// PHY channel information
+ struct phy_channel_info phy_info;
+
+ /// RX vector 1
+ u32_l rx_vect1[IPC_RXVEC_SIZE/4];
+
+ /// Used to print a valid rx vector
+ u32_l pattern;
+};
+
+///
+struct rxdesc_tag
+{
+ /// Host Buffer Address
+ u32_l host_id;
+ /// Length
+ u32_l frame_len;
+ /// Status
+ u16_l status;
+};
+
+/**
+ ****************************************************************************************
+ * @defgroup IPC IPC
+ * @ingroup NXMAC
+ * @brief Inter Processor Communication module.
+ *
+ * The IPC module implements the protocol to communicate between the Host CPU
+ * and the Embedded CPU.
+ *
+ * @see http://en.wikipedia.org/wiki/Circular_buffer
+ * For more information about the ring buffer typical use and difficulties.
+ ****************************************************************************************
+ */
+
+
+/**
+ ****************************************************************************************
+ * @addtogroup IPC_TX IPC Tx path
+ * @ingroup IPC
+ * @brief IPC Tx path structures and functions
+ *
+ * A typical use case of the IPC Tx path API:
+ * @msc
+ * hscale = "2";
+ *
+ * a [label=Driver],
+ * b [label="IPC host"],
+ * c [label="IPC emb"],
+ * d [label=Firmware];
+ *
+ * --- [label="Tx descriptor queue example"];
+ * a=>a [label="Driver receives a Tx packet from OS"];
+ * a=>b [label="ipc_host_txdesc_get()"];
+ * a<<b [label="struct txdesc_host *"];
+ * a=>a [label="Driver fill the descriptor"];
+ * a=>b [label="ipc_host_txdesc_push()"];
+ * ... [label="(several Tx desc can be pushed)"];
+ * b:>c [label="Tx desc queue filled IRQ"];
+ * c=>>d [label="EDCA sub-scheduler callback"];
+ * c<<d [label="Tx desc queue to pop"];
+ * c=>>d [label="UMAC Tx desc callback"];
+ * ... [label="(several Tx desc can be popped)"];
+ * d=>d [label="Packets are sent or discarded"];
+ * --- [label="Tx confirm queue example"];
+ * c<=d [label="ipc_emb_txcfm_push()"];
+ * c>>d [label="Request accepted"];
+ * ... [label="(several Tx cfm can be pushed)"];
+ * b<:c [label="Tx cfm queue filled IRQ"];
+ * a<<=b [label="Driver's Tx Confirm callback"];
+ * a=>b [label="ipc_host_txcfm_pop()"];
+ * a<<b [label="struct ipc_txcfm"];
+ * a<=a [label="Packets are freed by the driver"];
+ * @endmsc
+ *
+ * @{
+ ****************************************************************************************
+ */
+
+/// @} IPC_TX
+
+/**
+ ****************************************************************************************
+ * @defgroup IPC_RX IPC Rx path
+ * @ingroup IPC
+ * @brief IPC Rx path functions and structures
+ *
+ * A typical use case of the IPC Rx path API:
+ * @msc
+ * hscale = "2";
+ *
+ * a [label=Firmware],
+ * b [label="IPC emb"],
+ * c [label="IPC host"],
+ * d [label=Driver];
+ *
+ * --- [label="Rx buffer and desc queues usage example"];
+ * d=>c [label="ipc_host_rxbuf_push()"];
+ * d=>c [label="ipc_host_rxbuf_push()"];
+ * d=>c [label="ipc_host_rxbuf_push()"];
+ * ... [label="(several Rx buffer are pushed)"];
+ * a=>a [label=" Frame is received\n from the medium"];
+ * a<<b [label="struct ipc_rxbuf"];
+ * a=>a [label=" Firmware fill the buffer\n with received frame"];
+ * a<<b [label="Push accepted"];
+ * ... [label="(several Rx desc can be pushed)"];
+ * b:>c [label="Rx desc queue filled IRQ"];
+ * c=>>d [label="Driver Rx packet callback"];
+ * c<=d [label="ipc_host_rxdesc_pop()"];
+ * d=>d [label="Rx packet is handed \nover to the OS "];
+ * ... [label="(several Rx desc can be poped)"];
+ * --- [label="Rx buffer request exemple"];
+ * b:>c [label="Low Rx buffer count IRQ"];
+ * a<<b [label="struct ipc_rxbuf"];
+ * c=>>d [label="Driver Rx buffer callback"];
+ * d=>c [label="ipc_host_rxbuf_push()"];
+ * d=>c [label="ipc_host_rxbuf_push()"];
+ * d=>c [label="ipc_host_rxbuf_push()"];
+ * ... [label="(several Rx buffer are pushed)"];
+ * @endmsc
+ *
+ * @addtogroup IPC_RX
+ * @{
+ ****************************************************************************************
+ */
+
+/// @} IPC_RX
+
+
+
+/**
+ ****************************************************************************************
+ * @defgroup IPC_MISC IPC Misc
+ * @ingroup IPC
+ * @brief IPC miscellaneous functions
+ ****************************************************************************************
+ */
+/** IPC header structure. This structure is stored at the beginning of every IPC message.
+ * @warning This structure's size must NOT exceed 4 bytes in length.
+ */
+struct ipc_header
+{
+ /// IPC message type.
+ u16_l type;
+ /// IPC message size in number of bytes.
+ u16_l size;
+};
+
+struct ipc_msg_elt
+{
+ /// Message header (alignment forced on word size, see allocation in shared env).
+ struct ipc_header header __ALIGN4;
+};
+
+/// Message structure for MSGs from Emb to App
+struct ipc_e2a_msg
+{
+ u16_l id; ///< Message id.
+ u16_l dummy_dest_id;
+ u16_l dummy_src_id;
+#ifdef CONFIG_ECRNX_ESWIN
+ u8_l hostid[8];
+#endif
+
+ u16_l param_len; ///< Parameter embedded struct length.
+ u32_l param[IPC_E2A_MSG_PARAM_SIZE]; ///< Parameter embedded struct. Must be word-aligned.
+ u32_l pattern; ///< Used to stamp a valid MSG buffer
+};
+
+/// Message structure for Debug messages from Emb to App
+struct ipc_dbg_msg
+{
+ u32_l string[IPC_DBG_PARAM_SIZE/4]; ///< Debug string
+ u32_l pattern; ///< Used to stamp a valid buffer
+};
+
+/// Message structure for MSGs from App to Emb.
+/// Actually a sub-structure will be used when filling the messages.
+struct ipc_a2e_msg
+{
+ u32_l dummy_word; // used to cope with kernel message structure
+ u32_l msg[IPC_A2E_MSG_BUF_SIZE]; // body of the msg
+};
+
+struct ipc_shared_rx_buf
+{
+ /// < ptr to hostbuf client (ipc_host client) structure
+ u64_l hostid;
+ /// < ptr to real hostbuf dma address
+ u64_l dma_addr;
+};
+
+struct ipc_shared_rx_desc
+{
+ /// DMA Address
+ u64_l dma_addr;
+};
+
+/// Structure containing FW characteristics for compatibility checking
+struct compatibility_tag {
+ /// Size of IPC shared memory
+ u16_l ipc_shared_size;
+ /// Message struct/ID API version
+ u16_l msg_api;
+ /// Version of IPC shared
+ u8_l ipc_shared_version;
+ /// Number of host buffers available for Emb->App MSGs sending
+ u8_l msge2a_buf_cnt;
+ /// Number of host buffers available for Debug Messages sending
+ u8_l dbgbuf_cnt;
+ /// Number of host buffers available for Radar events handling
+ u8_l radarbuf_cnt;
+ /// Number of host buffers available for unsupported Rx vectors handling
+ u8_l unsuprxvecbuf_cnt;
+ /// Number of shared descriptors available for Data RX handling
+ u8_l rxdesc_cnt;
+ /// Number of host buffers available for Data Rx handling
+ u8_l rxbuf_cnt;
+ /// Number of descriptors in BK TX queue (power of 2, min 4, max 64)
+ u8_l bk_txq;
+ /// Number of descriptors in BE TX queue (power of 2, min 4, max 64)
+ u8_l be_txq;
+ /// Number of descriptors in VI TX queue (power of 2, min 4, max 64)
+ u8_l vi_txq;
+ /// Number of descriptors in VO TX queue (power of 2, min 4, max 64)
+ u8_l vo_txq;
+ /// Number of descriptors in BCN TX queue (power of 2, min 4, max 64)
+ u8_l bcn_txq;
+};
+
+/*
+ * TYPE and STRUCT DEFINITIONS
+ ****************************************************************************************
+ */
+
+
+// Indexes are defined in the MIB shared structure
+struct ipc_shared_env_tag
+{
+ volatile struct compatibility_tag comp_info; //FW characteristics
+
+ volatile struct ipc_a2e_msg msg_a2e_buf; // room for MSG to be sent from App to Emb
+
+ // Fields for MSGs sending from Emb to App
+ volatile struct ipc_e2a_msg msg_e2a_buf; // room to build the MSG to be DMA Xferred
+ volatile struct dma_desc msg_dma_desc; // DMA descriptor for Emb->App MSGs Xfers
+ volatile u32_l msg_e2a_hostbuf_addr [IPC_MSGE2A_BUF_CNT]; // buffers @ for DMA Xfers
+
+ // Fields for Debug MSGs sending from Emb to App
+ volatile struct ipc_dbg_msg dbg_buf; // room to build the MSG to be DMA Xferred
+ volatile struct dma_desc dbg_dma_desc; // DMA descriptor for Emb->App MSGs Xfers
+ volatile u32_l dbg_hostbuf_addr [IPC_DBGBUF_CNT]; // buffers @ for MSGs DMA Xfers
+ volatile u32_l la_dbginfo_addr; // Host buffer address for the debug information
+ volatile u32_l pattern_addr;
+ volatile u32_l radarbuf_hostbuf [IPC_RADARBUF_CNT]; // buffers @ for Radar Events
+ volatile u32_l unsuprxvecbuf_hostbuf [IPC_UNSUPRXVECBUF_CNT]; // buffers @ for unsupported Rx vectors
+ volatile struct txdesc_host txdesc0[CONFIG_USER_MAX][NX_TXDESC_CNT0];
+ volatile struct txdesc_host txdesc1[CONFIG_USER_MAX][NX_TXDESC_CNT1];
+ volatile struct txdesc_host txdesc2[CONFIG_USER_MAX][NX_TXDESC_CNT2];
+ volatile struct txdesc_host txdesc3[CONFIG_USER_MAX][NX_TXDESC_CNT3];
+ #if NX_TXQ_CNT == 5
+ volatile struct txdesc_host txdesc4[1][NX_TXDESC_CNT4];
+ #endif
+ #ifdef CONFIG_ECRNX_FULLMAC
+ // RX Descriptors Array
+ volatile struct ipc_shared_rx_desc host_rxdesc[IPC_RXDESC_CNT];
+ // RX Buffers Array
+ volatile struct ipc_shared_rx_buf host_rxbuf[IPC_RXBUF_CNT];
+ #else
+ // buffers @ for Data Rx
+ volatile u32_l host_rxbuf[IPC_RXBUF_CNT];
+ #endif /* CONFIG_ECRNX_FULLMAC */
+
+ u32_l buffered[NX_REMOTE_STA_MAX][TID_MAX];
+
+ volatile uint16_t trace_pattern;
+ volatile uint32_t trace_start;
+ volatile uint32_t trace_end;
+ volatile uint32_t trace_size;
+ volatile uint32_t trace_offset;
+ volatile uint32_t trace_nb_compo;
+ volatile uint32_t trace_offset_compo;
+};
+
+extern struct ipc_shared_env_tag ipc_shared_env;
+
+
+/*
+ * TYPE and STRUCT DEFINITIONS
+ ****************************************************************************************
+ */
+
+// IRQs from app to emb
+/// Interrupts bits used for the TX descriptors of the AC queues
+#ifdef CONFIG_ECRNX_MUMIMO_TX
+#ifdef CONFIG_ECRNX_OLD_IPC
+#error "MU-MIMO cannot be compiled for old IPC"
+#endif
+/// Interrupts bits used
+#if CONFIG_USER_MAX > 3
+#define IPC_IRQ_A2E_USER_MSK 0xF
+#elif CONFIG_USER_MAX > 2
+#define IPC_IRQ_A2E_USER_MSK 0x7
+#else
+#define IPC_IRQ_A2E_USER_MSK 0x3
+#endif
+
+/// Offset of the interrupts for AC0
+#define IPC_IRQ_A2E_AC0_OFT 8
+/// Mask of the interrupts for AC0
+#define IPC_IRQ_A2E_AC0_MSK (IPC_IRQ_A2E_USER_MSK << IPC_IRQ_A2E_AC0_OFT)
+/// Offset of the interrupts for AC1
+#define IPC_IRQ_A2E_AC1_OFT (IPC_IRQ_A2E_AC0_OFT + CONFIG_USER_MAX)
+/// Mask of the interrupts for AC1
+#define IPC_IRQ_A2E_AC1_MSK (IPC_IRQ_A2E_USER_MSK << IPC_IRQ_A2E_AC1_OFT)
+/// Offset of the interrupts for AC2
+#define IPC_IRQ_A2E_AC2_OFT (IPC_IRQ_A2E_AC1_OFT + CONFIG_USER_MAX)
+/// Mask of the interrupts for AC2
+#define IPC_IRQ_A2E_AC2_MSK (IPC_IRQ_A2E_USER_MSK << IPC_IRQ_A2E_AC2_OFT)
+/// Offset of the interrupts for AC3
+#define IPC_IRQ_A2E_AC3_OFT (IPC_IRQ_A2E_AC2_OFT + CONFIG_USER_MAX)
+/// Mask of the interrupts for AC3
+#define IPC_IRQ_A2E_AC3_MSK (IPC_IRQ_A2E_USER_MSK << IPC_IRQ_A2E_AC3_OFT)
+/// Offset of the interrupts for BCN
+#define IPC_IRQ_A2E_BCN_OFT (IPC_IRQ_A2E_AC3_OFT + CONFIG_USER_MAX)
+/// Mask of the interrupts for BCN
+#define IPC_IRQ_A2E_BCN_MSK CO_BIT(IPC_IRQ_A2E_BCN_OFT)
+
+#define IPC_IRQ_A2E_AC_TXDESC (IPC_IRQ_A2E_AC0_MSK | IPC_IRQ_A2E_AC1_MSK | \
+ IPC_IRQ_A2E_AC2_MSK | IPC_IRQ_A2E_AC3_MSK)
+
+/// Interrupts bits used for the TX descriptors of the BCN queue
+#if NX_TXQ_CNT < 5
+#define IPC_IRQ_A2E_BCN_TXDESC 0
+#else
+#define IPC_IRQ_A2E_BCN_TXDESC (0x01 << IPC_IRQ_A2E_BCN_OFT)
+#endif
+
+/// IPC TX descriptor interrupt mask
+#define IPC_IRQ_A2E_TXDESC (IPC_IRQ_A2E_AC_TXDESC | IPC_IRQ_A2E_BCN_TXDESC)
+#else
+/// IPC TX descriptor interrupt mask
+#define IPC_IRQ_A2E_TXDESC 0xFF00
+#endif
+
+#define IPC_IRQ_A2E_TXDESC_FIRSTBIT (8)
+#define IPC_IRQ_A2E_RXBUF_BACK CO_BIT(5)
+#define IPC_IRQ_A2E_RXDESC_BACK CO_BIT(4)
+
+#define IPC_IRQ_A2E_MSG CO_BIT(1)
+#define IPC_IRQ_A2E_DBG CO_BIT(0)
+
+#define IPC_IRQ_A2E_ALL (IPC_IRQ_A2E_TXDESC|IPC_IRQ_A2E_MSG|IPC_IRQ_A2E_DBG)
+
+// IRQs from emb to app
+#define IPC_IRQ_E2A_TXCFM_POS 7
+
+#ifdef CONFIG_ECRNX_MUMIMO_TX
+#ifdef CONFIG_ECRNX_OLD_IPC
+#error "MU-MIMO cannot be compiled for old IPC"
+#endif
+/// Interrupts bits used
+#if CONFIG_USER_MAX > 3
+#define IPC_IRQ_E2A_USER_MSK 0xF
+#elif CONFIG_USER_MAX > 2
+#define IPC_IRQ_E2A_USER_MSK 0x7
+#else
+#define IPC_IRQ_E2A_USER_MSK 0x3
+#endif
+
+/// Offset of the interrupts for AC0
+#define IPC_IRQ_E2A_AC0_OFT IPC_IRQ_E2A_TXCFM_POS
+/// Mask of the interrupts for AC0
+#define IPC_IRQ_E2A_AC0_MSK (IPC_IRQ_E2A_USER_MSK << IPC_IRQ_E2A_AC0_OFT)
+/// Offset of the interrupts for AC1
+#define IPC_IRQ_E2A_AC1_OFT (IPC_IRQ_E2A_AC0_OFT + CONFIG_USER_MAX)
+/// Mask of the interrupts for AC1
+#define IPC_IRQ_E2A_AC1_MSK (IPC_IRQ_E2A_USER_MSK << IPC_IRQ_E2A_AC1_OFT)
+/// Offset of the interrupts for AC2
+#define IPC_IRQ_E2A_AC2_OFT (IPC_IRQ_E2A_AC1_OFT + CONFIG_USER_MAX)
+/// Mask of the interrupts for AC2
+#define IPC_IRQ_E2A_AC2_MSK (IPC_IRQ_E2A_USER_MSK << IPC_IRQ_E2A_AC2_OFT)
+/// Offset of the interrupts for AC3
+#define IPC_IRQ_E2A_AC3_OFT (IPC_IRQ_E2A_AC2_OFT + CONFIG_USER_MAX)
+/// Mask of the interrupts for AC3
+#define IPC_IRQ_E2A_AC3_MSK (IPC_IRQ_E2A_USER_MSK << IPC_IRQ_E2A_AC3_OFT)
+/// Offset of the interrupts for BCN
+#define IPC_IRQ_E2A_BCN_OFT (IPC_IRQ_E2A_AC3_OFT + CONFIG_USER_MAX)
+/// Mask of the interrupts for BCN
+#define IPC_IRQ_E2A_BCN_MSK CO_BIT(IPC_IRQ_E2A_BCN_OFT)
+
+#define IPC_IRQ_E2A_AC_TXCFM (IPC_IRQ_E2A_AC0_MSK | IPC_IRQ_E2A_AC1_MSK | \
+ IPC_IRQ_E2A_AC2_MSK | IPC_IRQ_E2A_AC3_MSK)
+
+/// Interrupts bits used for the TX descriptors of the BCN queue
+#if NX_TXQ_CNT < 5
+#define IPC_IRQ_E2A_BCN_TXCFM 0
+#else
+#define IPC_IRQ_E2A_BCN_TXCFM (0x01 << IPC_IRQ_E2A_BCN_OFT)
+#endif
+
+/// IPC TX descriptor interrupt mask
+#define IPC_IRQ_E2A_TXCFM (IPC_IRQ_E2A_AC_TXCFM | IPC_IRQ_E2A_BCN_TXCFM)
+
+#else
+
+#define IPC_IRQ_E2A_TXCFM ((1 << NX_TXQ_CNT) - 1 ) << IPC_IRQ_E2A_TXCFM_POS
+
+#endif /* CONFIG_ECRNX_MUMIMO_TX */
+
+#define IPC_IRQ_E2A_UNSUP_RX_VEC CO_BIT(7)
+#define IPC_IRQ_E2A_RADAR CO_BIT(6)
+#define IPC_IRQ_E2A_TBTT_SEC CO_BIT(5)
+#define IPC_IRQ_E2A_TBTT_PRIM CO_BIT(4)
+#define IPC_IRQ_E2A_RXDESC CO_BIT(3)
+#define IPC_IRQ_E2A_MSG_ACK CO_BIT(2)
+#define IPC_IRQ_E2A_MSG CO_BIT(1)
+#define IPC_IRQ_E2A_DBG CO_BIT(0)
+
+#define IPC_IRQ_E2A_ALL ( IPC_IRQ_E2A_TXCFM \
+ | IPC_IRQ_E2A_RXDESC \
+ | IPC_IRQ_E2A_MSG_ACK \
+ | IPC_IRQ_E2A_MSG \
+ | IPC_IRQ_E2A_DBG \
+ | IPC_IRQ_E2A_TBTT_PRIM \
+ | IPC_IRQ_E2A_TBTT_SEC \
+ | IPC_IRQ_E2A_RADAR \
+ | IPC_IRQ_E2A_UNSUP_RX_VEC)
+
+// FLAGS for RX desc
+#define IPC_RX_FORWARD CO_BIT(1)
+#define IPC_RX_INTRABSS CO_BIT(0)
+
+
+// IPC message TYPE
+enum
+{
+ IPC_MSG_NONE = 0,
+ IPC_MSG_WRAP,
+ IPC_MSG_KMSG,
+
+ IPC_DBG_STRING,
+
+};
+
+#endif // _IPC_SHARED_H_
+
diff --git a/drivers/net/wireless/eswin/lmac_mac.h b/drivers/net/wireless/eswin/lmac_mac.h
new file mode 100644
index 000000000000..bb31e8be83f4
--- /dev/null
+++ b/drivers/net/wireless/eswin/lmac_mac.h
@@ -0,0 +1,467 @@
+/**
+ ****************************************************************************************
+ *
+ * @file lmac_mac_types.h
+ *
+ * @brief MAC related definitions.
+ *
+ * Adapted from mac_types.h to used lmac_types.h instead of standard types
+ * eg: perl -pi -e '$_ =~ s/uint(\d{1,2})_t/u$1_l/g; \
+ * $_ =~ s/int(\d{1,2})_t/s$1_l/g; \
+ * $_ =~ s/CO_BIT/BIT/g;' lmac_mac.h
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ****************************************************************************************
+ */
+
+#ifndef LMAC_MAC_H_
+#define LMAC_MAC_H_
+
+#include "lmac_types.h"
+
+/// Interface types
+enum mac_vif_type
+{
+ /// ESS STA interface
+ VIF_STA,
+ /// IBSS STA interface
+ VIF_IBSS,
+ /// AP interface
+ VIF_AP,
+ /// Mesh Point interface
+ VIF_MESH_POINT,
+ /// Monitor interface
+ VIF_MONITOR,
+ /// Unknown type
+ VIF_UNKNOWN
+};
+
+/// MAC address length in bytes.
+#define MAC_ADDR_LEN 6
+
+/// MAC address structure.
+struct mac_addr
+{
+ /// Array of 16-bit words that make up the MAC address.
+ u16_l array[MAC_ADDR_LEN/2];
+};
+
+/// SSID maximum length.
+#define MAC_SSID_LEN 32
+
+/// SSID.
+struct mac_ssid
+{
+ /// Actual length of the SSID.
+ u8_l length;
+ /// Array containing the SSID name.
+ u8_l array[MAC_SSID_LEN];
+};
+
+/// BSS type
+enum mac_bss_type
+{
+ INFRASTRUCTURE_MODE = 1,
+ INDEPENDENT_BSS_MODE,
+ ANY_BSS_MODE
+};
+
+/// Channel Band
+enum mac_chan_band
+{
+ /// 2.4GHz Band
+ PHY_BAND_2G4,
+ /// 5GHz band
+ PHY_BAND_5G,
+ /// Number of bands
+ PHY_BAND_MAX,
+};
+
+/// Operating Channel Bandwidth
+enum mac_chan_bandwidth
+{
+ /// 20MHz BW
+ PHY_CHNL_BW_20,
+ /// 40MHz BW
+ PHY_CHNL_BW_40,
+ /// 80MHz BW
+ PHY_CHNL_BW_80,
+ /// 160MHz BW
+ PHY_CHNL_BW_160,
+ /// 80+80MHz BW
+ PHY_CHNL_BW_80P80,
+ /// Reserved BW
+ PHY_CHNL_BW_OTHER,
+};
+
+/// max number of channels in the 2.4 GHZ band
+#define MAC_DOMAINCHANNEL_24G_MAX 14
+
+/// max number of channels in the 5 GHZ band
+#define MAC_DOMAINCHANNEL_5G_MAX 28
+
+/// Channel Flag
+enum mac_chan_flags
+{
+ /// Cannot initiate radiation on this channel
+ CHAN_NO_IR = BIT(0),
+ /// Channel is not allowed
+ CHAN_DISABLED = BIT(1),
+ /// Radar detection required on this channel
+ CHAN_RADAR = BIT(2),
+};
+
+/// Primary Channel definition
+struct mac_chan_def
+{
+ /// Frequency of the channel (in MHz)
+ u16_l freq;
+ /// RF band (@ref mac_chan_band)
+ u8_l band;
+ /// Additional information (@ref mac_chan_flags)
+ u8_l flags;
+ /// Max transmit power allowed on this channel (dBm)
+ s8_l tx_power;
+};
+
+/// Operating Channel
+struct mac_chan_op
+{
+ /// Band (@ref mac_chan_band)
+ u8_l band;
+ /// Channel type (@ref mac_chan_bandwidth)
+ u8_l type;
+ /// Frequency for Primary 20MHz channel (in MHz)
+ u16_l prim20_freq;
+ /// Frequency center of the contiguous channel or center of Primary 80+80 (in MHz)
+ u16_l center1_freq;
+ /// Frequency center of the non-contiguous secondary 80+80 (in MHz)
+ u16_l center2_freq;
+ /// Max transmit power allowed on this channel (dBm)
+ s8_l tx_power;
+ /// Additional information (@ref mac_chan_flags)
+ u8_l flags;
+};
+
+/// Cipher suites (order is important as it is used by MACHW)
+enum mac_cipher_suite
+{
+ /// 00-0F-AC 1
+ MAC_CIPHER_WEP40 = 0,
+ /// 00-0F-AC 2
+ MAC_CIPHER_TKIP = 1,
+ /// 00-0F-AC 4
+ MAC_CIPHER_CCMP = 2,
+ /// 00-0F-AC 5
+ MAC_CIPHER_WEP104 = 3,
+ /// 00-14-72 1
+ MAC_CIPHER_WPI_SMS4 = 4,
+ /// 00-0F-AC 6 (aka AES_CMAC)
+ MAC_CIPHER_BIP_CMAC_128 = 5,
+
+ // following cipher are not supported by MACHW
+ /// 00-0F-AC 08
+ MAC_CIPHER_GCMP_128,
+ /// 00-0F-AC 09
+ MAC_CIPHER_GCMP_256,
+ /// 00-0F-AC 10
+ MAC_CIPHER_CCMP_256,
+ /// 00-0F-AC 11
+ MAC_CIPHER_BIP_GMAC_128,
+ /// 00-0F-AC 12
+ MAC_CIPHER_BIP_GMAC_256,
+ /// 00-0F-AC 13
+ MAC_CIPHER_BIP_CMAC_256,
+
+ MAC_CIPHER_INVALID = 0xFF
+};
+
+/// Authentication and Key Management suite
+enum mac_akm_suite
+{
+ /// No security
+ MAC_AKM_NONE,
+ /// Pre RSN (WEP or WPA)
+ MAC_AKM_PRE_RSN,
+ /// 00-0F-AC 1
+ MAC_AKM_8021X,
+ /// 00-0F-AC 2
+ MAC_AKM_PSK,
+ /// 00-0F-AC 3
+ MAC_AKM_FT_8021X,
+ /// 00-0F-AC 4
+ MAC_AKM_FT_PSK,
+ /// 00-0F-AC 5
+ MAC_AKM_8021X_SHA256,
+ /// 00-0F-AC 6
+ MAC_AKM_PSK_SHA256,
+ /// 00-0F-AC 7
+ MAC_AKM_TDLS,
+ /// 00-0F-AC 8
+ MAC_AKM_SAE,
+ /// 00-0F-AC 9
+ MAC_AKM_FT_OVER_SAE,
+ /// 00-0F-AC 11
+ MAC_AKM_8021X_SUITE_B,
+ /// 00-0F-AC 12
+ MAC_AKM_8021X_SUITE_B_192,
+ /// 00-0F-AC 14
+ MAC_AKM_FILS_SHA256,
+ /// 00-0F-AC 15
+ MAC_AKM_FILS_SHA384,
+ /// 00-0F-AC 16
+ MAC_AKM_FT_FILS_SHA256,
+ /// 00-0F-AC 17
+ MAC_AKM_FT_FILS_SHA384,
+ /// 00-0F-AC 18
+ MAC_AKM_OWE,
+
+ /// 00-14-72 1
+ MAC_AKM_WAPI_CERT,
+ /// 00-14-72 2
+ MAC_AKM_WAPI_PSK,
+};
+
+/// Scan result element, parsed from beacon or probe response frames.
+struct mac_scan_result
+{
+ /// Scan result is valid
+ bool valid_flag;
+ /// Network BSSID.
+ struct mac_addr bssid;
+ /// Network name.
+ struct mac_ssid ssid;
+ /// Network type (@ref mac_bss_type).
+ u16_l bsstype;
+ /// Network channel.
+ struct mac_chan_def *chan;
+ /// Network beacon period (in TU).
+ u16_l beacon_period;
+ /// Capability information
+ u16_l cap_info;
+ /// Supported AKM (bit-field of @ref mac_akm_suite)
+ u32_l akm;
+ /// Group cipher (bit-field of @ref mac_cipher_suite)
+ u16_l group_cipher;
+ /// Group cipher (bit-field of @ref mac_cipher_suite)
+ u16_l pairwise_cipher;
+ /// RSSI of the scanned BSS (in dBm)
+ s8_l rssi;
+ /// Multi-BSSID index (0 if this is the reference (i.e. transmitted) BSSID)
+ u8_l multi_bssid_index;
+ /// Maximum BSSID indicator
+ u8_l max_bssid_indicator;
+};
+
+/// Legacy rate 802.11 definitions
+enum mac_legacy_rates
+{
+ /// DSSS/CCK 1Mbps
+ MAC_RATE_1MBPS = 2,
+ /// DSSS/CCK 2Mbps
+ MAC_RATE_2MBPS = 4,
+ /// DSSS/CCK 5.5Mbps
+ MAC_RATE_5_5MBPS = 11,
+ /// OFDM 6Mbps
+ MAC_RATE_6MBPS = 12,
+ /// OFDM 9Mbps
+ MAC_RATE_9MBPS = 18,
+ /// DSSS/CCK 11Mbps
+ MAC_RATE_11MBPS = 22,
+ /// OFDM 12Mbps
+ MAC_RATE_12MBPS = 24,
+ /// OFDM 18Mbps
+ MAC_RATE_18MBPS = 36,
+ /// OFDM 24Mbps
+ MAC_RATE_24MBPS = 48,
+ /// OFDM 36Mbps
+ MAC_RATE_36MBPS = 72,
+ /// OFDM 48Mbps
+ MAC_RATE_48MBPS = 96,
+ /// OFDM 54Mbps
+ MAC_RATE_54MBPS = 108
+};
+
+/// BSS Membership Selector definitions
+enum mac_bss_membership
+{
+ /// HT PHY
+ MAC_BSS_MEMBERSHIP_HT_PHY = 127,
+ /// VHT PHY
+ MAC_BSS_MEMBERSHIP_VHT_PHY = 126,
+};
+
+/// MAC rateset maximum length
+#define MAC_RATESET_LEN 12
+
+/// Structure containing the legacy rateset of a station
+struct mac_rateset
+{
+ /// Number of legacy rates supported
+ u8_l length;
+ /// Array of legacy rates
+ u8_l array[MAC_RATESET_LEN];
+};
+
+/// MAC Security Key maximum length
+#define MAC_SEC_KEY_LEN 32 // TKIP keys 256 bits (max length) with MIC keys
+
+/// Structure defining a security key
+struct mac_sec_key
+{
+ /// Key material length
+ u8_l length;
+ /// Key material
+ u32_l array[MAC_SEC_KEY_LEN/4];
+};
+
+/// Access Category enumeration
+enum mac_ac
+{
+ /// Background
+ AC_BK = 0,
+ /// Best-effort
+ AC_BE,
+ /// Video
+ AC_VI,
+ /// Voice
+ AC_VO,
+ /// Number of access categories
+ AC_MAX
+};
+
+/// Traffic ID enumeration
+enum mac_tid
+{
+ /// TID_0. Mapped to @ref AC_BE as per 802.11 standard.
+ TID_0,
+ /// TID_1. Mapped to @ref AC_BK as per 802.11 standard.
+ TID_1,
+ /// TID_2. Mapped to @ref AC_BK as per 802.11 standard.
+ TID_2,
+ /// TID_3. Mapped to @ref AC_BE as per 802.11 standard.
+ TID_3,
+ /// TID_4. Mapped to @ref AC_VI as per 802.11 standard.
+ TID_4,
+ /// TID_5. Mapped to @ref AC_VI as per 802.11 standard.
+ TID_5,
+ /// TID_6. Mapped to @ref AC_VO as per 802.11 standard.
+ TID_6,
+ /// TID_7. Mapped to @ref AC_VO as per 802.11 standard.
+ TID_7,
+ /// Non standard Management TID used internally
+ TID_MGT,
+ /// Number of TID supported
+ TID_MAX
+};
+
+/// MCS bitfield maximum size (in bytes)
+#define MAX_MCS_LEN 16 // 16 * 8 = 128
+
+/// MAC HT capability information element
+struct mac_htcapability
+{
+ /// HT capability information
+ u16_l ht_capa_info;
+ /// A-MPDU parameters
+ u8_l a_mpdu_param;
+ /// Supported MCS
+ u8_l mcs_rate[MAX_MCS_LEN];
+ /// HT extended capability information
+ u16_l ht_extended_capa;
+ /// Beamforming capability information
+ u32_l tx_beamforming_capa;
+ /// Antenna selection capability information
+ u8_l asel_capa;
+};
+
+/// MAC VHT capability information element
+struct mac_vhtcapability
+{
+ /// VHT capability information
+ u32_l vht_capa_info;
+ /// RX MCS map
+ u16_l rx_mcs_map;
+ /// RX highest data rate
+ u16_l rx_highest;
+ /// TX MCS map
+ u16_l tx_mcs_map;
+ /// TX highest data rate
+ u16_l tx_highest;
+};
+
+/// Length (in bytes) of the MAC HE capability field
+#define MAC_HE_MAC_CAPA_LEN 6
+/// Length (in bytes) of the PHY HE capability field
+#define MAC_HE_PHY_CAPA_LEN 11
+/// Maximum length (in bytes) of the PPE threshold data
+#define MAC_HE_PPE_THRES_MAX_LEN 25
+
+/// Structure listing the per-NSS, per-BW supported MCS combinations
+struct mac_he_mcs_nss_supp
+{
+ /// per-NSS supported MCS in RX, for BW <= 80MHz
+ u16_l rx_mcs_80;
+ /// per-NSS supported MCS in TX, for BW <= 80MHz
+ u16_l tx_mcs_80;
+ /// per-NSS supported MCS in RX, for BW = 160MHz
+ u16_l rx_mcs_160;
+ /// per-NSS supported MCS in TX, for BW = 160MHz
+ u16_l tx_mcs_160;
+ /// per-NSS supported MCS in RX, for BW = 80+80MHz
+ u16_l rx_mcs_80p80;
+ /// per-NSS supported MCS in TX, for BW = 80+80MHz
+ u16_l tx_mcs_80p80;
+};
+
+/// MAC HE capability information element
+struct mac_hecapability
+{
+ /// MAC HE capabilities
+ u8_l mac_cap_info[MAC_HE_MAC_CAPA_LEN];
+ /// PHY HE capabilities
+ u8_l phy_cap_info[MAC_HE_PHY_CAPA_LEN];
+ /// Supported MCS combinations
+ struct mac_he_mcs_nss_supp mcs_supp;
+ /// PPE Thresholds data
+ u8_l ppe_thres[MAC_HE_PPE_THRES_MAX_LEN];
+};
+
+/// Station flags
+enum mac_sta_flags
+{
+ /// Bit indicating that a STA has QoS (WMM) capability
+ STA_QOS_CAPA = BIT(0),
+ /// Bit indicating that a STA has HT capability
+ STA_HT_CAPA = BIT(1),
+ /// Bit indicating that a STA has VHT capability
+ STA_VHT_CAPA = BIT(2),
+ /// Bit indicating that a STA has MFP capability
+ STA_MFP_CAPA = BIT(3),
+ /// Bit indicating that the STA included the Operation Notification IE
+ STA_OPMOD_NOTIF = BIT(4),
+ /// Bit indicating that a STA has HE capability
+ STA_HE_CAPA = BIT(5),
+ /// Bit Inidcating supprot for short Preamble in ERP
+ STA_SHORT_PREAMBLE_CAPA = BIT(6),
+};
+
+/// Connection flags
+enum mac_connection_flags
+{
+ /// Flag indicating whether the control port is controlled by host or not
+ CONTROL_PORT_HOST = BIT(0),
+ /// Flag indicating whether the control port frame shall be sent unencrypted
+ CONTROL_PORT_NO_ENC = BIT(1),
+ /// Flag indicating whether HT and VHT shall be disabled or not
+ DISABLE_HT = BIT(2),
+ /// Flag indicating whether WPA or WPA2 authentication is in use
+ WPA_WPA2_IN_USE = BIT(3),
+ /// Flag indicating whether MFP is in use
+ MFP_IN_USE = BIT(4),
+ REASSOCIATION = BIT(5),
+ FT_OVER_DS = BIT(6),
+};
+
+#endif // LMAC_MAC_H_
diff --git a/drivers/net/wireless/eswin/lmac_msg.h b/drivers/net/wireless/eswin/lmac_msg.h
new file mode 100644
index 000000000000..1e1fe7fca757
--- /dev/null
+++ b/drivers/net/wireless/eswin/lmac_msg.h
@@ -0,0 +1,2767 @@
+/**
+ ****************************************************************************************
+ *
+ * @file lmac_msg.h
+ *
+ * @brief Main definitions for message exchanges with LMAC
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ****************************************************************************************
+ */
+
+#ifndef LMAC_MSG_H_
+#define LMAC_MSG_H_
+
+/*
+ * INCLUDE FILES
+ ****************************************************************************************
+ */
+// for MAC related elements (mac_addr, mac_ssid...)
+#include "lmac_mac.h"
+
+
+#if defined(CONFIG_STANDALONE_WIFI) && defined(CONFIG_STANDALONE_WIFI_BLE)
+#error biuld mode error, wifi or wifi_ble must choice one!
+#endif
+
+/*
+ ****************************************************************************************
+ */
+/////////////////////////////////////////////////////////////////////////////////
+// COMMUNICATION WITH LMAC LAYER
+/////////////////////////////////////////////////////////////////////////////////
+/* Task identifiers for communication between LMAC and DRIVER */
+enum
+{
+ TASK_NONE = (u8_l) -1,
+#if defined(CONFIG_STANDALONE_WIFI)
+ // MAC Management task.
+ TASK_MM = 0,
+#elif defined(CONFIG_STANDALONE_WIFI_BLE)
+ TASK_MM = 32,
+#else
+#error #error not defined biuld mode!
+#endif
+ // DEBUG task
+ TASK_DBG,
+ /// SCAN task
+ TASK_SCAN,
+#ifdef CONFIG_CEVA_RTOS
+ TASK_CLI,
+#endif
+ /// TDLS task
+ TASK_TDLS,
+ /// SCANU task
+ TASK_SCANU,
+ /// ME task
+ TASK_ME,
+ /// SM task
+ TASK_SM,
+ /// APM task
+ TASK_APM,
+ /// BAM task
+ TASK_BAM,
+ /// MESH task
+ TASK_MESH,
+ /// RXU task
+ TASK_RXU,
+ TASK_RM,
+#if defined(CONFIG_ECRNX_P2P)
+ TASK_P2P_LISTEN,
+#endif
+ TASK_TWT,
+#if defined CONFIG_ECRNX_SOFTMAC
+ // This is used to define the last task that is running on the EMB processor
+ TASK_LAST_EMB = TASK_TDLS,
+#elif defined CONFIG_ECRNX_FULLMAC || defined CONFIG_ECRNX_FHOST
+ // This is used to define the last task that is running on the EMB processor
+ TASK_LAST_EMB = TASK_TWT,
+#else
+#error "Need to define SOFTMAC or FULLMAC"
+#endif
+ // nX API task
+ TASK_API,
+ TASK_MAX,
+};
+
+
+/// For MAC HW States copied from "hal_machw.h"
+enum
+{
+ /// MAC HW IDLE State.
+ HW_IDLE = 0,
+ /// MAC HW RESERVED State.
+ HW_RESERVED,
+ /// MAC HW DOZE State.
+ HW_DOZE,
+ /// MAC HW ACTIVE State.
+ HW_ACTIVE
+};
+
+/// Power Save mode setting
+enum mm_ps_mode_state
+{
+ MM_PS_MODE_OFF,
+ MM_PS_MODE_ON,
+ MM_PS_MODE_ON_DYN,
+};
+
+/// Status/error codes used in the MAC software.
+enum
+{
+ CO_OK,
+ CO_FAIL,
+ CO_EMPTY,
+ CO_FULL,
+ CO_BAD_PARAM,
+ CO_NOT_FOUND,
+ CO_NO_MORE_ELT_AVAILABLE,
+ CO_NO_ELT_IN_USE,
+ CO_BUSY,
+ CO_OP_IN_PROGRESS,
+};
+
+/// Remain on channel operation codes
+enum mm_remain_on_channel_op
+{
+ MM_ROC_OP_START = 0,
+ MM_ROC_OP_CANCEL,
+};
+
+#define DRV_TASK_ID 100
+
+/// Message Identifier. The number of messages is limited to 0xFFFF.
+/// The message ID is divided in two parts:
+/// - bits[15..10] : task index (no more than 64 tasks supported).
+/// - bits[9..0] : message index (no more that 1024 messages per task).
+typedef u16 lmac_msg_id_t;
+
+typedef u16 lmac_task_id_t;
+
+/// Build the first message ID of a task.
+#if defined(CONFIG_STANDALONE_WIFI)
+#define LMAC_FIRST_MSG(task) ((lmac_msg_id_t)((task) << 10))
+
+#define MSG_T(msg) ((lmac_task_id_t)((msg) >> 10))
+#define MSG_I(msg) ((msg) & ((1<<10)-1))
+
+#elif defined(CONFIG_STANDALONE_WIFI_BLE)
+#define LMAC_FIRST_MSG(task) ((lmac_msg_id_t)((task) << 8))
+
+#define MSG_T(msg) ((lmac_task_id_t)((msg) >> 8))
+#define MSG_I(msg) ((msg) & ((1<<8)-1))
+#else
+#error not defined biuld mode!
+#endif
+
+/// Message structure.
+struct lmac_msg
+{
+ lmac_msg_id_t id; ///< Message id.
+ lmac_task_id_t dest_id; ///< Destination kernel identifier.
+ lmac_task_id_t src_id; ///< Source kernel identifier.
+#ifdef CONFIG_ECRNX_ESWIN
+ uint8_t hostid[8];
+#endif
+
+ u16 param_len; ///< Parameter embedded struct length.
+ u32 param[]; ///< Parameter embedded struct. Must be word-aligned.
+};
+
+/// List of messages related to the task.
+enum mm_msg_tag
+{
+ /// RESET Request.
+ MM_RESET_REQ = LMAC_FIRST_MSG(TASK_MM),
+ /// RESET Confirmation.
+ MM_RESET_CFM,
+ /// START Request.
+ MM_START_REQ,
+ /// START Confirmation.
+ MM_START_CFM,
+ /// Read Version Request.
+ MM_VERSION_REQ,
+ /// Read Version Confirmation.
+ MM_VERSION_CFM,
+ /// ADD INTERFACE Request.
+ MM_ADD_IF_REQ,
+ /// ADD INTERFACE Confirmation.
+ MM_ADD_IF_CFM,
+ /// REMOVE INTERFACE Request.
+ MM_REMOVE_IF_REQ,
+ /// REMOVE INTERFACE Confirmation.
+ MM_REMOVE_IF_CFM,
+ /// STA ADD Request.
+ MM_STA_ADD_REQ,
+ /// STA ADD Confirm.
+ MM_STA_ADD_CFM,
+ /// STA DEL Request.
+ MM_STA_DEL_REQ,
+ /// STA DEL Confirm.
+ MM_STA_DEL_CFM,
+ /// RX FILTER CONFIGURATION Request.
+ MM_SET_FILTER_REQ,
+ /// RX FILTER CONFIGURATION Confirmation.
+ MM_SET_FILTER_CFM,
+ /// CHANNEL CONFIGURATION Request.
+ MM_SET_CHANNEL_REQ,
+ /// CHANNEL CONFIGURATION Confirmation.
+ MM_SET_CHANNEL_CFM,
+ /// DTIM PERIOD CONFIGURATION Request.
+ MM_SET_DTIM_REQ,
+ /// DTIM PERIOD CONFIGURATION Confirmation.
+ MM_SET_DTIM_CFM,
+ /// BEACON INTERVAL CONFIGURATION Request.
+ MM_SET_BEACON_INT_REQ,
+ /// BEACON INTERVAL CONFIGURATION Confirmation.
+ MM_SET_BEACON_INT_CFM,
+ /// BASIC RATES CONFIGURATION Request.
+ MM_SET_BASIC_RATES_REQ,
+ /// BASIC RATES CONFIGURATION Confirmation.
+ MM_SET_BASIC_RATES_CFM,
+ /// BSSID CONFIGURATION Request.
+ MM_SET_BSSID_REQ,
+ /// BSSID CONFIGURATION Confirmation.
+ MM_SET_BSSID_CFM,
+ /// EDCA PARAMETERS CONFIGURATION Request.
+ MM_SET_EDCA_REQ,
+ /// EDCA PARAMETERS CONFIGURATION Confirmation.
+ MM_SET_EDCA_CFM,
+ /// ABGN MODE CONFIGURATION Request.
+ MM_SET_MODE_REQ,
+ /// ABGN MODE CONFIGURATION Confirmation.
+ MM_SET_MODE_CFM,
+ /// Request setting the VIF active state (i.e associated or AP started)
+ MM_SET_VIF_STATE_REQ,
+ /// Confirmation of the @ref MM_SET_VIF_STATE_REQ message.
+ MM_SET_VIF_STATE_CFM,
+ /// SLOT TIME PARAMETERS CONFIGURATION Request.
+ MM_SET_SLOTTIME_REQ,
+ /// SLOT TIME PARAMETERS CONFIGURATION Confirmation.
+ MM_SET_SLOTTIME_CFM,
+ /// Power Mode Change Request.
+ MM_SET_IDLE_REQ,
+ /// Power Mode Change Confirm.
+ MM_SET_IDLE_CFM,
+ /// KEY ADD Request.
+ MM_KEY_ADD_REQ,
+ /// KEY ADD Confirm.
+ MM_KEY_ADD_CFM,
+ /// KEY DEL Request.
+ MM_KEY_DEL_REQ,
+ /// KEY DEL Confirm.
+ MM_KEY_DEL_CFM,
+ /// Block Ack agreement info addition
+ MM_BA_ADD_REQ,
+ /// Block Ack agreement info addition confirmation
+ MM_BA_ADD_CFM,
+ /// Block Ack agreement info deletion
+ MM_BA_DEL_REQ,
+ /// Block Ack agreement info deletion confirmation
+ MM_BA_DEL_CFM,
+ /// Indication of the primary TBTT to the upper MAC. Upon the reception of this
+ // message the upper MAC has to push the beacon(s) to the beacon transmission queue.
+ MM_PRIMARY_TBTT_IND,
+ /// Indication of the secondary TBTT to the upper MAC. Upon the reception of this
+ // message the upper MAC has to push the beacon(s) to the beacon transmission queue.
+ MM_SECONDARY_TBTT_IND,
+ /// Request for changing the TX power
+ MM_SET_POWER_REQ,
+ /// Confirmation of the TX power change
+ MM_SET_POWER_CFM,
+ /// Request to the LMAC to trigger the embedded logic analyzer and forward the debug
+ /// dump.
+ MM_DBG_TRIGGER_REQ,
+ /// Set Power Save mode
+ MM_SET_PS_MODE_REQ,
+ /// Set Power Save mode confirmation
+ MM_SET_PS_MODE_CFM,
+ /// Request to add a channel context
+ MM_CHAN_CTXT_ADD_REQ,
+ /// Confirmation of the channel context addition
+ MM_CHAN_CTXT_ADD_CFM,
+ /// Request to delete a channel context
+ MM_CHAN_CTXT_DEL_REQ,
+ /// Confirmation of the channel context deletion
+ MM_CHAN_CTXT_DEL_CFM,
+ /// Request to link a channel context to a VIF
+ MM_CHAN_CTXT_LINK_REQ,
+ /// Confirmation of the channel context link
+ MM_CHAN_CTXT_LINK_CFM,
+ /// Request to unlink a channel context from a VIF
+ MM_CHAN_CTXT_UNLINK_REQ,
+ /// Confirmation of the channel context unlink
+ MM_CHAN_CTXT_UNLINK_CFM,
+ /// Request to update a channel context
+ MM_CHAN_CTXT_UPDATE_REQ,
+ /// Confirmation of the channel context update
+ MM_CHAN_CTXT_UPDATE_CFM,
+ /// Request to schedule a channel context
+ MM_CHAN_CTXT_SCHED_REQ,
+ /// Confirmation of the channel context scheduling
+ MM_CHAN_CTXT_SCHED_CFM,
+ /// Request to change the beacon template in LMAC
+ MM_BCN_CHANGE_REQ,
+ /// Confirmation of the beacon change
+ MM_BCN_CHANGE_CFM,
+ /// Request to update the TIM in the beacon (i.e to indicate traffic bufferized at AP)
+ MM_TIM_UPDATE_REQ,
+ /// Confirmation of the TIM update
+ MM_TIM_UPDATE_CFM,
+ /// Connection loss indication
+ MM_CONNECTION_LOSS_IND,
+ /// Channel context switch indication to the upper layers
+ MM_CHANNEL_SWITCH_IND,
+ /// Channel context pre-switch indication to the upper layers
+ MM_CHANNEL_PRE_SWITCH_IND,
+ /// Request to remain on channel or cancel remain on channel
+ MM_REMAIN_ON_CHANNEL_REQ,
+ /// Confirmation of the (cancel) remain on channel request
+ MM_REMAIN_ON_CHANNEL_CFM,
+ /// Remain on channel expired indication
+ MM_REMAIN_ON_CHANNEL_EXP_IND,
+ /// Indication of a PS state change of a peer device
+ MM_PS_CHANGE_IND,
+ /// Indication that some buffered traffic should be sent to the peer device
+ MM_TRAFFIC_REQ_IND,
+ /// Request to modify the STA Power-save mode options
+ MM_SET_PS_OPTIONS_REQ,
+ /// Confirmation of the PS options setting
+ MM_SET_PS_OPTIONS_CFM,
+ /// Indication of PS state change for a P2P VIF
+ MM_P2P_VIF_PS_CHANGE_IND,
+ /// Indication that CSA counter has been updated
+ MM_CSA_COUNTER_IND,
+ /// Channel occupation report indication
+ MM_CHANNEL_SURVEY_IND,
+ /// Message containing Beamformer Information
+ MM_BFMER_ENABLE_REQ,
+ /// Request to Start/Stop/Update NOA - GO Only
+ MM_SET_P2P_NOA_REQ,
+ /// Request to Start/Stop/Update Opportunistic PS - GO Only
+ MM_SET_P2P_OPPPS_REQ,
+ /// Start/Stop/Update NOA Confirmation
+ MM_SET_P2P_NOA_CFM,
+ /// Start/Stop/Update Opportunistic PS Confirmation
+ MM_SET_P2P_OPPPS_CFM,
+ /// P2P NoA Update Indication - GO Only
+ MM_P2P_NOA_UPD_IND,
+ /// Request to set RSSI threshold and RSSI hysteresis
+ MM_CFG_RSSI_REQ,
+ /// Indication that RSSI level is below or above the threshold
+ MM_RSSI_STATUS_IND,
+ /// Indication that CSA is done
+ MM_CSA_FINISH_IND,
+ /// Indication that CSA is in prorgess (resp. done) and traffic must be stopped (resp. restarted)
+ MM_CSA_TRAFFIC_IND,
+ /// Request to update the group information of a station
+ MM_MU_GROUP_UPDATE_REQ,
+ /// Confirmation of the @ref MM_MU_GROUP_UPDATE_REQ message
+ MM_MU_GROUP_UPDATE_CFM,
+ /// Request to initialize the antenna diversity algorithm
+ MM_ANT_DIV_INIT_REQ,
+ /// Request to stop the antenna diversity algorithm
+ MM_ANT_DIV_STOP_REQ,
+ /// Request to update the antenna switch status
+ MM_ANT_DIV_UPDATE_REQ,
+ /// Request to switch the antenna connected to path_0
+ MM_SWITCH_ANTENNA_REQ,
+ /// Indication that a packet loss has occurred
+ MM_PKTLOSS_IND,
+ /// MU EDCA PARAMETERS Configuration Request.
+ MM_SET_MU_EDCA_REQ,
+ /// MU EDCA PARAMETERS Configuration Confirmation.
+ MM_SET_MU_EDCA_CFM,
+ /// UORA PARAMETERS Configuration Request.
+ MM_SET_UORA_REQ,
+ /// UORA PARAMETERS Configuration Confirmation.
+ MM_SET_UORA_CFM,
+ /// TXOP RTS THRESHOLD Configuration Request.
+ MM_SET_TXOP_RTS_THRES_REQ,
+ /// TXOP RTS THRESHOLD Configuration Confirmation.
+ MM_SET_TXOP_RTS_THRES_CFM,
+ /// HE BSS Color Configuration Request.
+ MM_SET_BSS_COLOR_REQ,
+ /// HE BSS Color Configuration Confirmation.
+ MM_SET_BSS_COLOR_CFM,
+ /// Calibration Gain Delta Configuration Request.
+ MM_SET_GAIN_DELTA_REQ,
+ /// Calibration Gain Delta Configuration Confirmation.
+ MM_SET_GAIN_DELTA_CFM,
+ /// Calibration result get Request.
+ MM_GET_CAL_RESULT_REQ,
+ /// Calibration result get Confirmation.
+ MM_GET_CAL_RESULT_CFM,
+
+ /*
+ * Section of internal MM messages. No MM API messages should be defined below this point
+ */
+ /// MAX number of messages
+ MM_MAX,
+};
+
+/// Interface types
+enum
+{
+ /// ESS STA interface
+ MM_STA,
+ /// IBSS STA interface
+ MM_IBSS,
+ /// AP interface
+ MM_AP,
+ // Mesh Point interface
+ MM_MESH_POINT,
+ // Monitor interface
+ MM_MONITOR,
+};
+
+///BA agreement types
+enum
+{
+ ///BlockAck agreement for TX
+ BA_AGMT_TX,
+ ///BlockAck agreement for RX
+ BA_AGMT_RX,
+};
+
+///BA agreement related status
+enum
+{
+ ///Correct BA agreement establishment
+ BA_AGMT_ESTABLISHED,
+ ///BA agreement already exists for STA+TID requested, cannot override it (should have been deleted first)
+ BA_AGMT_ALREADY_EXISTS,
+ ///Correct BA agreement deletion
+ BA_AGMT_DELETED,
+ ///BA agreement for the (STA, TID) doesn't exist so nothing to delete
+ BA_AGMT_DOESNT_EXIST,
+};
+
+/// Features supported by LMAC - Positions
+enum mm_features
+{
+ /// Beaconing
+ MM_FEAT_BCN_BIT = 0,
+ /// Autonomous Beacon Transmission
+ MM_FEAT_AUTOBCN_BIT,
+ /// Scan in LMAC
+ MM_FEAT_HWSCAN_BIT,
+ /// Connection Monitoring
+ MM_FEAT_CMON_BIT,
+ /// Multi Role
+ MM_FEAT_MROLE_BIT,
+ /// Radar Detection
+ MM_FEAT_RADAR_BIT,
+ /// Power Save
+ MM_FEAT_PS_BIT,
+ /// UAPSD
+ MM_FEAT_UAPSD_BIT,
+ /// DPSM
+ MM_FEAT_DPSM_BIT,
+ /// A-MPDU
+ MM_FEAT_AMPDU_BIT,
+ /// A-MSDU
+ MM_FEAT_AMSDU_BIT,
+ /// Channel Context
+ MM_FEAT_CHNL_CTXT_BIT,
+ /// Packet reordering
+ MM_FEAT_REORD_BIT,
+ /// P2P
+ MM_FEAT_P2P_BIT,
+ /// P2P Go
+ MM_FEAT_P2P_GO_BIT,
+ /// UMAC Present
+ MM_FEAT_UMAC_BIT,
+ /// VHT support
+ MM_FEAT_VHT_BIT,
+ /// Beamformee
+ MM_FEAT_BFMEE_BIT,
+ /// Beamformer
+ MM_FEAT_BFMER_BIT,
+ /// WAPI
+ MM_FEAT_WAPI_BIT,
+ /// MFP
+ MM_FEAT_MFP_BIT,
+ /// Mu-MIMO RX support
+ MM_FEAT_MU_MIMO_RX_BIT,
+ /// Mu-MIMO TX support
+ MM_FEAT_MU_MIMO_TX_BIT,
+ /// Wireless Mesh Networking
+ MM_FEAT_MESH_BIT,
+ /// TDLS support
+ MM_FEAT_TDLS_BIT,
+ /// Antenna Diversity support
+ MM_FEAT_ANT_DIV_BIT,
+ /// UF support
+ MM_FEAT_UF_BIT,
+ /// A-MSDU maximum size (bit0)
+ MM_AMSDU_MAX_SIZE_BIT0,
+ /// A-MSDU maximum size (bit1)
+ MM_AMSDU_MAX_SIZE_BIT1,
+ /// MON_DATA support
+ MM_FEAT_MON_DATA_BIT,
+ /// HE (802.11ax) support
+ MM_FEAT_HE_BIT,
+ MM_FEAT_TWT_BIT,
+};
+
+/// Maximum number of words in the configuration buffer
+#define PHY_CFG_BUF_SIZE 16
+
+/// Structure containing the parameters of the PHY configuration
+struct phy_cfg_tag
+{
+ /// Buffer containing the parameters specific for the PHY used
+ u32_l parameters[PHY_CFG_BUF_SIZE];
+};
+
+/// Structure containing the parameters of the Trident PHY configuration
+struct phy_trd_cfg_tag
+{
+ /// MDM type(nxm)(upper nibble) and MDM2RF path mapping(lower nibble)
+ u8_l path_mapping;
+ /// TX DC offset compensation
+ u32_l tx_dc_off_comp;
+};
+
+/// Structure containing the parameters of the Karst PHY configuration
+struct phy_karst_cfg_tag
+{
+ /// TX IQ mismatch compensation in 2.4GHz
+ u32_l tx_iq_comp_2_4G[2];
+ /// RX IQ mismatch compensation in 2.4GHz
+ u32_l rx_iq_comp_2_4G[2];
+ /// TX IQ mismatch compensation in 5GHz
+ u32_l tx_iq_comp_5G[2];
+ /// RX IQ mismatch compensation in 5GHz
+ u32_l rx_iq_comp_5G[2];
+ /// RF path used by default (0 or 1)
+ u8_l path_used;
+};
+
+struct phy_cataxia_cfg_tag
+{
+ u8_l path_used;
+};
+/// Structure containing the parameters of the @ref MM_START_REQ message
+struct mm_start_req
+{
+ /// PHY configuration
+ struct phy_cfg_tag phy_cfg;
+ /// UAPSD timeout
+ u32_l uapsd_timeout;
+ /// Local LP clock accuracy (in ppm)
+ u16_l lp_clk_accuracy;
+ u16_l tx_timeout[AC_MAX];
+};
+
+/// Structure containing the parameters of the @ref MM_SET_CHANNEL_REQ message
+struct mm_set_channel_req
+{
+ /// Channel information
+ struct mac_chan_op chan;
+ /// Index of the RF for which the channel has to be set (0: operating (primary), 1: secondary
+ /// RF (used for additional radar detection). This parameter is reserved if no secondary RF
+ /// is available in the system
+ u8_l index;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_CHANNEL_CFM message
+struct mm_set_channel_cfm
+{
+ /// Radio index to be used in policy table
+ u8_l radio_idx;
+ /// TX power configured (in dBm)
+ s8_l power;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_DTIM_REQ message
+struct mm_set_dtim_req
+{
+ /// DTIM period
+ u8_l dtim_period;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_POWER_REQ message
+struct mm_set_power_req
+{
+ /// Index of the interface for which the parameter is configured
+ u8_l inst_nbr;
+ /// TX power (in dBm)
+ s8_l power;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_POWER_CFM message
+struct mm_set_power_cfm
+{
+ /// Radio index to be used in policy table
+ u8_l radio_idx;
+ /// TX power configured (in dBm)
+ s8_l power;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_BEACON_INT_REQ message
+struct mm_set_beacon_int_req
+{
+ /// Beacon interval
+ u16_l beacon_int;
+ /// Index of the interface for which the parameter is configured
+ u8_l inst_nbr;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_BASIC_RATES_REQ message
+struct mm_set_basic_rates_req
+{
+ /// Basic rate set (as expected by bssBasicRateSet field of Rates MAC HW register)
+ u32_l rates;
+ /// Index of the interface for which the parameter is configured
+ u8_l inst_nbr;
+ /// Band on which the interface will operate
+ u8_l band;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_BSSID_REQ message
+struct mm_set_bssid_req
+{
+ /// BSSID to be configured in HW
+ struct mac_addr bssid;
+ /// Index of the interface for which the parameter is configured
+ u8_l inst_nbr;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_FILTER_REQ message
+struct mm_set_filter_req
+{
+ /// RX filter to be put into rxCntrlReg HW register
+ u32_l filter;
+};
+
+/// Structure containing the parameters of the @ref MM_ADD_IF_REQ message.
+struct mm_add_if_req
+{
+ /// Type of the interface (AP, STA, ADHOC, ...)
+ u8_l type;
+ /// MAC ADDR of the interface to start
+ struct mac_addr addr;
+ /// P2P Interface
+ bool_l p2p;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_EDCA_REQ message
+struct mm_set_edca_req
+{
+ /// EDCA parameters of the queue (as expected by edcaACxReg HW register)
+ u32_l ac_param;
+ /// Flag indicating if UAPSD can be used on this queue
+ bool_l uapsd;
+ /// HW queue for which the parameters are configured
+ u8_l hw_queue;
+ /// Index of the interface for which the parameters are configured
+ u8_l inst_nbr;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_MU_EDCA_REQ message
+struct mm_set_mu_edca_req
+{
+ /// MU EDCA parameters of the different HE queues
+ u32_l param[AC_MAX];
+};
+
+/// Structure containing the parameters of the @ref MM_SET_UORA_REQ message
+struct mm_set_uora_req
+{
+ /// Minimum exponent of OFDMA Contention Window.
+ u8_l eocw_min;
+ /// Maximum exponent of OFDMA Contention Window.
+ u8_l eocw_max;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_TXOP_RTS_THRES_REQ message
+struct mm_set_txop_rts_thres_req
+{
+ /// TXOP RTS threshold
+ u16_l txop_dur_rts_thres;
+ /// Index of the interface for which the parameter is configured
+ u8_l inst_nbr;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_BSS_COLOR_REQ message
+struct mm_set_bss_color_req
+{
+ /// HE BSS color, formatted as per BSS_COLOR MAC HW register
+ u32_l bss_color;
+};
+
+struct mm_set_idle_req
+{
+ u8_l hw_idle;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_SLOTTIME_REQ message
+struct mm_set_slottime_req
+{
+ /// Slot time expressed in us
+ u8_l slottime;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_MODE_REQ message
+struct mm_set_mode_req
+{
+ /// abgnMode field of macCntrl1Reg register
+ u8_l abgnmode;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_VIF_STATE_REQ message
+struct mm_set_vif_state_req
+{
+ /// Association Id received from the AP (valid only if the VIF is of STA type)
+ u16_l aid;
+ /// Flag indicating if the VIF is active or not
+ bool_l active;
+ /// Interface index
+ u8_l inst_nbr;
+};
+
+/// Structure containing the parameters of the @ref MM_ADD_IF_CFM message.
+struct mm_add_if_cfm
+{
+ /// Status of operation (different from 0 if unsuccessful)
+ u8_l status;
+ /// Interface index assigned by the LMAC
+ u8_l inst_nbr;
+};
+
+/// Structure containing the parameters of the @ref MM_REMOVE_IF_REQ message.
+struct mm_remove_if_req
+{
+ /// Interface index assigned by the LMAC
+ u8_l inst_nbr;
+};
+
+/// Structure containing the parameters of the @ref MM_VERSION_CFM message.
+struct mm_version_cfm
+{
+ /// Version of the LMAC FW
+ u32_l version_lmac;
+ /// Version1 of the MAC HW (as encoded in version1Reg MAC HW register)
+ u32_l version_machw_1;
+ /// Version2 of the MAC HW (as encoded in version2Reg MAC HW register)
+ u32_l version_machw_2;
+ /// Version1 of the PHY (depends on actual PHY)
+ u32_l version_phy_1;
+ /// Version2 of the PHY (depends on actual PHY)
+ u32_l version_phy_2;
+ /// Supported Features
+ u32_l features;
+ /// Maximum number of supported stations
+ u16_l max_sta_nb;
+ /// Maximum number of supported virtual interfaces
+ u8_l max_vif_nb;
+};
+
+/// Structure containing the parameters of the @ref MM_STA_ADD_REQ message.
+struct mm_sta_add_req
+{
+ u32_l capa_flags;
+ /// Maximum A-MPDU size, in bytes, for HE frames
+ u32_l ampdu_size_max_he;
+ /// Maximum A-MPDU size, in bytes, for VHT frames
+ u32_l ampdu_size_max_vht;
+ /// PAID/GID
+ u32_l paid_gid;
+ /// Maximum A-MPDU size, in bytes, for HT frames
+ u16_l ampdu_size_max_ht;
+ /// MAC address of the station to be added
+ struct mac_addr mac_addr;
+ /// A-MPDU spacing, in us
+ u8_l ampdu_spacing_min;
+ /// Interface index
+ u8_l inst_nbr;
+ /// TDLS station
+ bool_l tdls_sta;
+ /// Indicate if the station is TDLS link initiator station
+ bool_l tdls_sta_initiator;
+ /// Indicate if the TDLS Channel Switch is allowed
+ bool_l tdls_chsw_allowed;
+ /// nonTransmitted BSSID index, set to the BSSID index in case the STA added is an AP
+ /// that is a nonTransmitted BSSID. Should be set to 0 otherwise
+ u8_l bssid_index;
+ /// Maximum BSSID indicator, valid if the STA added is an AP that is a nonTransmitted
+ /// BSSID
+ u8_l max_bssid_ind;
+};
+
+/// Structure containing the parameters of the @ref MM_STA_ADD_CFM message.
+struct mm_sta_add_cfm
+{
+ /// Status of the operation (different from 0 if unsuccessful)
+ u8_l status;
+ /// Index assigned by the LMAC to the newly added station
+ u8_l sta_idx;
+ /// MAC HW index of the newly added station
+ u8_l hw_sta_idx;
+};
+
+/// Structure containing the parameters of the @ref MM_STA_DEL_REQ message.
+struct mm_sta_del_req
+{
+ /// Index of the station to be deleted
+ u8_l sta_idx;
+};
+
+/// Structure containing the parameters of the @ref MM_STA_DEL_CFM message.
+struct mm_sta_del_cfm
+{
+ /// Status of the operation (different from 0 if unsuccessful)
+ u8_l status;
+};
+
+/// Structure containing the parameters of the SET_POWER_MODE REQ message.
+struct mm_setpowermode_req
+{
+ u8_l mode;
+ u8_l sta_idx;
+};
+
+/// Structure containing the parameters of the SET_POWER_MODE CFM message.
+struct mm_setpowermode_cfm
+{
+ u8_l status;
+};
+
+/// Structure containing the parameters of the @ref MM_KEY_ADD REQ message.
+struct mm_key_add_req
+{
+ /// Key index (valid only for default keys)
+ u8_l key_idx;
+ /// STA index (valid only for pairwise or mesh group keys)
+ u8_l sta_idx;
+ /// Key material
+ struct mac_sec_key key;
+ /// Cipher suite (WEP64, WEP128, TKIP, CCMP)
+ u8_l cipher_suite;
+ /// Index of the interface for which the key is set (valid only for default keys or mesh group keys)
+ u8_l inst_nbr;
+ /// A-MSDU SPP parameter
+ u8_l spp;
+ /// Indicate if provided key is a pairwise key or not
+ bool_l pairwise;
+};
+
+/// Structure containing the parameters of the @ref MM_KEY_ADD_CFM message.
+struct mm_key_add_cfm
+{
+ /// Status of the operation (different from 0 if unsuccessful)
+ u8_l status;
+ /// HW index of the key just added
+ u8_l hw_key_idx;
+};
+
+/// Structure containing the parameters of the @ref MM_KEY_DEL_REQ message.
+struct mm_key_del_req
+{
+ /// HW index of the key to be deleted
+ u8_l hw_key_idx;
+};
+
+/// Structure containing the parameters of the @ref MM_BA_ADD_REQ message.
+struct mm_ba_add_req
+{
+ ///Type of agreement (0: TX, 1: RX)
+ u8_l type;
+ ///Index of peer station with which the agreement is made
+ u8_l sta_idx;
+ ///TID for which the agreement is made with peer station
+ u8_l tid;
+ ///Buffer size - number of MPDUs that can be held in its buffer per TID
+ u8_l bufsz;
+ /// Start sequence number negotiated during BA setup - the one in first aggregated MPDU counts more
+ u16_l ssn;
+};
+
+/// Structure containing the parameters of the @ref MM_BA_ADD_CFM message.
+struct mm_ba_add_cfm
+{
+ ///Index of peer station for which the agreement is being confirmed
+ u8_l sta_idx;
+ ///TID for which the agreement is being confirmed
+ u8_l tid;
+ /// Status of ba establishment
+ u8_l status;
+};
+
+/// Structure containing the parameters of the @ref MM_BA_DEL_REQ message.
+struct mm_ba_del_req
+{
+ ///Type of agreement (0: TX, 1: RX)
+ u8_l type;
+ ///Index of peer station for which the agreement is being deleted
+ u8_l sta_idx;
+ ///TID for which the agreement is being deleted
+ u8_l tid;
+};
+
+/// Structure containing the parameters of the @ref MM_BA_DEL_CFM message.
+struct mm_ba_del_cfm
+{
+ ///Index of peer station for which the agreement deletion is being confirmed
+ u8_l sta_idx;
+ ///TID for which the agreement deletion is being confirmed
+ u8_l tid;
+ /// Status of ba deletion
+ u8_l status;
+};
+
+/// Structure containing the parameters of the @ref MM_CHAN_CTXT_ADD_REQ message
+struct mm_chan_ctxt_add_req
+{
+ /// Operating channel
+ struct mac_chan_op chan;
+};
+
+/// Structure containing the parameters of the @ref MM_CHAN_CTXT_ADD_REQ message
+struct mm_chan_ctxt_add_cfm
+{
+ /// Status of the addition
+ u8_l status;
+ /// Index of the new channel context
+ u8_l index;
+};
+
+
+/// Structure containing the parameters of the @ref MM_CHAN_CTXT_DEL_REQ message
+struct mm_chan_ctxt_del_req
+{
+ /// Index of the new channel context to be deleted
+ u8_l index;
+};
+
+
+/// Structure containing the parameters of the @ref MM_CHAN_CTXT_LINK_REQ message
+struct mm_chan_ctxt_link_req
+{
+ /// VIF index
+ u8_l vif_index;
+ /// Channel context index
+ u8_l chan_index;
+ /// Indicate if this is a channel switch (unlink current ctx first if true)
+ u8_l chan_switch;
+};
+
+/// Structure containing the parameters of the @ref MM_CHAN_CTXT_UNLINK_REQ message
+struct mm_chan_ctxt_unlink_req
+{
+ /// VIF index
+ u8_l vif_index;
+};
+
+/// Structure containing the parameters of the @ref MM_CHAN_CTXT_UPDATE_REQ message
+struct mm_chan_ctxt_update_req
+{
+ /// Channel context index
+ u8_l chan_index;
+ /// New channel information
+ struct mac_chan_op chan;
+};
+
+/// Structure containing the parameters of the @ref MM_CHAN_CTXT_SCHED_REQ message
+struct mm_chan_ctxt_sched_req
+{
+ /// VIF index
+ u8_l vif_index;
+ /// Channel context index
+ u8_l chan_index;
+ /// Type of the scheduling request (0: normal scheduling, 1: derogatory
+ /// scheduling)
+ u8_l type;
+};
+
+/// Structure containing the parameters of the @ref MM_CHANNEL_SWITCH_IND message
+struct mm_channel_switch_ind
+{
+ /// Index of the channel context we will switch to
+ u8_l chan_index;
+ /// Indicate if the switch has been triggered by a Remain on channel request
+ bool_l roc;
+ /// VIF on which remain on channel operation has been started (if roc == 1)
+ u8_l vif_index;
+ /// Indicate if the switch has been triggered by a TDLS Remain on channel request
+ bool_l roc_tdls;
+};
+
+/// Structure containing the parameters of the @ref MM_CHANNEL_PRE_SWITCH_IND message
+struct mm_channel_pre_switch_ind
+{
+ /// Index of the channel context we will switch to
+ u8_l chan_index;
+};
+
+/// Structure containing the parameters of the @ref MM_CONNECTION_LOSS_IND message.
+struct mm_connection_loss_ind
+{
+ /// VIF instance number
+ u8_l inst_nbr;
+};
+
+
+/// Structure containing the parameters of the @ref MM_DBG_TRIGGER_REQ message.
+struct mm_dbg_trigger_req
+{
+ /// Error trace to be reported by the LMAC
+ char error[64];
+};
+
+/// Structure containing the parameters of the @ref MM_SET_PS_MODE_REQ message.
+struct mm_set_ps_mode_req
+{
+ /// Power Save is activated or deactivated
+ u8_l new_state;
+};
+
+/// Structure containing the parameters of the @ref MM_BCN_CHANGE_REQ message.
+#define BCN_MAX_CSA_CPT 2
+struct mm_bcn_change_req
+{
+ /// Offset of the TIM IE in the beacon
+ u16_l tim_oft;
+ /// Length of the TIM IE
+ u8_l tim_len;
+ /// Index of the VIF for which the beacon is updated
+ u8_l inst_nbr;
+ /// Offset of CSA (channel switch announcement) counters (0 means no counter)
+ u8_l csa_oft[BCN_MAX_CSA_CPT];
+ /// Length of the beacon template
+ u16_l bcn_len;
+ /// Pointer, in host memory, to the new beacon template
+ ptr_addr bcn_ptr;
+};
+
+
+/// Structure containing the parameters of the @ref MM_TIM_UPDATE_REQ message.
+struct mm_tim_update_req
+{
+ /// Association ID of the STA the bit of which has to be updated (0 for BC/MC traffic)
+ u16_l aid;
+ /// Flag indicating the availability of data packets for the given STA
+ u8_l tx_avail;
+ /// Index of the VIF for which the TIM is updated
+ u8_l inst_nbr;
+};
+
+/// Structure containing the parameters of the @ref MM_REMAIN_ON_CHANNEL_REQ message.
+struct mm_remain_on_channel_req
+{
+ /// Operation Code
+ u8_l op_code;
+ /// VIF Index
+ u8_l vif_index;
+ /// Channel parameter
+ struct mac_chan_op chan;
+ /// Duration (in ms)
+ u32_l duration_ms;
+};
+
+/// Structure containing the parameters of the @ref MM_REMAIN_ON_CHANNEL_CFM message
+struct mm_remain_on_channel_cfm
+{
+ /// Operation Code
+ u8_l op_code;
+ /// Status of the operation
+ u8_l status;
+ /// Channel Context index
+ u8_l chan_ctxt_index;
+};
+
+/// Structure containing the parameters of the @ref MM_REMAIN_ON_CHANNEL_EXP_IND message
+struct mm_remain_on_channel_exp_ind
+{
+ /// VIF Index
+ u8_l vif_index;
+ /// Channel Context index
+ u8_l chan_ctxt_index;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_UAPSD_TMR_REQ message.
+struct mm_set_uapsd_tmr_req
+{
+ /// action: Start or Stop the timer
+ u8_l action;
+ /// timeout value, in milliseconds
+ u32_l timeout;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_UAPSD_TMR_CFM message.
+struct mm_set_uapsd_tmr_cfm
+{
+ /// Status of the operation (different from 0 if unsuccessful)
+ u8_l status;
+};
+
+
+/// Structure containing the parameters of the @ref MM_PS_CHANGE_IND message
+struct mm_ps_change_ind
+{
+ /// Index of the peer device that is switching its PS state
+ u8_l sta_idx;
+ /// New PS state of the peer device (0: active, 1: sleeping)
+ u8_l ps_state;
+};
+
+/// Structure containing the parameters of the @ref MM_P2P_VIF_PS_CHANGE_IND message
+struct mm_p2p_vif_ps_change_ind
+{
+ /// Index of the P2P VIF that is switching its PS state
+ u8_l vif_index;
+ /// New PS state of the P2P VIF interface (0: active, 1: sleeping)
+ u8_l ps_state;
+};
+
+/// Structure containing the parameters of the @ref MM_TRAFFIC_REQ_IND message
+struct mm_traffic_req_ind
+{
+ /// Index of the peer device that needs traffic
+ u8_l sta_idx;
+ /// Number of packets that need to be sent (if 0, all buffered traffic shall be sent and
+ /// if set to @ref PS_SP_INTERRUPTED, it means that current service period has been interrupted)
+ u8_l pkt_cnt;
+ /// Flag indicating if the traffic request concerns U-APSD queues or not
+ bool_l uapsd;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_PS_OPTIONS_REQ message.
+struct mm_set_ps_options_req
+{
+ /// VIF Index
+ u8_l vif_index;
+ /// Listen interval (0 if wake up shall be based on DTIM period)
+ u16_l listen_interval;
+ /// Flag indicating if we shall listen the BC/MC traffic or not
+ bool_l dont_listen_bc_mc;
+};
+
+/// Structure containing the parameters of the @ref MM_CSA_COUNTER_IND message
+struct mm_csa_counter_ind
+{
+ /// Index of the VIF
+ u8_l vif_index;
+ /// Updated CSA counter value
+ u8_l csa_count;
+};
+
+/// Structure containing the parameters of the @ref MM_CHANNEL_SURVEY_IND message
+struct mm_channel_survey_ind
+{
+ /// Frequency of the channel
+ u16_l freq;
+ /// Noise in dbm
+ s8_l noise_dbm;
+ /// Amount of time spent of the channel (in ms)
+ u32_l chan_time_ms;
+ /// Amount of time the primary channel was sensed busy
+ u32_l chan_time_busy_ms;
+};
+
+/// Structure containing the parameters of the @ref MM_BFMER_ENABLE_REQ message.
+struct mm_bfmer_enable_req
+{
+ /**
+ * Address of the beamforming report space allocated in host memory
+ * (Valid only if vht_su_bfmee is true)
+ */
+ u32_l host_bfr_addr;
+ /**
+ * Size of the beamforming report space allocated in host memory. This space should
+ * be twice the maximum size of the expected beamforming reports as the FW will
+ * divide it in two in order to be able to upload a new report while another one is
+ * used in transmission
+ */
+ u16_l host_bfr_size;
+ /// AID
+ u16_l aid;
+ /// Station Index
+ u8_l sta_idx;
+ /// Maximum number of spatial streams the station can receive
+ u8_l rx_nss;
+ /**
+ * Indicate if peer STA is MU Beamformee (VHT) capable
+ * (Valid only if vht_su_bfmee is true)
+ */
+ bool_l vht_mu_bfmee;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_P2P_NOA_REQ message.
+struct mm_set_p2p_noa_req
+{
+ /// VIF Index
+ u8_l vif_index;
+ /// Allocated NOA Instance Number - Valid only if count = 0
+ u8_l noa_inst_nb;
+ /// Count
+ u8_l count;
+ /// Indicate if NoA can be paused for traffic reason
+ bool_l dyn_noa;
+ /// Duration (in us)
+ u32_l duration_us;
+ /// Interval (in us)
+ u32_l interval_us;
+ /// Start Time offset from next TBTT (in us)
+ u32_l start_offset;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_P2P_OPPPS_REQ message.
+struct mm_set_p2p_oppps_req
+{
+ /// VIF Index
+ u8_l vif_index;
+ /// CTWindow
+ u8_l ctwindow;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_P2P_NOA_CFM message.
+struct mm_set_p2p_noa_cfm
+{
+ /// Request status
+ u8_l status;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_P2P_OPPPS_CFM message.
+struct mm_set_p2p_oppps_cfm
+{
+ /// Request status
+ u8_l status;
+};
+
+/// Structure containing the parameters of the @ref MM_P2P_NOA_UPD_IND message.
+struct mm_p2p_noa_upd_ind
+{
+ /// VIF Index
+ u8_l vif_index;
+ /// NOA Instance Number
+ u8_l noa_inst_nb;
+ /// NoA Type
+ u8_l noa_type;
+ /// Count
+ u8_l count;
+ /// Duration (in us)
+ u32_l duration_us;
+ /// Interval (in us)
+ u32_l interval_us;
+ /// Start Time
+ u32_l start_time;
+};
+
+/// Structure containing the parameters of the @ref MM_CFG_RSSI_REQ message
+struct mm_cfg_rssi_req
+{
+ /// Index of the VIF
+ u8_l vif_index;
+ /// RSSI threshold
+ s8_l rssi_thold;
+ /// RSSI hysteresis
+ u8_l rssi_hyst;
+};
+
+/// Structure containing the parameters of the @ref MM_RSSI_STATUS_IND message
+struct mm_rssi_status_ind
+{
+ /// Index of the VIF
+ u8_l vif_index;
+ /// Status of the RSSI
+ bool_l rssi_status;
+ /// Current RSSI
+ s8_l rssi;
+};
+
+/// Structure containing the parameters of the @ref MM_PKTLOSS_IND message
+struct mm_pktloss_ind
+{
+ /// Index of the VIF
+ u8_l vif_index;
+ /// Address of the STA for which there is a packet loss
+ struct mac_addr mac_addr;
+ /// Number of packets lost
+ u32 num_packets;
+};
+
+/// Structure containing the parameters of the @ref MM_CSA_FINISH_IND message
+struct mm_csa_finish_ind
+{
+ /// Index of the VIF
+ u8_l vif_index;
+ /// Status of the operation
+ u8_l status;
+ /// New channel ctx index
+ u8_l chan_idx;
+};
+
+/// Structure containing the parameters of the @ref MM_CSA_TRAFFIC_IND message
+struct mm_csa_traffic_ind
+{
+ /// Index of the VIF
+ u8_l vif_index;
+ /// Is tx traffic enable or disable
+ bool_l enable;
+};
+
+/// Structure containing the parameters of the @ref MM_MU_GROUP_UPDATE_REQ message.
+/// Size allocated for the structure depends of the number of group
+struct mm_mu_group_update_req
+{
+ /// Station index
+ u8_l sta_idx;
+ /// Number of groups the STA belongs to
+ u8_l group_cnt;
+ /// Group information
+ struct
+ {
+ /// Group Id
+ u8_l group_id;
+ /// User position
+ u8_l user_pos;
+ } groups[0];
+};
+
+///////////////////////////////////////////////////////////////////////////////
+/////////// For Scan messages
+///////////////////////////////////////////////////////////////////////////////
+enum scan_msg_tag
+{
+ /// Scanning start Request.
+ SCAN_START_REQ = LMAC_FIRST_MSG(TASK_SCAN),
+ /// Scanning start Confirmation.
+ SCAN_START_CFM,
+ /// End of scanning indication.
+ SCAN_DONE_IND,
+ /// Cancel scan request
+ SCAN_CANCEL_REQ,
+ /// Cancel scan confirmation
+ SCAN_CANCEL_CFM,
+
+ /// MAX number of messages
+ SCAN_MAX,
+};
+
+/// Maximum number of SSIDs in a scan request
+#define SCAN_SSID_MAX 4
+
+/// Maximum number of channels in a scan request
+#define SCAN_CHANNEL_MAX (MAC_DOMAINCHANNEL_24G_MAX + MAC_DOMAINCHANNEL_5G_MAX)
+
+/// Maximum length of the ProbeReq IEs (SoftMAC mode)
+#define SCAN_MAX_IE_LEN 300
+
+/// Maximum number of PHY bands supported
+#define SCAN_BAND_MAX 2
+
+/// Structure containing the parameters of the @ref SCAN_START_REQ message
+struct scan_start_req
+{
+ /// List of channel to be scanned
+ struct mac_chan_def chan[SCAN_CHANNEL_MAX];
+ /// List of SSIDs to be scanned
+ struct mac_ssid ssid[SCAN_SSID_MAX];
+ /// BSSID to be scanned
+ struct mac_addr bssid;
+ /// Index of the VIF that is scanning
+ u8_l vif_idx;
+ /// Number of channels to scan
+ u8_l chan_cnt;
+ /// Number of SSIDs to scan for
+ u8_l ssid_cnt;
+ /// no CCK - For P2P frames not being sent at CCK rate in 2GHz band.
+ bool no_cck;
+ u32_l duration;
+ /// Length of the additional IEs
+ u16_l add_ie_len;
+ /// Pointer (in host memory) to the additional IEs that need to be added to the ProbeReq
+ /// (following the SSID element)
+ ptr_addr add_ies;
+};
+
+/// Structure containing the parameters of the @ref SCAN_START_CFM message
+struct scan_start_cfm
+{
+ /// Status of the request
+ u8_l status;
+};
+
+/// Structure containing the parameters of the @ref SCAN_CANCEL_REQ message
+struct scan_cancel_req
+{
+};
+
+/// Structure containing the parameters of the @ref SCAN_START_CFM message
+struct scan_cancel_cfm
+{
+ /// Status of the request
+ u8_l status;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+/////////// For Scanu messages
+///////////////////////////////////////////////////////////////////////////////
+/// Messages that are logically related to the task.
+enum
+{
+ /// Scan request from host.
+ SCANU_START_REQ = LMAC_FIRST_MSG(TASK_SCANU),
+ /// Scanning start Confirmation.
+ SCANU_START_CFM,
+ /// Join request
+ SCANU_JOIN_REQ,
+ /// Join confirmation.
+ SCANU_JOIN_CFM,
+ /// Scan result indication.
+ SCANU_RESULT_IND,
+ /// Fast scan request from any other module.
+ SCANU_FAST_REQ,
+ /// Confirmation of fast scan request.
+ SCANU_FAST_CFM,
+ ///Scan cancel from host.
+ SCANU_CANCEL_REQ,
+ ///Scan cancel confirmation
+ SCANU_CANCEL_CFM,
+
+ /// MAX number of messages
+ SCANU_MAX,
+};
+
+/// Maximum length of the additional ProbeReq IEs (FullMAC mode)
+#define SCANU_MAX_IE_LEN 200
+
+/// Structure containing the parameters of the @ref SCANU_START_REQ message
+struct scanu_start_req
+{
+ /// List of channel to be scanned
+ struct mac_chan_def chan[SCAN_CHANNEL_MAX];
+ /// List of SSIDs to be scanned
+ struct mac_ssid ssid[SCAN_SSID_MAX];
+ /// BSSID to be scanned (or WILDCARD BSSID if no BSSID is searched in particular)
+ struct mac_addr bssid;
+ /// Index of the VIF that is scanning
+ u8_l vif_idx;
+ /// Number of channels to scan
+ u8_l chan_cnt;
+ /// Number of SSIDs to scan for
+ u8_l ssid_cnt;
+ /// no CCK - For P2P frames not being sent at CCK rate in 2GHz band.
+ bool no_cck;
+ u32_l duration;
+ /// Length of the additional IEs
+ u16_l add_ie_len;
+ /// Address (in host memory) of the additional IEs that need to be added to the ProbeReq
+ /// (following the SSID element)
+ ptr_addr add_ies;
+};
+
+/// Structure containing the parameters of the @ref SCANU_START_CFM message
+struct scanu_start_cfm
+{
+ /// Index of the VIF that was scanning
+ u8_l vif_idx;
+ /// Status of the request
+ u8_l status;
+ /// Number of scan results available
+ u8_l result_cnt;
+};
+
+/// Parameters of the @SCANU_RESULT_IND message
+struct scanu_result_ind
+{
+ /// Length of the frame
+ u16_l length;
+ /// Frame control field of the frame.
+ u16_l framectrl;
+ /// Center frequency on which we received the packet
+ u16_l center_freq;
+ /// PHY band
+ u8_l band;
+ /// Index of the station that sent the frame. 0xFF if unknown.
+ u8_l sta_idx;
+ /// Index of the VIF that received the frame. 0xFF if unknown.
+ u8_l inst_nbr;
+ /// RSSI of the received frame.
+ s8_l rssi;
+ /// Frame payload.
+ u32_l payload[];
+};
+
+/// Structure containing the parameters of the message.
+struct scanu_fast_req
+{
+ /// The SSID to scan in the channel.
+ struct mac_ssid ssid;
+ /// BSSID.
+ struct mac_addr bssid;
+ /// Probe delay.
+ u16_l probe_delay;
+ /// Minimum channel time.
+ u16_l minch_time;
+ /// Maximum channel time.
+ u16_l maxch_time;
+ /// The channel number to scan.
+ u16_l ch_nbr;
+};
+/// Structure containing the parameters of the @ref SCANU_CANCEL_REQ message
+struct scanu_cancel_req
+{
+ /// index of the scan element
+ uint8_t vif_idx;
+};
+
+/// Structure containing the parameters of the @ref SCANU_CANCEL_CFM and
+/// @ref SCANU_CANCEL_CFM messages
+struct scanu_cancel_cfm
+{
+ /// Index of the VIF that was scanning
+ uint8_t vif_idx;
+ /// Status of the request
+ uint8_t status;
+};
+
+
+///////////////////////////////////////////////////////////////////////////////
+/////////// For ME messages
+///////////////////////////////////////////////////////////////////////////////
+/// Messages that are logically related to the task.
+enum
+{
+ /// Configuration request from host.
+ ME_CONFIG_REQ = LMAC_FIRST_MSG(TASK_ME),
+ /// Configuration confirmation.
+ ME_CONFIG_CFM,
+ /// Configuration request from host.
+ ME_CHAN_CONFIG_REQ,
+ /// Configuration confirmation.
+ ME_CHAN_CONFIG_CFM,
+ /// Set control port state for a station.
+ ME_SET_CONTROL_PORT_REQ,
+ /// Control port setting confirmation.
+ ME_SET_CONTROL_PORT_CFM,
+ /// TKIP MIC failure indication.
+ ME_TKIP_MIC_FAILURE_IND,
+ /// Add a station to the FW (AP mode)
+ ME_STA_ADD_REQ,
+ /// Confirmation of the STA addition
+ ME_STA_ADD_CFM,
+ /// Delete a station from the FW (AP mode)
+ ME_STA_DEL_REQ,
+ /// Confirmation of the STA deletion
+ ME_STA_DEL_CFM,
+ /// Indication of a TX RA/TID queue credit update
+ ME_TX_CREDITS_UPDATE_IND,
+ /// Request indicating to the FW that there is traffic buffered on host
+ ME_TRAFFIC_IND_REQ,
+ /// Confirmation that the @ref ME_TRAFFIC_IND_REQ has been executed
+ ME_TRAFFIC_IND_CFM,
+ /// Request of RC statistics to a station
+ ME_RC_STATS_REQ,
+ /// RC statistics confirmation
+ ME_RC_STATS_CFM,
+ /// RC fixed rate request
+ ME_RC_SET_RATE_REQ,
+ /// Configure monitor interface
+ ME_CONFIG_MONITOR_REQ,
+ /// Configure monitor interface response
+ ME_CONFIG_MONITOR_CFM,
+ /// Setting power Save mode request from host
+ ME_SET_PS_MODE_REQ,
+ /// Set power Save mode confirmation
+ ME_SET_PS_MODE_CFM,
+ /// MAX number of messages
+ ME_MAX,
+};
+
+/// Structure containing the parameters of the @ref ME_START_REQ message
+struct me_config_req
+{
+ /// HT Capabilities
+ struct mac_htcapability ht_cap;
+ /// VHT Capabilities
+ struct mac_vhtcapability vht_cap;
+ /// HE capabilities
+ struct mac_hecapability he_cap;
+ /// Lifetime of packets sent under a BlockAck agreement (expressed in TUs)
+ u16_l tx_lft;
+ /// Maximum supported BW
+ u8_l phy_bw_max;
+ /// Boolean indicating if HT is supported or not
+ bool_l ht_supp;
+ /// Boolean indicating if VHT is supported or not
+ bool_l vht_supp;
+ /// Boolean indicating if HE is supported or not
+ bool_l he_supp;
+ /// Boolean indicating if HE OFDMA UL is enabled or not
+ bool_l he_ul_on;
+ /// Boolean indicating if PS mode shall be enabled or not
+ bool_l ps_on;
+ /// Boolean indicating if Antenna Diversity shall be enabled or not
+ bool_l ant_div_on;
+ /// Boolean indicating if Dynamic PS mode shall be used or not
+ bool_l dpsm;
+ unsigned char sleep_flag;
+};
+
+/// Structure containing the parameters of the @ref ME_CHAN_CONFIG_REQ message
+struct me_chan_config_req
+{
+ /// List of 2.4GHz supported channels
+ struct mac_chan_def chan2G4[MAC_DOMAINCHANNEL_24G_MAX];
+ /// List of 5GHz supported channels
+ struct mac_chan_def chan5G[MAC_DOMAINCHANNEL_5G_MAX];
+ /// Number of 2.4GHz channels in the list
+ u8_l chan2G4_cnt;
+ /// Number of 5GHz channels in the list
+ u8_l chan5G_cnt;
+};
+
+/// Structure containing the parameters of the @ref ME_SET_CONTROL_PORT_REQ message
+struct me_set_control_port_req
+{
+ /// Index of the station for which the control port is opened
+ u8_l sta_idx;
+ /// Control port state
+ bool_l control_port_open;
+};
+
+/// Structure containing the parameters of the @ref ME_TKIP_MIC_FAILURE_IND message
+struct me_tkip_mic_failure_ind
+{
+ /// Address of the sending STA
+ struct mac_addr addr;
+ /// TSC value
+ u64_l tsc;
+ /// Boolean indicating if the packet was a group or unicast one (true if group)
+ bool_l ga;
+ /// Key Id
+ u8_l keyid;
+ /// VIF index
+ u8_l vif_idx;
+};
+
+/// Structure containing the parameters of the @ref ME_STA_ADD_REQ message
+struct me_sta_add_req
+{
+ /// MAC address of the station to be added
+ struct mac_addr mac_addr;
+ /// Supported legacy rates
+ struct mac_rateset rate_set;
+ /// HT Capabilities
+ struct mac_htcapability ht_cap;
+ /// VHT Capabilities
+ struct mac_vhtcapability vht_cap;
+ /// HE capabilities
+ struct mac_hecapability he_cap;
+ /// Flags giving additional information about the station (@ref mac_sta_flags)
+ u32_l flags;
+ /// Association ID of the station
+ u16_l aid;
+ /// Bit field indicating which queues have U-APSD enabled
+ u8_l uapsd_queues;
+ /// Maximum size, in frames, of a APSD service period
+ u8_l max_sp_len;
+ /// Operation mode information (valid if bit @ref STA_OPMOD_NOTIF is
+ /// set in the flags)
+ u8_l opmode;
+ /// Index of the VIF the station is attached to
+ u8_l vif_idx;
+ /// Whether the the station is TDLS station
+ bool_l tdls_sta;
+ /// Indicate if the station is TDLS link initiator station
+ bool_l tdls_sta_initiator;
+ /// Indicate if the TDLS Channel Switch is allowed
+ bool_l tdls_chsw_allowed;
+};
+
+/// Structure containing the parameters of the @ref ME_STA_ADD_CFM message
+struct me_sta_add_cfm
+{
+ /// Station index
+ u8_l sta_idx;
+ /// Status of the station addition
+ u8_l status;
+ /// PM state of the station
+ u8_l pm_state;
+};
+
+/// Structure containing the parameters of the @ref ME_STA_DEL_REQ message.
+struct me_sta_del_req
+{
+ /// Index of the station to be deleted
+ u8_l sta_idx;
+ /// Whether the the station is TDLS station
+ bool_l tdls_sta;
+};
+
+/// Structure containing the parameters of the @ref ME_TX_CREDITS_UPDATE_IND message.
+struct me_tx_credits_update_ind
+{
+ /// Index of the station for which the credits are updated
+ u8_l sta_idx;
+ /// TID for which the credits are updated
+ u8_l tid;
+ /// Offset to be applied on the credit count
+ s8_l credits;
+};
+
+/// Structure containing the parameters of the @ref ME_TRAFFIC_IND_REQ message.
+struct me_traffic_ind_req
+{
+ /// Index of the station for which UAPSD traffic is available on host
+ u8_l sta_idx;
+ /// Flag indicating the availability of UAPSD packets for the given STA
+ u8_l tx_avail;
+ /// Indicate if traffic is on uapsd-enabled queues
+ bool_l uapsd;
+};
+
+/// Structure containing the parameters of the @ref ME_RC_STATS_REQ message.
+struct me_rc_stats_req
+{
+ /// Index of the station for which the RC statistics are requested
+ u8_l sta_idx;
+};
+
+/// Structure containing the rate control statistics
+struct rc_rate_stats
+{
+ /// Number of attempts (per sampling interval)
+ u16_l attempts;
+ /// Number of success (per sampling interval)
+ u16_l success;
+ /// Estimated probability of success (EWMA)
+ u16_l probability;
+ /// Rate configuration of the sample
+ u16_l rate_config;
+ union
+ {
+ struct {
+ /// Number of times the sample has been skipped (per sampling interval)
+ u8_l sample_skipped;
+ /// Whether the old probability is available
+ bool_l old_prob_available;
+ /// Whether the rate can be used in the retry chain
+ bool_l rate_allowed;
+ };
+ struct {
+ /// RU size and UL length received in the latest HE trigger frame
+ u16_l ru_and_length;
+ };
+ };
+};
+
+/// Number of RC samples
+#define RC_MAX_N_SAMPLE 10
+/// Index of the HE statistics element in the table
+#define RC_HE_STATS_IDX RC_MAX_N_SAMPLE
+
+/// Structure containing the parameters of the @ref ME_RC_STATS_CFM message.
+struct me_rc_stats_cfm
+{
+ /// Index of the station for which the RC statistics are provided
+ u8_l sta_idx;
+ /// Number of samples used in the RC algorithm
+ u16_l no_samples;
+ /// Number of MPDUs transmitted (per sampling interval)
+ u16_l ampdu_len;
+ /// Number of AMPDUs transmitted (per sampling interval)
+ u16_l ampdu_packets;
+ /// Average number of MPDUs in each AMPDU frame (EWMA)
+ u32_l avg_ampdu_len;
+ // Current step 0 of the retry chain
+ u8_l sw_retry_step;
+ /// Trial transmission period
+ u8_l sample_wait;
+ /// Retry chain steps
+ u16_l retry_step_idx[4];
+ /// RC statistics - Max number of RC samples, plus one for the HE TB statistics
+ struct rc_rate_stats rate_stats[RC_MAX_N_SAMPLE + 1];
+ /// Throughput - Max number of RC samples, plus one for the HE TB statistics
+ u32_l tp[RC_MAX_N_SAMPLE + 1];
+};
+
+/// Structure containing the parameters of the @ref ME_RC_SET_RATE_REQ message.
+struct me_rc_set_rate_req
+{
+ /// Index of the station for which the fixed rate is set
+ u8_l sta_idx;
+ /// Rate configuration to be set
+ u16_l fixed_rate_cfg;
+};
+
+/// Structure containing the parameters of the @ref ME_CONFIG_MONITOR_REQ message.
+struct me_config_monitor_req
+{
+ /// Channel to configure
+ struct mac_chan_op chan;
+ /// Is channel data valid
+ bool_l chan_set;
+ /// Enable report of unsupported HT frames
+ bool_l uf;
+};
+
+/// Structure containing the parameters of the @ref ME_CONFIG_MONITOR_CFM message.
+struct me_config_monitor_cfm
+{
+ /// Channel context index
+ u8_l chan_index;
+ /// Channel parameters
+ struct mac_chan_op chan;
+};
+
+/// Structure containing the parameters of the @ref ME_SET_PS_MODE_REQ message.
+struct me_set_ps_mode_req
+{
+ /// Power Save is activated or deactivated
+ u8_l ps_state;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+/////////// For TWT messages
+///////////////////////////////////////////////////////////////////////////////
+/// Message API of the TWT task
+enum
+{
+ /// Request Individual TWT Establishment
+ TWT_SETUP_REQ = LMAC_FIRST_MSG(TASK_TWT),
+ /// Confirm Individual TWT Establishment
+ TWT_SETUP_CFM,
+ /// Indicate TWT Setup response from peer
+ TWT_SETUP_IND,
+ /// Request to destroy a TWT Establishment or all of them
+ TWT_TEARDOWN_REQ,
+ /// Confirm to destroy a TWT Establishment or all of them
+ TWT_TEARDOWN_CFM,
+
+ /// MAX number of messages
+ TWT_MAX,
+};
+
+///TWT Group assignment
+struct twt_grp_assignment_tag
+{
+ /// TWT Group assignment byte array
+ u8_l grp_assignment[9];
+};
+
+///TWT Flow configuration
+struct twt_conf_tag
+{
+ /// Flow Type (0: Announced, 1: Unannounced)
+ u8_l flow_type;
+ /// Wake interval Exponent
+ u8_l wake_int_exp;
+ /// Unit of measurement of TWT Minimum Wake Duration (0:256us, 1:tu)
+ bool_l wake_dur_unit;
+ /// Nominal Minimum TWT Wake Duration
+ u8_l min_twt_wake_dur;
+ /// TWT Wake Interval Mantissa
+ u16_l wake_int_mantissa;
+};
+
+///TWT Setup request message structure
+struct twt_setup_req
+{
+ /// VIF Index
+ u8_l vif_idx;
+ /// Setup request type
+ u8_l setup_type;
+ /// TWT Setup configuration
+ struct twt_conf_tag conf;
+};
+
+///TWT Setup confirmation message structure
+struct twt_setup_cfm
+{
+ /// Status (0 = TWT Setup Request has been transmitted to peer)
+ u8_l status;
+};
+
+/// TWT Setup command
+enum twt_setup_types
+{
+ MAC_TWT_SETUP_REQ = 0,
+ MAC_TWT_SETUP_SUGGEST,
+ MAC_TWT_SETUP_DEMAND,
+ MAC_TWT_SETUP_GROUPING,
+ MAC_TWT_SETUP_ACCEPT,
+ MAC_TWT_SETUP_ALTERNATE,
+ MAC_TWT_SETUP_DICTATE,
+ MAC_TWT_SETUP_REJECT,
+};
+
+///TWT Setup indication message structure
+struct twt_setup_ind
+{
+ /// Response type
+ u8_l resp_type;
+ /// STA Index
+ u8_l sta_idx;
+ /// TWT Setup configuration
+ struct twt_conf_tag conf;
+};
+
+/// TWT Teardown request message structure
+struct twt_teardown_req
+{
+ /// TWT Negotiation type
+ u8_l neg_type;
+ /// All TWT
+ u8_l all_twt;
+ /// TWT flow ID
+ u8_l id;
+ /// VIF Index
+ u8_l vif_idx;
+};
+
+///TWT Teardown confirmation message structure
+struct twt_teardown_cfm
+{
+ /// Status (0 = TWT Teardown Request has been transmitted to peer)
+ u8_l status;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+/////////// For SM messages
+///////////////////////////////////////////////////////////////////////////////
+/// Message API of the SM task
+enum sm_msg_tag
+{
+ /// Request to connect to an AP
+ SM_CONNECT_REQ = LMAC_FIRST_MSG(TASK_SM),
+ /// Confirmation of connection
+ SM_CONNECT_CFM,
+ /// Indicates that the SM associated to the AP
+ SM_CONNECT_IND,
+ /// Request to disconnect
+ SM_DISCONNECT_REQ,
+ /// Confirmation of disconnection
+ SM_DISCONNECT_CFM,
+ /// Indicates that the SM disassociated the AP
+ SM_DISCONNECT_IND,
+ /// Request to start external authentication
+ SM_EXTERNAL_AUTH_REQUIRED_IND,
+ /// Response to external authentication request
+ SM_EXTERNAL_AUTH_REQUIRED_RSP,
+ SM_FT_AUTH_IND,
+ SM_FT_AUTH_RSP,
+
+ /// MAX number of messages
+ SM_MAX,
+};
+
+/// Structure containing the parameters of @ref SM_CONNECT_REQ message.
+struct sm_connect_req
+{
+ /// SSID to connect to
+ struct mac_ssid ssid;
+ /// BSSID to connect to (if not specified, set this field to WILDCARD BSSID)
+ struct mac_addr bssid;
+ /// Channel on which we have to connect (if not specified, set -1 in the chan.freq field)
+ struct mac_chan_def chan;
+ /// Connection flags (see @ref mac_connection_flags)
+ u32_l flags;
+ /// Control port Ethertype (in network endianness)
+ u16_l ctrl_port_ethertype;
+ /// Length of the association request IEs
+ /// Listen interval to be used for this connection
+ u16_l listen_interval;
+ /// Flag indicating if the we have to wait for the BC/MC traffic after beacon or not
+ bool_l dont_wait_bcmc;
+ /// Authentication type
+ u8_l auth_type;
+ /// UAPSD queues (bit0: VO, bit1: VI, bit2: BE, bit3: BK)
+ u8_l uapsd_queues;
+ /// VIF index
+ u8_l vif_idx;
+ u16_l ie_len;
+ /// Buffer containing the additional information elements to be put in the
+ /// association request
+ u32_l ie_buf[0];
+};
+
+/// Structure containing the parameters of the @ref SM_CONNECT_CFM message.
+struct sm_connect_cfm
+{
+ /// Status. If 0, it means that the connection procedure will be performed and that
+ /// a subsequent @ref SM_CONNECT_IND message will be forwarded once the procedure is
+ /// completed
+ u8_l status;
+};
+
+#define SM_ASSOC_IE_LEN 800
+/// Structure containing the parameters of the @ref SM_CONNECT_IND message.
+struct sm_connect_ind
+{
+ /// Status code of the connection procedure
+ u16_l status_code;
+ /// BSSID
+ struct mac_addr bssid;
+ /// Flag indicating if the indication refers to an internal roaming or from a host request
+ bool_l roamed;
+ /// Index of the VIF for which the association process is complete
+ u8_l vif_idx;
+ /// Index of the STA entry allocated for the AP
+ u8_l ap_idx;
+ /// Index of the LMAC channel context the connection is attached to
+ u8_l ch_idx;
+ /// Flag indicating if the AP is supporting QoS
+ bool_l qos;
+ /// ACM bits set in the AP WMM parameter element
+ u8_l acm;
+ /// Length of the AssocReq IEs
+ u16_l assoc_req_ie_len;
+ /// Length of the AssocRsp IEs
+ u16_l assoc_rsp_ie_len;
+ /// IE buffer
+ /// Association Id allocated by the AP for this connection
+ u16_l aid;
+ /// AP operating channel
+ struct mac_chan_op chan;
+ /// EDCA parameters
+ u32_l ac_param[AC_MAX];
+ u32_l assoc_ie_buf[0];
+};
+
+/// Structure containing the parameters of the @ref SM_DISCONNECT_REQ message.
+struct sm_disconnect_req
+{
+ /// Reason of the deauthentication.
+ u16_l reason_code;
+ /// Index of the VIF.
+ u8_l vif_idx;
+};
+
+/// Structure containing the parameters of SM_ASSOCIATION_IND the message
+struct sm_association_ind
+{
+ // MAC ADDR of the STA
+ struct mac_addr me_mac_addr;
+};
+
+
+/// Structure containing the parameters of the @ref SM_DISCONNECT_IND message.
+struct sm_disconnect_ind
+{
+ /// Reason of the disconnection.
+ u16_l reason_code;
+ /// Index of the VIF.
+ u8_l vif_idx;
+ /// FT over DS is ongoing
+ bool_l reassoc;
+};
+
+/// Structure containing the parameters of the @ref SM_EXTERNAL_AUTH_REQUIRED_IND
+struct sm_external_auth_required_ind
+{
+ /// Index of the VIF.
+ u8_l vif_idx;
+ /// SSID to authenticate to
+ struct mac_ssid ssid;
+ /// BSSID to authenticate to
+ struct mac_addr bssid;
+ /// AKM suite of the respective authentication
+ u32_l akm;
+};
+
+/// Structure containing the parameters of the @ref SM_EXTERNAL_AUTH_REQUIRED_RSP
+struct sm_external_auth_required_rsp
+{
+ /// Index of the VIF.
+ u8_l vif_idx;
+ /// Authentication status
+ u16_l status;
+};
+
+/// Structure containing the parameters of the @ref SM_FT_AUTH_IND
+struct sm_ft_auth_ind
+{
+ /// Index of the VIF.
+ u8_l vif_idx;
+ /// Size of the FT elements
+ u16_l ft_ie_len;
+ /// Fast Transition elements in the authentication
+ u32_l ft_ie_buf[0];
+};
+///////////////////////////////////////////////////////////////////////////////
+/////////// For APM messages
+///////////////////////////////////////////////////////////////////////////////
+/// Message API of the APM task
+enum apm_msg_tag
+{
+ /// Request to start the AP.
+ APM_START_REQ = LMAC_FIRST_MSG(TASK_APM),
+ /// Confirmation of the AP start.
+ APM_START_CFM,
+ /// Request to stop the AP.
+ APM_STOP_REQ,
+ /// Confirmation of the AP stop.
+ APM_STOP_CFM,
+ /// Request to start CAC
+ APM_START_CAC_REQ,
+ /// Confirmation of the CAC start
+ APM_START_CAC_CFM,
+ /// Request to stop CAC
+ APM_STOP_CAC_REQ,
+ /// Confirmation of the CAC stop
+ APM_STOP_CAC_CFM,
+ APM_PROBE_CLIENT_REQ,
+ APM_PROBE_CLIENT_CFM,
+ APM_PROBE_CLIENT_IND,
+
+ /// MAX number of messages
+ APM_MAX,
+};
+
+/// Structure containing the parameters of the @ref APM_START_REQ message.
+struct apm_start_req
+{
+ /// Basic rate set
+ struct mac_rateset basic_rates;
+ /// Operating channel on which we have to enable the AP
+ struct mac_chan_op chan;
+ /// Offset of the TIM IE in the beacon
+ u16_l tim_oft;
+ /// Beacon interval
+ u16_l bcn_int;
+ /// Flags (@ref mac_connection_flags)
+ u32_l flags;
+ /// Control port Ethertype
+ u16_l ctrl_port_ethertype;
+ /// Length of the TIM IE
+ u8_l tim_len;
+ /// Index of the VIF for which the AP is started
+ u8_l vif_idx;
+ /// Length of the beacon template
+ u16_l bcn_len;
+ /// Address, in host memory, to the beacon template
+ ptr_addr bcn_addr;
+};
+
+/// Structure containing the parameters of the @ref APM_START_CFM message.
+struct apm_start_cfm
+{
+ /// Status of the AP starting procedure
+ u8_l status;
+ /// Index of the VIF for which the AP is started
+ u8_l vif_idx;
+ /// Index of the channel context attached to the VIF
+ u8_l ch_idx;
+ /// Index of the STA used for BC/MC traffic
+ u8_l bcmc_idx;
+};
+
+/// Structure containing the parameters of the @ref APM_STOP_REQ message.
+struct apm_stop_req
+{
+ /// Index of the VIF for which the AP has to be stopped
+ u8_l vif_idx;
+};
+
+/// Structure containing the parameters of the @ref APM_START_CAC_REQ message.
+struct apm_start_cac_req
+{
+ /// Channel configuration
+ struct mac_chan_op chan;
+ /// Index of the VIF for which the CAC is started
+ u8_l vif_idx;
+};
+
+/// Structure containing the parameters of the @ref APM_START_CAC_CFM message.
+struct apm_start_cac_cfm
+{
+ /// Status of the CAC starting procedure
+ u8_l status;
+ /// Index of the channel context attached to the VIF for CAC
+ u8_l ch_idx;
+};
+
+/// Structure containing the parameters of the @ref APM_STOP_CAC_REQ message.
+struct apm_stop_cac_req
+{
+ /// Index of the VIF for which the CAC has to be stopped
+ u8_l vif_idx;
+};
+
+/// Structure containing the parameters of the @ref APM_PROBE_CLIENT_REQ message.
+struct apm_probe_client_req
+{
+ /// Index of the VIF
+ u8_l vif_idx;
+ /// Index of the Station to probe
+ u8_l sta_idx;
+};
+
+/// Structure containing the parameters of the @ref APM_PROBE_CLIENT_CFM message.
+struct apm_probe_client_cfm
+{
+ /// Status of the probe request
+ u8_l status;
+ /// Unique ID to distinguish @ref APM_PROBE_CLIENT_IND message
+ u32_l probe_id;
+};
+
+/// Structure containing the parameters of the @ref APM_PROBE_CLIENT_CFM message.
+struct apm_probe_client_ind
+{
+ /// Index of the VIF
+ u8_l vif_idx;
+ /// Index of the Station to probe
+ u8_l sta_idx;
+ /// Whether client is still present or not
+ bool_l client_present;
+ /// Unique ID as returned in @ref APM_PROBE_CLIENT_CFM
+ u32_l probe_id;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+/////////// For MESH messages
+///////////////////////////////////////////////////////////////////////////////
+
+/// Maximum length of the Mesh ID
+#define MESH_MESHID_MAX_LEN (32)
+
+/// Message API of the MESH task
+enum mesh_msg_tag
+{
+ /// Request to start the MP
+ MESH_START_REQ = LMAC_FIRST_MSG(TASK_MESH),
+ /// Confirmation of the MP start.
+ MESH_START_CFM,
+
+ /// Request to stop the MP.
+ MESH_STOP_REQ,
+ /// Confirmation of the MP stop.
+ MESH_STOP_CFM,
+
+ // Request to update the MP
+ MESH_UPDATE_REQ,
+ /// Confirmation of the MP update
+ MESH_UPDATE_CFM,
+
+ /// Request information about a given link
+ MESH_PEER_INFO_REQ,
+ /// Response to the MESH_PEER_INFO_REQ message
+ MESH_PEER_INFO_CFM,
+
+ /// Request automatic establishment of a path with a given mesh STA
+ MESH_PATH_CREATE_REQ,
+ /// Confirmation to the MESH_PATH_CREATE_REQ message
+ MESH_PATH_CREATE_CFM,
+
+ /// Request a path update (delete path, modify next hop mesh STA)
+ MESH_PATH_UPDATE_REQ,
+ /// Confirmation to the MESH_PATH_UPDATE_REQ message
+ MESH_PATH_UPDATE_CFM,
+
+ /// Indication from Host that the indicated Mesh Interface is a proxy for an external STA
+ MESH_PROXY_ADD_REQ,
+
+ /// Indicate that a connection has been established or lost
+ MESH_PEER_UPDATE_IND,
+ /// Notification that a connection has been established or lost (when MPM handled by userspace)
+ MESH_PEER_UPDATE_NTF = MESH_PEER_UPDATE_IND,
+
+ /// Indicate that a path is now active or inactive
+ MESH_PATH_UPDATE_IND,
+ /// Indicate that proxy information have been updated
+ MESH_PROXY_UPDATE_IND,
+
+ /// MAX number of messages
+ MESH_MAX,
+};
+
+/// Structure containing the parameters of the @ref MESH_START_REQ message.
+struct mesh_start_req
+{
+ /// Basic rate set
+ struct mac_rateset basic_rates;
+ /// Operating channel on which we have to enable the AP
+ struct mac_chan_op chan;
+ /// DTIM Period
+ u8_l dtim_period;
+ /// Beacon Interval
+ u16_l bcn_int;
+ /// Index of the VIF for which the MP is started
+ u8_l vif_index;
+ /// Length of the Mesh ID
+ u8_l mesh_id_len;
+ /// Mesh ID
+ u8_l mesh_id[MESH_MESHID_MAX_LEN];
+ /// Address of the IEs to download
+ u32_l ie_addr;
+ /// Length of the provided IEs
+ u8_l ie_len;
+ /// Indicate if Mesh Peering Management (MPM) protocol is handled in userspace
+ bool_l user_mpm;
+ /// Indicate if Mesh Point is using authentication
+ bool_l is_auth;
+ /// Indicate which authentication method is used
+ u8_l auth_id;
+};
+
+/// Structure containing the parameters of the @ref MESH_START_CFM message.
+struct mesh_start_cfm
+{
+ /// Status of the MP starting procedure
+ u8_l status;
+ /// Index of the VIF for which the MP is started
+ u8_l vif_idx;
+ /// Index of the channel context attached to the VIF
+ u8_l ch_idx;
+ /// Index of the STA used for BC/MC traffic
+ u8_l bcmc_idx;
+};
+
+/// Structure containing the parameters of the @ref MESH_STOP_REQ message.
+struct mesh_stop_req
+{
+ /// Index of the VIF for which the MP has to be stopped
+ u8_l vif_idx;
+};
+
+/// Structure containing the parameters of the @ref MESH_STOP_CFM message.
+struct mesh_stop_cfm
+{
+ /// Index of the VIF for which the MP has to be stopped
+ u8_l vif_idx;
+ /// Status
+ u8_l status;
+};
+
+/// Bit fields for mesh_update_req message's flags value
+enum mesh_update_flags_bit
+{
+ /// Root Mode
+ MESH_UPDATE_FLAGS_ROOT_MODE_BIT = 0,
+ /// Gate Mode
+ MESH_UPDATE_FLAGS_GATE_MODE_BIT,
+ /// Mesh Forwarding
+ MESH_UPDATE_FLAGS_MESH_FWD_BIT,
+ /// Local Power Save Mode
+ MESH_UPDATE_FLAGS_LOCAL_PSM_BIT,
+};
+
+/// Structure containing the parameters of the @ref MESH_UPDATE_REQ message.
+struct mesh_update_req
+{
+ /// Flags, indicate fields which have been updated
+ u8_l flags;
+ /// VIF Index
+ u8_l vif_idx;
+ /// Root Mode
+ u8_l root_mode;
+ /// Gate Announcement
+ bool_l gate_announ;
+ /// Mesh Forwarding
+ bool_l mesh_forward;
+ /// Local PS Mode
+ u8_l local_ps_mode;
+};
+
+/// Structure containing the parameters of the @ref MESH_UPDATE_CFM message.
+struct mesh_update_cfm
+{
+ /// Status
+ u8_l status;
+};
+
+/// Structure containing the parameters of the @ref MESH_PEER_INFO_REQ message.
+struct mesh_peer_info_req
+{
+ ///Index of the station allocated for the peer
+ u8_l sta_idx;
+};
+
+/// Structure containing the parameters of the @ref MESH_PEER_INFO_CFM message.
+struct mesh_peer_info_cfm
+{
+ /// Response status
+ u8_l status;
+ /// Index of the station allocated for the peer
+ u8_l sta_idx;
+ /// Local Link ID
+ u16_l local_link_id;
+ /// Peer Link ID
+ u16_l peer_link_id;
+ /// Local PS Mode
+ u8_l local_ps_mode;
+ /// Peer PS Mode
+ u8_l peer_ps_mode;
+ /// Non-peer PS Mode
+ u8_l non_peer_ps_mode;
+ /// Link State
+ u8_l link_state;
+ u32_l last_bcn_age;
+};
+
+/// Structure containing the parameters of the @ref MESH_PATH_CREATE_REQ message.
+struct mesh_path_create_req
+{
+ /// Index of the interface on which path has to be created
+ u8_l vif_idx;
+ /// Indicate if originator MAC Address is provided
+ bool_l has_orig_addr;
+ /// Path Target MAC Address
+ struct mac_addr tgt_mac_addr;
+ /// Originator MAC Address
+ struct mac_addr orig_mac_addr;
+};
+
+/// Structure containing the parameters of the @ref MESH_PATH_CREATE_CFM message.
+struct mesh_path_create_cfm
+{
+ /// Confirmation status
+ u8_l status;
+ /// VIF Index
+ u8_l vif_idx;
+};
+
+/// Structure containing the parameters of the @ref MESH_PATH_UPDATE_REQ message.
+struct mesh_path_update_req
+{
+ /// Indicate if path must be deleted
+ bool_l delete;
+ /// Index of the interface on which path has to be created
+ u8_l vif_idx;
+ /// Path Target MAC Address
+ struct mac_addr tgt_mac_addr;
+ /// Next Hop MAC Address
+ struct mac_addr nhop_mac_addr;
+};
+
+/// Structure containing the parameters of the @ref MESH_PATH_UPDATE_CFM message.
+struct mesh_path_update_cfm
+{
+ /// Confirmation status
+ u8_l status;
+ /// VIF Index
+ u8_l vif_idx;
+};
+
+/// Structure containing the parameters of the @ref MESH_PROXY_ADD_REQ message.
+struct mesh_proxy_add_req
+{
+ /// VIF Index
+ u8_l vif_idx;
+ /// MAC Address of the External STA
+ struct mac_addr ext_sta_addr;
+};
+
+/// Structure containing the parameters of the @ref MESH_PROXY_UPDATE_IND
+struct mesh_proxy_update_ind
+{
+ /// Indicate if proxy information has been added or deleted
+ bool_l delete;
+ /// Indicate if we are a proxy for the external STA
+ bool_l local;
+ /// VIF Index
+ u8_l vif_idx;
+ /// MAC Address of the External STA
+ struct mac_addr ext_sta_addr;
+ /// MAC Address of the proxy (only valid if local is false)
+ struct mac_addr proxy_mac_addr;
+};
+
+/// Structure containing the parameters of the @ref MESH_PEER_UPDATE_IND message.
+struct mesh_peer_update_ind
+{
+ /// Indicate if connection has been established or lost
+ bool_l estab;
+ /// VIF Index
+ u8_l vif_idx;
+ /// STA Index
+ u8_l sta_idx;
+ /// Peer MAC Address
+ struct mac_addr peer_addr;
+};
+
+/// Structure containing the parameters of the @ref MESH_PEER_UPDATE_NTF message.
+struct mesh_peer_update_ntf
+{
+ /// VIF Index
+ u8_l vif_idx;
+ /// STA Index
+ u8_l sta_idx;
+ /// Mesh Link State
+ u8_l state;
+};
+
+/// Structure containing the parameters of the @ref MESH_PATH_UPDATE_IND message.
+struct mesh_path_update_ind
+{
+ /// Indicate if path is deleted or not
+ bool_l delete;
+ /// Indicate if path is towards an external STA (not part of MBSS)
+ bool_l ext_sta;
+ /// VIF Index
+ u8_l vif_idx;
+ /// Path Index
+ u8_l path_idx;
+ /// Target MAC Address
+ struct mac_addr tgt_mac_addr;
+ /// External STA MAC Address (only if ext_sta is true)
+ struct mac_addr ext_sta_mac_addr;
+ /// Next Hop STA Index
+ u8_l nhop_sta_idx;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+/////////// For Debug messages
+///////////////////////////////////////////////////////////////////////////////
+
+/// Messages related to Debug Task
+enum dbg_msg_tag
+{
+ /// Memory read request
+ DBG_MEM_READ_REQ = LMAC_FIRST_MSG(TASK_DBG),
+ /// Memory read confirm
+ DBG_MEM_READ_CFM,
+ /// Memory write request
+ DBG_MEM_WRITE_REQ,
+ /// Memory write confirm
+ DBG_MEM_WRITE_CFM,
+ /// Module filter request
+ DBG_SET_MOD_FILTER_REQ,
+ /// Module filter confirm
+ DBG_SET_MOD_FILTER_CFM,
+ /// Severity filter request
+ DBG_SET_SEV_FILTER_REQ,
+ /// Severity filter confirm
+ DBG_SET_SEV_FILTER_CFM,
+ /// LMAC/MAC HW fatal error indication
+ DBG_ERROR_IND,
+ /// Request to get system statistics
+ DBG_GET_SYS_STAT_REQ,
+ /// COnfirmation of system statistics
+ DBG_GET_SYS_STAT_CFM,
+ /// Max number of Debug messages
+ DBG_MAX,
+};
+
+/// Structure containing the parameters of the @ref DBG_MEM_READ_REQ message.
+struct dbg_mem_read_req
+{
+ u32_l memaddr;
+};
+
+/// Structure containing the parameters of the @ref DBG_MEM_READ_CFM message.
+struct dbg_mem_read_cfm
+{
+ u32_l memaddr;
+ u32_l memdata;
+};
+
+/// Structure containing the parameters of the @ref DBG_MEM_WRITE_REQ message.
+struct dbg_mem_write_req
+{
+ u32_l memaddr;
+ u32_l memdata;
+};
+
+/// Structure containing the parameters of the @ref DBG_MEM_WRITE_CFM message.
+struct dbg_mem_write_cfm
+{
+ u32_l memaddr;
+ u32_l memdata;
+};
+
+/// Structure containing the parameters of the @ref DBG_SET_MOD_FILTER_REQ message.
+struct dbg_set_mod_filter_req
+{
+ /// Bit field indicating for each module if the traces are enabled or not
+ u32_l mod_filter;
+};
+
+/// Structure containing the parameters of the @ref DBG_SEV_MOD_FILTER_REQ message.
+struct dbg_set_sev_filter_req
+{
+ /// Bit field indicating the severity threshold for the traces
+ u32_l sev_filter;
+};
+
+/// Structure containing the parameters of the @ref DBG_GET_SYS_STAT_CFM message.
+struct dbg_get_sys_stat_cfm
+{
+ /// Time spent in CPU sleep since last reset of the system statistics
+ u32_l cpu_sleep_time;
+ /// Time spent in DOZE since last reset of the system statistics
+ u32_l doze_time;
+ /// Total time spent since last reset of the system statistics
+ u32_l stats_time;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+/////////// For TDLS messages
+///////////////////////////////////////////////////////////////////////////////
+
+/// List of messages related to the task.
+enum tdls_msg_tag
+{
+ /// TDLS channel Switch Request.
+ TDLS_CHAN_SWITCH_REQ = LMAC_FIRST_MSG(TASK_TDLS),
+ /// TDLS channel switch confirmation.
+ TDLS_CHAN_SWITCH_CFM,
+ /// TDLS channel switch indication.
+ TDLS_CHAN_SWITCH_IND,
+ /// TDLS channel switch to base channel indication.
+ TDLS_CHAN_SWITCH_BASE_IND,
+ /// TDLS cancel channel switch request.
+ TDLS_CANCEL_CHAN_SWITCH_REQ,
+ /// TDLS cancel channel switch confirmation.
+ TDLS_CANCEL_CHAN_SWITCH_CFM,
+ /// TDLS peer power save indication.
+ TDLS_PEER_PS_IND,
+ /// TDLS peer traffic indication request.
+ TDLS_PEER_TRAFFIC_IND_REQ,
+ /// TDLS peer traffic indication confirmation.
+ TDLS_PEER_TRAFFIC_IND_CFM,
+ /// MAX number of messages
+ TDLS_MAX
+};
+
+/// Structure containing the parameters of the @ref TDLS_CHAN_SWITCH_REQ message
+struct tdls_chan_switch_req
+{
+ /// Index of the VIF
+ u8_l vif_index;
+ /// STA Index
+ u8_l sta_idx;
+ /// MAC address of the TDLS station
+ struct mac_addr peer_mac_addr;
+ /// Flag to indicate if the TDLS peer is the TDLS link initiator
+ bool_l initiator;
+ /// Channel configuration
+ struct mac_chan_op chan;
+ /// Operating class
+ u8_l op_class;
+};
+
+/// Structure containing the parameters of the @ref TDLS_CANCEL_CHAN_SWITCH_REQ message
+struct tdls_cancel_chan_switch_req
+{
+ /// Index of the VIF
+ u8_l vif_index;
+ /// STA Index
+ u8_l sta_idx;
+ /// MAC address of the TDLS station
+ struct mac_addr peer_mac_addr;
+};
+
+
+/// Structure containing the parameters of the @ref TDLS_CHAN_SWITCH_CFM message
+struct tdls_chan_switch_cfm
+{
+ /// Status of the operation
+ u8_l status;
+};
+
+/// Structure containing the parameters of the @ref TDLS_CANCEL_CHAN_SWITCH_CFM message
+struct tdls_cancel_chan_switch_cfm
+{
+ /// Status of the operation
+ u8_l status;
+};
+
+/// Structure containing the parameters of the @ref TDLS_CHAN_SWITCH_IND message
+struct tdls_chan_switch_ind
+{
+ /// VIF Index
+ u8_l vif_index;
+ /// Channel Context Index
+ u8_l chan_ctxt_index;
+ /// Status of the operation
+ u8_l status;
+};
+
+/// Structure containing the parameters of the @ref TDLS_CHAN_SWITCH_BASE_IND message
+struct tdls_chan_switch_base_ind
+{
+ /// VIF Index
+ u8_l vif_index;
+ /// Channel Context index
+ u8_l chan_ctxt_index;
+};
+
+/// Structure containing the parameters of the @ref TDLS_PEER_PS_IND message
+struct tdls_peer_ps_ind
+{
+ /// VIF Index
+ u8_l vif_index;
+ /// STA Index
+ u8_l sta_idx;
+ /// MAC ADDR of the TDLS STA
+ struct mac_addr peer_mac_addr;
+ /// Flag to indicate if the TDLS peer is going to sleep
+ bool ps_on;
+};
+
+/// Structure containing the parameters of the @ref TDLS_PEER_TRAFFIC_IND_REQ message
+struct tdls_peer_traffic_ind_req
+{
+ /// VIF Index
+ u8_l vif_index;
+ /// STA Index
+ u8_l sta_idx;
+ // MAC ADDR of the TDLS STA
+ struct mac_addr peer_mac_addr;
+ /// Dialog token
+ u8_l dialog_token;
+ /// TID of the latest MPDU transmitted over the TDLS direct link to the TDLS STA
+ u8_l last_tid;
+ /// Sequence number of the latest MPDU transmitted over the TDLS direct link
+ /// to the TDLS STA
+ u16_l last_sn;
+};
+
+/// Structure containing the parameters of the @ref TDLS_PEER_TRAFFIC_IND_CFM message
+struct tdls_peer_traffic_ind_cfm
+{
+ /// Status of the operation
+ u8_l status;
+};
+
+#if defined(CONFIG_ECRNX_P2P)
+enum p2p_listen_msg_tag
+{
+ /// Scan request from host.
+ P2P_LISTEN_START_REQ = LMAC_FIRST_MSG(TASK_P2P_LISTEN),
+ P2P_LISTEN_START_CFM,
+ P2P_CANCEL_LISTEN_REQ,
+ P2P_CANCEL_LISTEN_CFM,
+
+ P2P_LISTEN_MAX,
+};
+
+struct p2p_listen_start_req
+{
+ /// Index of the interface on which path has to be created
+ u8_l vif_idx;
+};
+
+struct p2p_listen_start_cfm
+{
+ /// p2p listen task status
+ bool_l status;
+ /// VIF Index
+ u8_l vif_idx;
+};
+
+
+struct p2p_cancel_listen_req
+{
+ /// Index of the interface on which path has to be created
+ u8_l vif_idx;
+};
+
+struct p2p_cancel_listen_cfm
+{
+ /// p2p listen task status
+ bool_l status;
+ /// VIF Index
+ u8_l vif_idx;
+};
+#endif
+
+#endif // LMAC_MSG_H_
diff --git a/drivers/net/wireless/eswin/lmac_types.h b/drivers/net/wireless/eswin/lmac_types.h
new file mode 100644
index 000000000000..b8dcd2b1d9f9
--- /dev/null
+++ b/drivers/net/wireless/eswin/lmac_types.h
@@ -0,0 +1,69 @@
+/**
+ ****************************************************************************************
+ *
+ * @file co_types.h
+ *
+ * @brief This file replaces the need to include stdint or stdbool typical headers,
+ * which may not be available in all toolchains, and adds new types
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ * $Rev: $
+ *
+ ****************************************************************************************
+ */
+
+#ifndef _LMAC_INT_H_
+#define _LMAC_INT_H_
+
+
+/**
+ ****************************************************************************************
+ * @addtogroup CO_INT
+ * @ingroup COMMON
+ * @brief Common integer standard types (removes use of stdint)
+ *
+ * @{
+ ****************************************************************************************
+ */
+
+
+/*
+ * DEFINES
+ ****************************************************************************************
+ */
+
+#include <linux/version.h>
+#include <linux/types.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)
+#include <linux/bits.h>
+#else
+#include <linux/bitops.h>
+#endif
+
+#ifdef CONFIG_ECRNX_TL4
+typedef uint16_t u8_l;
+typedef int16_t s8_l;
+typedef uint16_t bool_l;
+#else
+typedef uint8_t u8_l;
+typedef int8_t s8_l;
+typedef bool bool_l;
+#endif
+typedef uint16_t u16_l;
+typedef int16_t s16_l;
+typedef uint32_t u32_l;
+typedef int32_t s32_l;
+typedef uint64_t u64_l;
+
+#define ALIGNED(n) __attribute__((aligned (n)))
+
+#if (__SIZEOF_POINTER__ == 8)
+#define ptr_addr u64_l
+#else
+#define ptr_addr u32_l
+#endif
+
+
+/// @} CO_INT
+#endif // _LMAC_INT_H_
diff --git a/drivers/net/wireless/eswin/reg_access.h b/drivers/net/wireless/eswin/reg_access.h
new file mode 100644
index 000000000000..16a02eb3ba29
--- /dev/null
+++ b/drivers/net/wireless/eswin/reg_access.h
@@ -0,0 +1,156 @@
+/**
+ ******************************************************************************
+ *
+ * @file reg_access.h
+ *
+ * @brief Definitions and macros for MAC HW and platform register accesses
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+
+#ifndef REG_ACCESS_H_
+#define REG_ACCESS_H_
+
+/*****************************************************************************
+ * Addresses within ECRNX_ADDR_CPU
+ *****************************************************************************/
+#define RAM_LMAC_FW_ADDR 0x00000000
+
+/*****************************************************************************
+ * Addresses within ECRNX_ADDR_SYSTEM
+ *****************************************************************************/
+/* Shard RAM */
+#define SHARED_RAM_START_ADDR 0x00000000
+
+/* IPC registers */
+#define IPC_REG_BASE_ADDR 0x00800000
+
+/* System Controller Registers */
+#define SYSCTRL_SIGNATURE_ADDR 0x00900000
+// old diag register name
+#define SYSCTRL_DIAG_CONF_ADDR 0x00900068
+#define SYSCTRL_PHYDIAG_CONF_ADDR 0x00900074
+#define SYSCTRL_RIUDIAG_CONF_ADDR 0x00900078
+// new diag register name
+#define SYSCTRL_DIAG_CONF0 0x00900064
+#define SYSCTRL_DIAG_CONF1 0x00900068
+#define SYSCTRL_DIAG_CONF2 0x00900074
+#define SYSCTRL_DIAG_CONF3 0x00900078
+#define SYSCTRL_MISC_CNTL_ADDR 0x009000E0
+#define BOOTROM_ENABLE BIT(4)
+#define FPGA_B_RESET BIT(1)
+#define SOFT_RESET BIT(0)
+
+/* MAC platform */
+#define NXMAC_VERSION_1_ADDR 0x00B00004
+#define NXMAC_MU_MIMO_TX_BIT BIT(19)
+#define NXMAC_BFMER_BIT BIT(18)
+#define NXMAC_BFMEE_BIT BIT(17)
+#define NXMAC_MAC_80211MH_FORMAT_BIT BIT(16)
+#define NXMAC_COEX_BIT BIT(14)
+#define NXMAC_WAPI_BIT BIT(13)
+#define NXMAC_TPC_BIT BIT(12)
+#define NXMAC_VHT_BIT BIT(11)
+#define NXMAC_HT_BIT BIT(10)
+#define NXMAC_GCMP_BIT BIT(9)
+#define NXMAC_RCE_BIT BIT(8)
+#define NXMAC_CCMP_BIT BIT(7)
+#define NXMAC_TKIP_BIT BIT(6)
+#define NXMAC_WEP_BIT BIT(5)
+#define NXMAC_SECURITY_BIT BIT(4)
+#define NXMAC_SME_BIT BIT(3)
+#define NXMAC_HCCA_BIT BIT(2)
+#define NXMAC_EDCA_BIT BIT(1)
+#define NXMAC_QOS_BIT BIT(0)
+
+#define NXMAC_RX_CNTRL_ADDR 0x00B00060
+#define NXMAC_EN_DUPLICATE_DETECTION_BIT BIT(31)
+#define NXMAC_ACCEPT_UNKNOWN_BIT BIT(30)
+#define NXMAC_ACCEPT_OTHER_DATA_FRAMES_BIT BIT(29)
+#define NXMAC_ACCEPT_QO_S_NULL_BIT BIT(28)
+#define NXMAC_ACCEPT_QCFWO_DATA_BIT BIT(27)
+#define NXMAC_ACCEPT_Q_DATA_BIT BIT(26)
+#define NXMAC_ACCEPT_CFWO_DATA_BIT BIT(25)
+#define NXMAC_ACCEPT_DATA_BIT BIT(24)
+#define NXMAC_ACCEPT_OTHER_CNTRL_FRAMES_BIT BIT(23)
+#define NXMAC_ACCEPT_CF_END_BIT BIT(22)
+#define NXMAC_ACCEPT_ACK_BIT BIT(21)
+#define NXMAC_ACCEPT_CTS_BIT BIT(20)
+#define NXMAC_ACCEPT_RTS_BIT BIT(19)
+#define NXMAC_ACCEPT_PS_POLL_BIT BIT(18)
+#define NXMAC_ACCEPT_BA_BIT BIT(17)
+#define NXMAC_ACCEPT_BAR_BIT BIT(16)
+#define NXMAC_ACCEPT_OTHER_MGMT_FRAMES_BIT BIT(15)
+#define NXMAC_ACCEPT_BFMEE_FRAMES_BIT BIT(14)
+#define NXMAC_ACCEPT_ALL_BEACON_BIT BIT(13)
+#define NXMAC_ACCEPT_NOT_EXPECTED_BA_BIT BIT(12)
+#define NXMAC_ACCEPT_DECRYPT_ERROR_FRAMES_BIT BIT(11)
+#define NXMAC_ACCEPT_BEACON_BIT BIT(10)
+#define NXMAC_ACCEPT_PROBE_RESP_BIT BIT(9)
+#define NXMAC_ACCEPT_PROBE_REQ_BIT BIT(8)
+#define NXMAC_ACCEPT_MY_UNICAST_BIT BIT(7)
+#define NXMAC_ACCEPT_UNICAST_BIT BIT(6)
+#define NXMAC_ACCEPT_ERROR_FRAMES_BIT BIT(5)
+#define NXMAC_ACCEPT_OTHER_BSSID_BIT BIT(4)
+#define NXMAC_ACCEPT_BROADCAST_BIT BIT(3)
+#define NXMAC_ACCEPT_MULTICAST_BIT BIT(2)
+#define NXMAC_DONT_DECRYPT_BIT BIT(1)
+#define NXMAC_EXC_UNENCRYPTED_BIT BIT(0)
+
+#define NXMAC_DEBUG_PORT_SEL_ADDR 0x00B00510
+#define NXMAC_SW_SET_PROFILING_ADDR 0x00B08564
+#define NXMAC_SW_CLEAR_PROFILING_ADDR 0x00B08568
+
+/* Modem Status */
+#define MDM_HDMCONFIG_ADDR 0x00C00000
+#define MDM_HDMVERSION_ADDR 0x00C0003C
+
+/* Clock gating configuration */
+#define MDM_MEMCLKCTRL0_ADDR 0x00C00848
+#define MDM_CLKGATEFCTRL0_ADDR 0x00C00874
+#define CRM_CLKGATEFCTRL0_ADDR 0x00940010
+
+/* AGC (trident) */
+#define AGC_ECRNXAGCCNTL_ADDR 0x00C02060
+
+/* LDPC RAM*/
+#define PHY_LDPC_RAM_ADDR 0x00C09000
+
+/* FCU (elma )*/
+#define FCU_ECRNXFCAGCCNTL_ADDR 0x00C09034
+
+/* AGC RAM */
+#define PHY_AGC_UCODE_ADDR 0x00C0A000
+
+/* RIU */
+#define RIU_ECRNXVERSION_ADDR 0x00C0B000
+#define RIU_ECRNXDYNAMICCONFIG_ADDR 0x00C0B008
+#define RIU_AGCMEMBISTSTAT_ADDR 0x00C0B238
+#define RIU_AGCMEMSIGNATURESTAT_ADDR 0x00C0B23C
+#define RIU_ECRNXAGCCNTL_ADDR 0x00C0B390
+
+/* FCU RAM */
+#define RC_SYSTEM_CONFIGURATION_ADDR 0x00C0C000
+#define RC_ACCES_TO_CATAXIA_REG_ADDR 0x00C0C004
+
+/* RF ITF */
+#define FPGAB_MPIF_SEL_ADDR 0x00C10030
+#define RF_V6_DIAGPORT_CONF1_ADDR 0x00C10010
+#define RF_v6_PHYDIAG_CONF1_ADDR 0x00C10018
+
+#define RF_V7_DIAGPORT_CONF1_ADDR 0x00F10010
+#define RF_v7_PHYDIAG_CONF1_ADDR 0x00F10018
+
+/*****************************************************************************
+ * Macros for generated register files
+ *****************************************************************************/
+/* Macros for IPC registers access (used in reg_ipc_app.h) */
+#define REG_IPC_APP_RD(env, INDEX) \
+ (*(volatile u32*)((u8*)env + IPC_REG_BASE_ADDR + 4*(INDEX)))
+
+#define REG_IPC_APP_WR(env, INDEX, value) \
+ (*(volatile u32*)((u8*)env + IPC_REG_BASE_ADDR + 4*(INDEX)) = value)
+
+#endif /* REG_ACCESS_H_ */
diff --git a/drivers/net/wireless/eswin/reg_ipc_app.h b/drivers/net/wireless/eswin/reg_ipc_app.h
new file mode 100644
index 000000000000..9be4151a7c28
--- /dev/null
+++ b/drivers/net/wireless/eswin/reg_ipc_app.h
@@ -0,0 +1,299 @@
+/**
+ ****************************************************************************************
+ *
+ * @file ecrnx_ipc_app.h
+ *
+ * @brief IPC module register definitions
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ****************************************************************************************
+ */
+
+#ifndef _REG_IPC_APP_H_
+#define _REG_IPC_APP_H_
+
+#ifndef __KERNEL__
+#include <stdint.h>
+#include "arch.h"
+#else
+#include "ipc_compat.h"
+#endif
+#include "reg_access.h"
+
+#define REG_IPC_APP_DECODING_MASK 0x0000007F
+
+/**
+ * @brief APP2EMB_TRIGGER register definition
+ * <pre>
+ * Bits Field Name Reset Value
+ * ----- ------------------ -----------
+ * 31:00 APP2EMB_TRIGGER 0x0
+ * </pre>
+ */
+#define IPC_APP2EMB_TRIGGER_ADDR 0x12000000
+#define IPC_APP2EMB_TRIGGER_OFFSET 0x00000000
+#define IPC_APP2EMB_TRIGGER_INDEX 0x00000000
+#define IPC_APP2EMB_TRIGGER_RESET 0x00000000
+
+__INLINE u32 ipc_app2emb_trigger_get(void *env)
+{
+ return REG_IPC_APP_RD(env, IPC_APP2EMB_TRIGGER_INDEX);
+}
+
+__INLINE void ipc_app2emb_trigger_set(void *env, u32 value)
+{
+ REG_IPC_APP_WR(env, IPC_APP2EMB_TRIGGER_INDEX, value);
+}
+
+// field definitions
+#define IPC_APP2EMB_TRIGGER_MASK ((u32)0xFFFFFFFF)
+#define IPC_APP2EMB_TRIGGER_LSB 0
+#define IPC_APP2EMB_TRIGGER_WIDTH ((u32)0x00000020)
+
+#define IPC_APP2EMB_TRIGGER_RST 0x0
+
+__INLINE u32 ipc_app2emb_trigger_getf(void *env)
+{
+ u32 localVal = REG_IPC_APP_RD(env, IPC_APP2EMB_TRIGGER_INDEX);
+ ASSERT_ERR((localVal & ~((u32)0xFFFFFFFF)) == 0);
+ return (localVal >> 0);
+}
+
+__INLINE void ipc_app2emb_trigger_setf(void *env, u32 app2embtrigger)
+{
+ ASSERT_ERR((((u32)app2embtrigger << 0) & ~((u32)0xFFFFFFFF)) == 0);
+ REG_IPC_APP_WR(env, IPC_APP2EMB_TRIGGER_INDEX, (u32)app2embtrigger << 0);
+}
+
+/**
+ * @brief EMB2APP_RAWSTATUS register definition
+ * <pre>
+ * Bits Field Name Reset Value
+ * ----- ------------------ -----------
+ * 31:00 EMB2APP_RAWSTATUS 0x0
+ * </pre>
+ */
+#define IPC_EMB2APP_RAWSTATUS_ADDR 0x12000004
+#define IPC_EMB2APP_RAWSTATUS_OFFSET 0x00000004
+#define IPC_EMB2APP_RAWSTATUS_INDEX 0x00000001
+#define IPC_EMB2APP_RAWSTATUS_RESET 0x00000000
+
+__INLINE u32 ipc_emb2app_rawstatus_get(void *env)
+{
+ return REG_IPC_APP_RD(env, IPC_EMB2APP_RAWSTATUS_INDEX);
+}
+
+__INLINE void ipc_emb2app_rawstatus_set(void *env, u32 value)
+{
+ REG_IPC_APP_WR(env, IPC_EMB2APP_RAWSTATUS_INDEX, value);
+}
+
+// field definitions
+#define IPC_EMB2APP_RAWSTATUS_MASK ((u32)0xFFFFFFFF)
+#define IPC_EMB2APP_RAWSTATUS_LSB 0
+#define IPC_EMB2APP_RAWSTATUS_WIDTH ((u32)0x00000020)
+
+#define IPC_EMB2APP_RAWSTATUS_RST 0x0
+
+__INLINE u32 ipc_emb2app_rawstatus_getf(void *env)
+{
+ u32 localVal = REG_IPC_APP_RD(env, IPC_EMB2APP_RAWSTATUS_INDEX);
+ ASSERT_ERR((localVal & ~((u32)0xFFFFFFFF)) == 0);
+ return (localVal >> 0);
+}
+
+/**
+ * @brief EMB2APP_ACK register definition
+ * <pre>
+ * Bits Field Name Reset Value
+ * ----- ------------------ -----------
+ * 31:00 EMB2APP_ACK 0x0
+ * </pre>
+ */
+#define IPC_EMB2APP_ACK_ADDR 0x12000008
+#define IPC_EMB2APP_ACK_OFFSET 0x00000008
+#define IPC_EMB2APP_ACK_INDEX 0x00000002
+#define IPC_EMB2APP_ACK_RESET 0x00000000
+
+__INLINE u32 ipc_emb2app_ack_get(void *env)
+{
+ return REG_IPC_APP_RD(env, IPC_EMB2APP_ACK_INDEX);
+}
+
+__INLINE void ipc_emb2app_ack_clear(void *env, u32 value)
+{
+ REG_IPC_APP_WR(env, IPC_EMB2APP_ACK_INDEX, value);
+}
+
+// field definitions
+#define IPC_EMB2APP_ACK_MASK ((u32)0xFFFFFFFF)
+#define IPC_EMB2APP_ACK_LSB 0
+#define IPC_EMB2APP_ACK_WIDTH ((u32)0x00000020)
+
+#define IPC_EMB2APP_ACK_RST 0x0
+
+__INLINE u32 ipc_emb2app_ack_getf(void *env)
+{
+ u32 localVal = REG_IPC_APP_RD(env, IPC_EMB2APP_ACK_INDEX);
+ ASSERT_ERR((localVal & ~((u32)0xFFFFFFFF)) == 0);
+ return (localVal >> 0);
+}
+
+__INLINE void ipc_emb2app_ack_clearf(void *env, u32 emb2appack)
+{
+ ASSERT_ERR((((u32)emb2appack << 0) & ~((u32)0xFFFFFFFF)) == 0);
+ REG_IPC_APP_WR(env, IPC_EMB2APP_ACK_INDEX, (u32)emb2appack << 0);
+}
+
+/**
+ * @brief EMB2APP_UNMASK_SET register definition
+ * <pre>
+ * Bits Field Name Reset Value
+ * ----- ------------------ -----------
+ * 31:00 EMB2APP_UNMASK 0x0
+ * </pre>
+ */
+#define IPC_EMB2APP_UNMASK_SET_ADDR 0x1200000C
+#define IPC_EMB2APP_UNMASK_SET_OFFSET 0x0000000C
+#define IPC_EMB2APP_UNMASK_SET_INDEX 0x00000003
+#define IPC_EMB2APP_UNMASK_SET_RESET 0x00000000
+
+__INLINE u32 ipc_emb2app_unmask_get(void *env)
+{
+ return REG_IPC_APP_RD(env, IPC_EMB2APP_UNMASK_SET_INDEX);
+}
+
+__INLINE void ipc_emb2app_unmask_set(void *env, u32 value)
+{
+ REG_IPC_APP_WR(env, IPC_EMB2APP_UNMASK_SET_INDEX, value);
+}
+
+// field definitions
+#define IPC_EMB2APP_UNMASK_MASK ((u32)0xFFFFFFFF)
+#define IPC_EMB2APP_UNMASK_LSB 0
+#define IPC_EMB2APP_UNMASK_WIDTH ((u32)0x00000020)
+
+#define IPC_EMB2APP_UNMASK_RST 0x0
+
+__INLINE u32 ipc_emb2app_unmask_getf(void *env)
+{
+ u32 localVal = REG_IPC_APP_RD(env, IPC_EMB2APP_UNMASK_SET_INDEX);
+ ASSERT_ERR((localVal & ~((u32)0xFFFFFFFF)) == 0);
+ return (localVal >> 0);
+}
+
+__INLINE void ipc_emb2app_unmask_setf(void *env, u32 emb2appunmask)
+{
+ ASSERT_ERR((((u32)emb2appunmask << 0) & ~((u32)0xFFFFFFFF)) == 0);
+ REG_IPC_APP_WR(env, IPC_EMB2APP_UNMASK_SET_INDEX, (u32)emb2appunmask << 0);
+}
+
+/**
+ * @brief EMB2APP_UNMASK_CLEAR register definition
+ * <pre>
+ * Bits Field Name Reset Value
+ * ----- ------------------ -----------
+ * 31:00 EMB2APP_UNMASK 0x0
+ * </pre>
+ */
+#define IPC_EMB2APP_UNMASK_CLEAR_ADDR 0x12000010
+#define IPC_EMB2APP_UNMASK_CLEAR_OFFSET 0x00000010
+#define IPC_EMB2APP_UNMASK_CLEAR_INDEX 0x00000004
+#define IPC_EMB2APP_UNMASK_CLEAR_RESET 0x00000000
+
+__INLINE void ipc_emb2app_unmask_clear(void *env, u32 value)
+{
+ REG_IPC_APP_WR(env, IPC_EMB2APP_UNMASK_CLEAR_INDEX, value);
+}
+
+// fields defined in symmetrical set/clear register
+__INLINE void ipc_emb2app_unmask_clearf(void *env, u32 emb2appunmask)
+{
+ ASSERT_ERR((((u32)emb2appunmask << 0) & ~((u32)0xFFFFFFFF)) == 0);
+ REG_IPC_APP_WR(env, IPC_EMB2APP_UNMASK_CLEAR_INDEX, (u32)emb2appunmask << 0);
+}
+
+/**
+ * @brief EMB2APP_STATUS register definition
+ * <pre>
+ * Bits Field Name Reset Value
+ * ----- ------------------ -----------
+ * 31:00 EMB2APP_STATUS 0x0
+ * </pre>
+ */
+#ifdef CONFIG_ECRNX_OLD_IPC
+#define IPC_EMB2APP_STATUS_ADDR 0x12000014
+#define IPC_EMB2APP_STATUS_OFFSET 0x00000014
+#define IPC_EMB2APP_STATUS_INDEX 0x00000005
+#else
+#define IPC_EMB2APP_STATUS_ADDR 0x1200001C
+#define IPC_EMB2APP_STATUS_OFFSET 0x0000001C
+#define IPC_EMB2APP_STATUS_INDEX 0x00000007
+#endif
+#define IPC_EMB2APP_STATUS_RESET 0x00000000
+
+__INLINE u32 ipc_emb2app_status_get(void *env)
+{
+ return REG_IPC_APP_RD(env, IPC_EMB2APP_STATUS_INDEX);
+}
+
+__INLINE void ipc_emb2app_status_set(void *env, u32 value)
+{
+ REG_IPC_APP_WR(env, IPC_EMB2APP_STATUS_INDEX, value);
+}
+
+// field definitions
+#define IPC_EMB2APP_STATUS_MASK ((u32)0xFFFFFFFF)
+#define IPC_EMB2APP_STATUS_LSB 0
+#define IPC_EMB2APP_STATUS_WIDTH ((u32)0x00000020)
+
+#define IPC_EMB2APP_STATUS_RST 0x0
+
+__INLINE u32 ipc_emb2app_status_getf(void *env)
+{
+ u32 localVal = REG_IPC_APP_RD(env, IPC_EMB2APP_STATUS_INDEX);
+ ASSERT_ERR((localVal & ~((u32)0xFFFFFFFF)) == 0);
+ return (localVal >> 0);
+}
+
+/**
+ * @brief APP_SIGNATURE register definition
+ * <pre>
+ * Bits Field Name Reset Value
+ * ----- ------------------ ----------
+ * 31:00 APP_SIGNATURE 0x0
+ * </pre>
+ */
+#define IPC_APP_SIGNATURE_ADDR 0x12000040
+#define IPC_APP_SIGNATURE_OFFSET 0x00000040
+#define IPC_APP_SIGNATURE_INDEX 0x00000010
+#define IPC_APP_SIGNATURE_RESET 0x00000000
+
+__INLINE u32 ipc_app_signature_get(void *env)
+{
+ return REG_IPC_APP_RD(env, IPC_APP_SIGNATURE_INDEX);
+}
+
+__INLINE void ipc_app_signature_set(void *env, u32 value)
+{
+ REG_IPC_APP_WR(env, IPC_APP_SIGNATURE_INDEX, value);
+}
+
+// field definitions
+#define IPC_APP_SIGNATURE_MASK ((u32)0xFFFFFFFF)
+#define IPC_APP_SIGNATURE_LSB 0
+#define IPC_APP_SIGNATURE_WIDTH ((u32)0x00000020)
+
+#define IPC_APP_SIGNATURE_RST 0x0
+
+__INLINE u32 ipc_app_signature_getf(void *env)
+{
+ u32 localVal = REG_IPC_APP_RD(env, IPC_APP_SIGNATURE_INDEX);
+ ASSERT_ERR((localVal & ~((u32)0xFFFFFFFF)) == 0);
+ return (localVal >> 0);
+}
+
+
+#endif // _REG_IPC_APP_H_
+
diff --git a/drivers/net/wireless/eswin/sdio/core.c b/drivers/net/wireless/eswin/sdio/core.c
new file mode 100644
index 000000000000..603b7be766dd
--- /dev/null
+++ b/drivers/net/wireless/eswin/sdio/core.c
@@ -0,0 +1,774 @@
+/**
+ ******************************************************************************
+ *
+ * @file core.c
+ *
+ * @brief sdio core function definitions
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+#include <linux/firmware.h>
+#include <linux/kthread.h>
+#include "core.h"
+#include "fw.h"
+#include <uapi/linux/sched/types.h>
+//#include "debug.h"
+#include "ecrnx_platform.h"
+#include "sdio.h"
+#include "ecrnx_rx.h"
+#include "sdio_host_interface.h"
+#include "eswin_utils.h"
+
+bool loopback;
+module_param(loopback, bool, S_IRUSR | S_IWUSR);
+MODULE_PARM_DESC(loopback, "HIF loopback");
+
+int power_save;
+module_param(power_save, int, S_IRUSR | S_IWUSR);
+MODULE_PARM_DESC(power_save, "Power Save(0: disable, 1:enable)");
+
+int disable_cqm = 0;
+module_param(disable_cqm, int, S_IRUSR | S_IWUSR);
+MODULE_PARM_DESC(disable_cqm, "Disable CQM (0: disable, 1:enable)");
+
+
+int listen_interval = 0;
+module_param(listen_interval, int, S_IRUSR | S_IWUSR);
+MODULE_PARM_DESC(listen_interval, "Listen Interval");
+
+int bss_max_idle = 0;
+module_param(bss_max_idle, int, S_IRUSR | S_IWUSR);
+MODULE_PARM_DESC(bss_max_idle, "BSS Max Idle");
+
+
+bool dl_fw;
+module_param(dl_fw, bool, S_IRUSR | S_IWUSR);
+MODULE_PARM_DESC(dl_fw, "download firmware");
+
+
+#ifdef CONFIG_ECRNX_WIFO_CAIL
+bool amt_mode;
+module_param(amt_mode, bool, S_IRUSR | S_IWUSR);
+MODULE_PARM_DESC(amt_mode, "calibrate mode");
+#endif
+
+bool set_gain;
+module_param(set_gain, bool, S_IRUSR | S_IWUSR);
+MODULE_PARM_DESC(set_gain, "set gain delta");
+
+char *fw_name;
+struct eswin *pEswin;
+
+module_param(fw_name, charp, S_IRUGO);
+MODULE_PARM_DESC(fw_name, "Firmware file name");
+
+#if 0
+static void eswin_fw_ready(struct sk_buff *skb, struct eswin * tr)
+{
+ struct ieee80211_hw *hw = tr->hw;
+ struct wim_ready *ready;
+ struct wim *wim = (struct wim *)skb->data;
+
+ ECRNX_PRINT(" %s entry!!", __func__);
+ ready = (struct wim_ready *) (wim + 1);
+
+ ECRNX_PRINT(" %s -- version: 0x%x", __func__, ready->v.version);
+ ECRNX_PRINT(" %s -- rx_head_size: %d", __func__, ready->v.rx_head_size);
+ ECRNX_PRINT(" %s -- tx_head_size: %d", __func__, ready->v.tx_head_size);
+ ECRNX_PRINT(" %s -- buffer_size: %d", __func__, ready->v.buffer_size);
+
+ tr->fwinfo.ready = 2;
+ tr->fwinfo.version = ready->v.version;
+ tr->fwinfo.rx_head_size = ready->v.rx_head_size;
+ tr->fwinfo.tx_head_size = ready->v.tx_head_size;
+ tr->fwinfo.payload_align = ready->v.payload_align;
+ tr->fwinfo.buffer_size = ready->v.buffer_size;
+
+ ECRNX_PRINT(" %s -- cap_mask: 0x%llx", __func__, ready->v.cap.cap);
+ ECRNX_PRINT(" %s -- cap_li: %d, %d", __func__, ready->v.cap.listen_interval, listen_interval);
+ ECRNX_PRINT(" %s -- cap_idle: %d, %d", __func__, ready->v.cap.bss_max_idle, bss_max_idle);
+
+ tr->cap.cap_mask = ready->v.cap.cap;
+ tr->cap.listen_interval = ready->v.cap.listen_interval;
+ tr->cap.bss_max_idle = ready->v.cap.bss_max_idle;
+
+ if (listen_interval) {
+ hw->max_listen_interval = listen_interval;
+ tr->cap.listen_interval = listen_interval;
+ }
+
+ if (bss_max_idle) {
+ tr->cap.bss_max_idle = bss_max_idle;
+ }
+
+ dev_kfree_skb(skb);
+ ECRNX_PRINT(" %s exit!!", __func__);
+}
+#endif
+static unsigned int sdio_tx_packets = 0;
+static struct timer_list sdio_tx_timer = {0};
+
+#define SDIO_TX_TIMER_TIMEOUT_US (200)
+
+void sdio_tx_queue_init(struct tx_buff_queue * queue)
+{
+ queue->head = NULL;
+ queue->tail = NULL;
+ queue->count = 0;
+ spin_lock_init(&queue->lock);
+}
+
+void sdio_tx_queue_push(struct tx_buff_queue * queue, struct tx_buff_node *node)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&queue->lock, flags);
+ if (queue->head) {
+ queue->tail->next = node;
+ } else {
+ queue->head = node;
+ }
+
+ queue->tail = node;;
+ queue->tail->next = NULL;
+ queue->count++;
+
+ spin_unlock_irqrestore(&queue->lock, flags);
+
+ //ECRNX_PRINT(" queue push count: %d\n", queue->count);
+ //ECRNX_PRINT(" queue push head: %#x\n", queue->head);
+}
+
+struct tx_buff_node *sdio_tx_queue_pop(struct tx_buff_queue *queue)
+{
+ unsigned long flags;
+ struct tx_buff_node *res = NULL;
+
+ //ECRNX_PRINT(" queue pop count: %d\n", queue->count);
+ //ECRNX_PRINT(" queue pop head: %#x\n", queue->head);
+
+ spin_lock_irqsave(&queue->lock, flags);
+
+ if (queue->count) {
+ res = queue->head;
+ queue->head = res->next;
+ res->next = NULL;
+ queue->count--;
+ }
+
+ spin_unlock_irqrestore(&queue->lock, flags);
+ return res;
+}
+
+struct tx_buff_node *sdio_tx_queue_peek(struct tx_buff_queue *queue)
+{
+ unsigned long flags;
+ struct tx_buff_node *res = NULL;
+
+ spin_lock_irqsave(&queue->lock, flags);
+
+ if (queue->count) {
+ res = queue->head;
+ }
+
+ spin_unlock_irqrestore(&queue->lock, flags);
+ return res;
+}
+
+struct tx_buff_node * sdio_tx_node_alloc(struct eswin *tr)
+{
+ struct tx_buff_node * res;
+ unsigned long flags;
+
+ spin_lock_irqsave(&tr->tx_lock,flags);
+ res = tr->tx_node_head;
+ if(res == NULL)
+ {
+ spin_unlock_irqrestore(&tr->tx_lock, flags);
+ return NULL;
+ }
+ tr->tx_node_head = tr->tx_node_head->next;
+ res->next = NULL;
+ tr->tx_node_num--;
+ spin_unlock_irqrestore(&tr->tx_lock, flags);
+
+ return res;
+}
+
+void sdio_tx_node_free(struct eswin *tr, struct tx_buff_node * node)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&tr->tx_lock,flags);
+ kfree(node->buff);
+ node->buff = NULL;
+ node->next = tr->tx_node_head;
+ tr->tx_node_head = node;
+ tr->tx_node_num++;
+ spin_unlock_irqrestore(&tr->tx_lock, flags);
+}
+
+void sdio_tx_pkg_queue_init(struct tx_buff_pkg_queue * queue)
+{
+ queue->head = NULL;
+ queue->tail = NULL;
+ queue->count = 0;
+ spin_lock_init(&queue->lock);
+}
+
+void sdio_tx_pkg_queue_push(struct tx_buff_pkg_queue * queue, struct tx_buff_pkg_node *node)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&queue->lock, flags);
+ if (queue->head) {
+ queue->tail->next = node;
+ } else {
+ queue->head = node;
+ }
+
+ queue->tail = node;;
+ queue->tail->next = NULL;
+ queue->count++;
+
+ spin_unlock_irqrestore(&queue->lock, flags);
+}
+
+struct tx_buff_pkg_node *sdio_tx_pkg_queue_pop(struct tx_buff_pkg_queue *queue)
+{
+ unsigned long flags;
+ struct tx_buff_pkg_node *res = NULL;
+
+ spin_lock_irqsave(&queue->lock, flags);
+
+ if (queue->count) {
+ res = queue->head;
+ queue->head = res->next;
+ res->next = NULL;
+ queue->count--;
+ }
+
+ spin_unlock_irqrestore(&queue->lock, flags);
+ return res;
+}
+
+struct tx_buff_pkg_node * sdio_tx_pkg_node_alloc(struct eswin *tr)
+{
+ struct tx_buff_pkg_node * res;
+ unsigned long flags;
+
+ spin_lock_irqsave(&tr->tx_pkg_lock,flags);
+ res = tr->tx_pkg_node_head;
+ if(res == NULL)
+ {
+ spin_unlock_irqrestore(&tr->tx_pkg_lock, flags);
+ return NULL;
+ }
+ tr->tx_pkg_node_head = tr->tx_pkg_node_head->next;
+ res->next = NULL;
+ tr->tx_pkg_node_num--;
+ spin_unlock_irqrestore(&tr->tx_pkg_lock, flags);
+
+ return res;
+}
+
+void sdio_tx_pkg_node_free(struct eswin *tr, struct tx_buff_pkg_node * node)
+{
+ unsigned long flags;
+ int i;
+ spin_lock_irqsave(&tr->tx_pkg_lock,flags);
+ if((node->flag & FLAG_MSG_TYPE_MASK) == TX_FLAG_TX_DESC)
+ {
+ kfree(node->buff);
+ }
+ node->buff = NULL;
+ node->next = tr->tx_pkg_node_head;
+ tr->tx_pkg_node_head = node;
+ tr->tx_pkg_node_num++;
+ /*
+ if (node->node_cnt > 1)
+ {
+ printk("%s,count :%d\n",__func__,node->node_cnt);
+ }
+ */
+
+ for (i = 0; i < node->node_cnt; ++i)
+ {
+ sdio_tx_node_free(tr, node->tx_node[i]);
+ }
+ spin_unlock_irqrestore(&tr->tx_pkg_lock, flags);
+}
+
+
+void eswin_sdio_register_rx_cb(struct eswin *tr, sdio_rx_cb_t cb)
+{
+ tr->rx_callback = cb;
+}
+
+extern int ecrnx_data_cfm_callback(void *priv, void *host_id);
+extern int ecrnx_msg_cfm_callback(void *priv, void *host_id);
+static void eswin_core_register_work(struct work_struct *work)
+{
+ //struct sk_buff *skb_resp;
+ int ret;
+ struct eswin *tr = container_of(work, struct eswin, register_work.work);
+
+ ECRNX_PRINT(" %s entry, dl_fw = %d!!", __func__, dl_fw);
+
+ if (dl_fw && eswin_fw_file_chech(tr)) {
+ eswin_fw_file_download(tr);
+ release_firmware(tr->fw);
+ dl_fw = false;
+ schedule_delayed_work(&tr->register_work, msecs_to_jiffies(1000));
+ return;
+ }
+
+#ifdef CONFIG_ECRNX_WIFO_CAIL
+ ECRNX_PRINT(" %s entry, amt_mode = %d!!", __func__, amt_mode);
+#endif
+
+ tr->rx_callback = ecrnx_rx_callback;
+ tr->data_cfm_callback = ecrnx_data_cfm_callback;
+ tr->msg_cfm_callback = ecrnx_msg_cfm_callback;
+
+ ret = ecrnx_platform_init(tr, &tr->umac_priv);
+ set_bit(ESWIN_FLAG_CORE_REGISTERED, &tr->dev_flags);
+ ECRNX_DBG("%s exit!!", __func__);
+
+ return;
+}
+
+int eswin_core_register(struct eswin *tr)
+{
+ ECRNX_PRINT("%s entry!!", __func__);
+ tr->ops->start(tr);
+
+ //schedule_delayed_work(&tr->register_work, msecs_to_jiffies(10));
+ schedule_delayed_work(&tr->register_work, msecs_to_jiffies(1));
+ ECRNX_PRINT("%s exit!!", __func__);
+ return 0;
+}
+
+void eswin_core_unregister(struct eswin *tr)
+{
+ struct eswin_sdio *tr_sdio = (struct eswin_sdio *)tr->drv_priv;
+ ECRNX_PRINT("%s entry!!", __func__);
+
+ cancel_delayed_work(&tr->register_work);
+
+ if (!test_bit(ESWIN_FLAG_CORE_REGISTERED, &tr->dev_flags))
+ return;
+
+ ecrnx_platform_deinit(tr->umac_priv);
+}
+
+static int eswin_sdio_tx_thread(void *data)
+{
+ struct eswin *tr = (struct eswin *)data;
+ struct tx_buff_pkg_node *node;
+ int i, ret = 0, cb_per = 0;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 9, 0)
+ struct sched_param param = { .sched_priority = 1 };
+ param.sched_priority = 56;
+ sched_setscheduler(get_current(), SCHED_FIFO, &param);
+#else
+ sched_set_fifo(get_current());
+#endif
+ ECRNX_PRINT("sdio pkg thread entry\n");
+
+ while (!kthread_should_stop())
+ {
+ ret = wait_event_interruptible(tr->wait_tx, tr->tx_pkg_queue.count != 0 || kthread_should_stop());
+ if (ret < 0)
+ {
+ ECRNX_ERR("sdio pkg thread error!\n");
+ return 0;
+ }
+ if(kthread_should_stop())
+ {
+ continue;
+ }
+ while (tr->tx_pkg_queue.count != 0)
+ {
+ node = sdio_tx_pkg_queue_pop(&tr->tx_pkg_queue);
+ if (!node) {
+ cb_per = 0;
+ wake_up_interruptible(&tr->wait_cb);
+ break;
+ }
+
+ if (tr->ops->xmit) {
+ ret = tr->ops->xmit(tr, node);
+ WARN_ON(ret < 0);
+ if((node->flag & FLAG_MSG_TYPE_MASK) == TX_FLAG_TX_DESC || (node->flag & FLAG_MSG_TYPE_MASK) == TX_FLAG_MSG_E)
+ {
+ sdio_tx_pkg_queue_push(&tr->tx_c_queue,node);
+ }
+ else
+ {
+ sdio_tx_pkg_node_free(tr, node);
+ }
+ cb_per++;
+ //if (cb_per % 4 == 0)
+ {
+ cb_per = 0;
+ wake_up_interruptible(&tr->wait_cb);
+ }
+ } else {
+ ECRNX_ERR(" eswin_sdio_work, ops->xmit is null\n");
+ }
+ }
+ }
+ ECRNX_PRINT("sdio tx thread exit\n");
+ return 0;
+}
+
+static int eswin_sdio_callback_thread(void *data)
+{
+ struct eswin *tr = (struct eswin *)data;
+ struct tx_buff_pkg_node *node;
+ int i, ret = 0;
+ struct txdesc_api *tx_desc;
+ ptr_addr host_id;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 9, 0)
+ struct sched_param param = { .sched_priority = 1 };
+ param.sched_priority = 56;
+ sched_setscheduler(get_current(), SCHED_FIFO, &param);
+#else
+ sched_set_fifo(get_current());
+#endif
+ ECRNX_PRINT("sdio callback thread entry\n");
+
+ while (!kthread_should_stop())
+ {
+ ret = wait_event_interruptible(tr->wait_cb, tr->tx_c_queue.count != 0 || kthread_should_stop());
+ if (ret < 0)
+ {
+ ECRNX_ERR("sdio callback thread error!\n");
+ return 0;
+ }
+ if(kthread_should_stop())
+ {
+ continue;
+ }
+ while (tr->tx_c_queue.count != 0)
+ {
+ node = sdio_tx_pkg_queue_pop(&tr->tx_c_queue);
+ if((node->flag & FLAG_MSG_TYPE_MASK) == TX_FLAG_TX_DESC && (tr->data_cfm_callback))
+ {
+ for (i = 0; i < node->node_cnt; ++i)
+ {
+ tx_desc = (struct txdesc_api *)node->tx_node[i]->buff;
+ if (tx_desc->host.flags & TXU_CNTRL_MGMT)
+ {
+ continue;
+ }
+ memcpy(&host_id, tx_desc->host.packet_addr, sizeof(ptr_addr));
+ tr->data_cfm_callback(tr->umac_priv, (void*)host_id);
+ }
+ }
+ //else if((node->flag & FLAG_MSG_TYPE_MASK) == TX_FLAG_MSG_E && (tr->msg_cfm_callback))
+ //{
+ // for (i = 0; i < node->node_cnt; ++i)
+ // {
+ // struct ecrnx_cmd_a2emsg *msg = (struct ecrnx_cmd_a2emsg *)node->tx_node[i]->buff;
+ // tr->msg_cfm_callback(tr->umac_priv, msg->hostid);
+ // }
+ //}
+ sdio_tx_pkg_node_free(tr, node);
+ }
+ }
+ ECRNX_PRINT("rx callback thread exit\n");
+ return 0;
+}
+
+static int eswin_sdio_tx_pkg_thread(void *data)
+{
+ struct eswin *tr = (struct eswin *)data;
+ struct tx_buff_node *node;
+ struct txdesc_api *tx_desc;
+ struct tx_buff_pkg_node * pkg_node = NULL;
+ struct tx_buff_pkg_head tx_pkg_head;
+ unsigned int offset = 0;
+ int i, ret, pkg_cnt = 0;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 9, 0)
+ struct sched_param param = { .sched_priority = 1 };
+ param.sched_priority = 56;
+ sched_setscheduler(get_current(), SCHED_FIFO, &param);
+#else
+ sched_set_fifo(get_current());
+#endif
+ ECRNX_PRINT("sdio tx pkg thread entry\n");
+
+ while (!kthread_should_stop())
+ {
+ ret = wait_event_interruptible(tr->wait_pkg, tr->tx_queue.count != 0 || kthread_should_stop());
+ if (ret < 0)
+ {
+ ECRNX_ERR("sdio tx pkg thread error!\n");
+ return 0;
+ }
+ if(kthread_should_stop())
+ {
+ continue;
+ }
+ while (tr->tx_queue.count != 0)
+ {
+ pkg_cnt = 0;
+ offset = 0;
+ memset(&tx_pkg_head,0,sizeof(tx_pkg_head));
+ pkg_node = sdio_tx_pkg_node_alloc(tr);
+ memset(pkg_node,0,sizeof(struct tx_buff_pkg_node));
+ if (!pkg_node) {
+ ECRNX_PRINT(" sdio pkg failed, no node!!\n");
+ break;
+ }
+ node = sdio_tx_queue_peek(&tr->tx_queue);
+ pkg_node->flag = node->flag;
+
+ if((node->flag & FLAG_MSG_TYPE_MASK) != TX_FLAG_TX_DESC)
+ {
+ node = sdio_tx_queue_pop(&tr->tx_queue);
+ pkg_node->buff = node->buff;
+ pkg_node->len = node->len;
+ pkg_node->tx_node[pkg_cnt] = node;
+ pkg_cnt++;
+ }
+ else
+ {
+ pkg_node->buff = (void *)kzalloc(ALIGN(SDIO_PKG_MAX_DATA*SDIO_PKG_MAX_CNT + sizeof(tx_pkg_head), 512), GFP_ATOMIC);
+ if(!pkg_node->buff){
+ ECRNX_PRINT("pkg_node buff malloc error! \n");
+ }
+ pkg_node->len = sizeof(tx_pkg_head);
+
+ while (tr->tx_queue.count)
+ {
+ offset = pkg_node->len;
+ node = sdio_tx_queue_peek(&tr->tx_queue);
+
+ if (((node->flag & FLAG_MSG_TYPE_MASK) != TX_FLAG_TX_DESC) || (pkg_cnt > (SDIO_PKG_MAX_CNT-1)))
+ {
+ break;
+ }
+ //ECRNX_DBG("tx count 2 %d,node %x",tr->tx_queue.count,node);
+ node = sdio_tx_queue_pop(&tr->tx_queue);
+ if(ALIGN(node->len, SDIO_PKG_PAD_GRN) < (SDIO_PKG_DIV_MSZ+1))
+ {
+ pkg_node->len += ALIGN(node->len, SDIO_PKG_PAD_GRN);
+ pkg_node->flag |= (ALIGN(node->len, SDIO_PKG_PAD_GRN)/SDIO_PKG_PAD_GRN) << (8+SDIO_PKG_BIT_SHIFT*pkg_cnt);
+ }
+ else
+ {
+ pkg_node->len += SDIO_PKG_MAX_DATA;
+ pkg_node->flag |= ((1 << SDIO_PKG_BIT_SHIFT) - 1) << (8+SDIO_PKG_BIT_SHIFT*pkg_cnt);
+ }
+ memcpy(pkg_node->buff + offset, node->buff, node->len);
+ pkg_node->tx_node[pkg_cnt] = node;
+ tx_pkg_head.len[pkg_cnt] = node->len;
+ pkg_cnt++;
+ }
+ pkg_node->len = ALIGN(pkg_node->len, 512);
+ memcpy(pkg_node->buff, &tx_pkg_head, sizeof(tx_pkg_head));
+ }
+ pkg_node->node_cnt = pkg_cnt;
+ sdio_tx_pkg_queue_push(&tr->tx_pkg_queue, pkg_node);
+ wake_up_interruptible(&tr->wait_tx);
+ }
+ }
+ ECRNX_PRINT("tx pkg thread exit\n");
+ return 0;
+}
+
+void eswin_sdio_ops_init(struct eswin * tr, const struct sdio_ops * ops)
+{
+ int i;
+
+ tr->ops = ops;
+
+ sdio_tx_queue_init(&tr->tx_queue);
+ sdio_tx_pkg_queue_init(&tr->tx_c_queue);
+ sdio_tx_pkg_queue_init(&tr->tx_pkg_queue);
+
+ for (i=1; i<ESWIN_TX_NODE_CNT; i++) {
+ tr->tx_node[i-1].next = &tr->tx_node[i];
+ }
+
+ tr->tx_node[i-1].next = NULL;
+ tr->tx_node_head = &tr->tx_node[0];
+ tr->tx_node_num = ESWIN_TX_NODE_CNT;
+ spin_lock_init(&tr->tx_lock);
+
+ for (i=1; i<ESWIN_TX_NODE_CNT; i++) {
+ tr->tx_pkg_node[i-1].next = &tr->tx_pkg_node[i];
+ }
+
+ tr->tx_pkg_node[i-1].next = NULL;
+ tr->tx_pkg_node_head = &tr->tx_pkg_node[0];
+ tr->tx_pkg_node_num = ESWIN_TX_NODE_CNT;
+ spin_lock_init(&tr->tx_pkg_lock);
+}
+
+int tx_desc_count = 0;
+int sdio_host_send(void *buff, int len, int flag)
+{
+ struct eswin * tr= pEswin;
+ struct tx_buff_node * node = sdio_tx_node_alloc(tr);
+
+ ECRNX_DBG("%s enter, data len :%d ", __func__, len);
+
+ if (!node) {
+ ECRNX_PRINT(" sdio send failed, no node!!\n");
+ return -1;
+ }
+
+ node->buff = (struct lmac_msg *)kzalloc(len, GFP_ATOMIC);
+ if(!node->buff){
+ ECRNX_PRINT("buff malloc error! \n");
+ }
+
+ memcpy(node->buff, buff, len);
+ node->len = len;
+ node->flag = flag & 0xFF;
+
+ if ((len > 512) && (len%512) && ((node->flag & FLAG_MSG_TYPE_MASK) != TX_FLAG_TX_DESC)) {
+ node->flag |= (len%512)<<8;
+ }
+ else
+ {
+ }
+
+ sdio_tx_queue_push(&tr->tx_queue, node);
+
+ if((node->flag & FLAG_MSG_TYPE_MASK) != TX_FLAG_TX_DESC)
+ {
+ tx_desc_count = 0;
+ //queue_work(tr->workqueue_pkg,&tr->work_pkg);
+ wake_up_interruptible(&tr->wait_pkg);
+ }
+ else
+ {
+ tx_desc_count++;
+ if(tx_desc_count%SDIO_PKG_MAX_CNT == 0)
+ {
+ //queue_work(tr->workqueue_pkg,&tr->work_pkg);
+ wake_up_interruptible(&tr->wait_pkg);
+ }
+ else
+ {
+ mod_timer(&sdio_tx_timer, jiffies + usecs_to_jiffies(SDIO_TX_TIMER_TIMEOUT_US));
+ }
+ }
+ return 0;
+}
+
+void sdio_tx_timer_handle(struct timer_list *time)
+{
+ struct eswin * tr = pEswin;
+ if (tx_desc_count)
+ {
+ tx_desc_count = 0;
+ wake_up_interruptible(&tr->wait_pkg);
+ }
+}
+
+extern void ecrnx_send_handle_register(void * fn);
+
+struct eswin * eswin_core_create(size_t priv_size, struct device *dev,
+ const struct sdio_ops * ops)
+{
+ struct eswin * tr;
+
+ tr = (struct eswin *)kzalloc(sizeof(struct eswin) + priv_size, GFP_KERNEL);
+ if(!tr) {
+ return NULL;
+ }
+
+ pEswin = tr;
+
+ tr->dev = dev;
+ tr->loopback = loopback;
+ //tr->loopback = 1;
+ eswin_sdio_ops_init(tr, ops);
+ ecrnx_send_handle_register(sdio_host_send);
+
+ //init_completion(&tr->wim_responded);
+ init_waitqueue_head(&tr->wait_pkg);
+ init_waitqueue_head(&tr->wait_tx);
+ init_waitqueue_head(&tr->wait_cb);
+
+ tr->kthread_pkg = kthread_run(eswin_sdio_tx_pkg_thread, tr, "sdio-tx-pkg");
+ tr->kthread_tx = kthread_run(eswin_sdio_tx_thread, tr, "sdio-tx");
+ tr->kthread_cb = kthread_run(eswin_sdio_callback_thread, tr, "sdio-tx-callback");
+
+ INIT_DELAYED_WORK(&tr->register_work, eswin_core_register_work);
+ timer_setup(&sdio_tx_timer, sdio_tx_timer_handle, 0);
+
+ tr->state = ESWIN_STATE_INIT;
+
+ //eswin_init_debugfs(tr);
+
+ ECRNX_PRINT(" %s exit!!", __func__);
+ return tr;
+
+err_free_mac:
+ eswin_core_destroy(tr);
+ return NULL;
+}
+
+void eswin_core_destroy(struct eswin *tr)
+{
+ unsigned long flags;
+ int i;
+
+ ECRNX_PRINT("%s entry!!", __func__);
+ tr->state = ESWIN_STATE_CLOSEED;
+
+
+ //flush_workqueue(tr->workqueue);
+ //destroy_workqueue(tr->workqueue);
+ //tr->workqueue = NULL;
+
+ ECRNX_PRINT("%s node_num %d\n", __func__, tr->tx_node_num);
+ spin_lock_irqsave(&tr->tx_lock,flags);
+ for (i=0; i<64; i++)
+ {
+ if (tr->tx_node[i].buff)
+ {
+ kfree(tr->tx_node[i].buff);
+ }
+ }
+ spin_unlock_irqrestore(&tr->tx_lock, flags);
+
+ spin_lock_irqsave(&tr->tx_pkg_lock,flags);
+ for (i=0; i<64; i++)
+ {
+ if (tr->tx_pkg_node[i].buff)
+ {
+ kfree(tr->tx_pkg_node[i].buff);
+ }
+ }
+ spin_unlock_irqrestore(&tr->tx_pkg_lock, flags);
+
+ kthread_stop(tr->kthread_pkg);
+ wake_up_interruptible(&tr->wait_pkg);
+ kthread_stop(tr->kthread_cb);
+ wake_up_interruptible(&tr->wait_cb);
+ kthread_stop(tr->kthread_tx);
+ wake_up_interruptible(&tr->wait_tx);
+
+ kfree(tr);
+ tr = NULL;
+ //TODO:
+ //eswin_mac_destroy(tr);
+ ECRNX_PRINT("%s exit!!", __func__);
+}
+
+
+//MODULE_AUTHOR("Transa-Semi");
+//MODULE_LICENSE("Dual BSD/GPL");
+//MODULE_DESCRIPTION("Core module for Transa-Semi 802.11 WLAN SDIO driver");
diff --git a/drivers/net/wireless/eswin/sdio/core.h b/drivers/net/wireless/eswin/sdio/core.h
new file mode 100644
index 000000000000..58aa81237530
--- /dev/null
+++ b/drivers/net/wireless/eswin/sdio/core.h
@@ -0,0 +1,364 @@
+/**
+ ******************************************************************************
+ *
+ * @file core.h
+ *
+ * @brief sdio core definitions
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+
+#ifndef _CORE_H_
+#define _CORE_H_
+
+
+#include <linux/completion.h>
+#include <linux/if_ether.h>
+
+#include <net/mac80211.h>
+#include "ecrnx_compat.h"
+
+
+#define ESWIN_FLAG_CORE_REGISTERED 1
+
+#define ESWIN_NR_VIF 2
+#define ESWIN_NR_VIF_HW_QUEUE 4
+
+
+#define ESWIN_STATE_BOOT 1
+#define ESWIN_STATE_INIT 2
+#define ESWIN_STATE_STOP 3
+#define ESWIN_STATE_CLOSING 4
+#define ESWIN_STATE_CLOSEED 5
+#define ESWIN_STATE_START 6
+#define ESWIN_STATE_RUNNING 7
+
+#define SDIO_PKG_MAX_BIT 9
+#define SDIO_PKG_MAX_CNT 4//4/3
+#define SDIO_PKG_BIT_SHIFT (SDIO_PKG_MAX_BIT/SDIO_PKG_MAX_CNT)
+#define SDIO_PKG_PAD_GRN (512)//512/256
+#define SDIO_PKG_DIV_MSZ (1024)//1024/1536
+#define SDIO_DATA_MTU (1500)
+#define SDIO_PKG_MAX_DATA (SDIO_DATA_MTU + ECRNX_TX_TXDESC_API_ALIGN)
+
+
+#define WIM_RESP_TIMEOUT (msecs_to_jiffies(100))
+
+enum ESWIN_SCAN_MODE {
+ ESWIN_SCAN_MODE_IDLE = 0,
+ ESWIN_SCAN_MODE_SCANNING,
+ ESWIN_SCAN_MODE_ABORTING,
+};
+
+
+
+struct fwinfo_t {
+ uint32_t ready;
+ uint32_t version;
+ uint32_t tx_head_size;
+ uint32_t rx_head_size;
+ uint32_t payload_align;
+ uint32_t buffer_size;
+};
+
+struct eswin_capabilities {
+ uint64_t cap_mask;
+ uint16_t listen_interval;
+ uint16_t bss_max_idle;
+ uint8_t bss_max_idle_options;
+};
+
+struct eswin_max_idle {
+ bool enable;
+ u16 period;
+ u16 scale_factor;
+ u8 options;
+ struct timer_list keep_alive_timer;
+
+ unsigned long idle_period; /* jiffies */
+ struct timer_list timer;
+};
+
+
+/* Private txq driver data structure */
+struct eswin_txq {
+ u16 hw_queue; /* 0: AC_BK, 1: AC_BE, 2: AC_VI, 3: AC_VO */
+ struct list_head list;
+ struct sk_buff_head queue; /* own queue */
+ unsigned long nr_fw_queueud;
+ unsigned long nr_push_allowed;
+ struct ieee80211_vif vif;
+ struct ieee80211_sta sta;
+};
+
+struct tx_buff_node {
+ struct tx_buff_node * next;
+ void * buff;
+ int len;
+ int flag;
+};
+
+struct tx_buff_queue {
+ struct tx_buff_node * head;
+ struct tx_buff_node * tail;
+ int count;
+ spinlock_t lock;
+};
+
+struct tx_buff_pkg_node {
+ struct tx_buff_pkg_node * next;
+ void * buff;
+ int len;
+ int flag;
+ struct tx_buff_node * tx_node[SDIO_PKG_MAX_CNT];
+ int node_cnt;
+};
+
+struct tx_buff_pkg_queue {
+ struct tx_buff_pkg_node * head;
+ struct tx_buff_pkg_node * tail;
+ int count;
+ spinlock_t lock;
+};
+
+struct tx_buff_pkg_head {
+ unsigned int len[SDIO_PKG_MAX_CNT];
+};
+
+#define ESWIN_QUEUE_MAX (ESWIN_NR_VIF_HW_QUEUE*ESWIN_NR_VIF + 3)
+#define ESWIN_TX_NODE_CNT 64
+
+typedef int (*sdio_rx_cb_t)(void *priv, struct sk_buff *skb);
+typedef int (*sdio_data_cfm_cb_t)(void *priv, void *host_id);
+struct eswin {
+
+ void *umac_priv; //mac drv data.
+ struct ieee80211_hw *hw;
+ struct ieee80211_vif *vif[ESWIN_NR_VIF];
+ struct device *dev;
+ int nr_active_vif;
+ int state;
+ bool promisc;
+
+ bool loopback;
+ bool ampdu_supported;
+ int lb_count;
+ bool amsdu_supported;
+ bool block_frame;
+ bool ampdu_reject;
+
+ char alpha2[2];
+ u64 tsf_offset;
+
+ const struct sdio_ops *ops;
+ sdio_rx_cb_t rx_callback;
+ sdio_data_cfm_cb_t data_cfm_callback;
+ sdio_data_cfm_cb_t msg_cfm_callback;
+
+ //struct sx_buff_queue queue[ESWIN_NR_VIF]; /* 0: frame, 1: wim */
+ struct tx_buff_queue tx_queue;
+ struct tx_buff_pkg_queue tx_c_queue;
+ struct tx_buff_pkg_queue tx_pkg_queue;
+ struct tx_buff_node tx_node[ESWIN_TX_NODE_CNT];
+ struct tx_buff_node * tx_node_head;
+ spinlock_t tx_lock;
+ int tx_node_num;
+
+ struct tx_buff_pkg_node tx_pkg_node[ESWIN_TX_NODE_CNT];
+ struct tx_buff_pkg_node * tx_pkg_node_head;
+ spinlock_t tx_pkg_lock;
+ int tx_pkg_node_num;
+
+ struct work_struct work;
+ struct work_struct work_c;
+ struct work_struct work_pkg;
+
+ //struct task_struct *kthread_pkg;
+ //wait_queue_head_t wait_pkg; /* wait queue */
+
+ struct eswin_txq ntxq[ESWIN_QUEUE_MAX];
+
+ struct mutex state_mtx;
+ enum ESWIN_SCAN_MODE scan_mode;
+
+ struct task_struct *kthread_pkg;
+ struct task_struct *kthread_tx;
+ struct task_struct *kthread_cb;
+
+ wait_queue_head_t wait_pkg;
+ wait_queue_head_t wait_tx;
+ wait_queue_head_t wait_cb;
+
+ struct delayed_work scan_timeout;
+
+ //struct work_struct register_work;
+ struct delayed_work register_work;
+
+
+ unsigned long dev_flags;
+ struct mac_address mac_addr[ESWIN_NR_VIF];
+ struct ieee80211_supported_band bands[NUM_NL80211_BANDS];
+
+
+
+ /* Move to vif or sta driver data */
+ u8 frame_seqno;
+ u8 wim_seqno;
+ u8 band;
+ u16 center_freq;
+ u16 aid;
+ u32 cipher_pairwise;
+ u32 cipher_group;
+
+ struct fwinfo_t fwinfo;
+ struct eswin_capabilities cap;
+
+ /* power management */
+ enum ps_mode {
+ PS_DISABLED,
+ PS_ENABLED,
+ PS_AUTO_POLL,
+ PS_MANUAL_POLL
+ } ps;
+ bool ps_poll_pending;
+ bool ps_enabled;
+
+
+
+ /* tx */
+ spinlock_t txq_lock;
+ struct list_head txq;
+ /* 0: AC_BK, 1: AC_BE, 2: AC_VI, 3: AC_VO */
+ atomic_t tx_credit[IEEE80211_NUM_ACS*3];
+ atomic_t tx_pend[IEEE80211_NUM_ACS*3];
+
+// struct completion wim_responded;
+// struct sk_buff *last_wim_responded;
+
+
+ struct delayed_work roc_finish;
+
+ struct firmware *fw;
+
+ struct dentry *debugfs;
+
+ /* must be last */
+ u8 drv_priv[0] __aligned(sizeof(void *));
+};
+
+
+/* vif driver data structure */
+struct eswin_vif {
+ struct eswin *tr;
+ int index;
+ struct net_device *dev;
+
+ /* scan */
+ struct delayed_work scan_timeout;
+
+ /* power save */
+ bool ps_polling;
+
+ /* MLME */
+ spinlock_t preassoc_sta_lock;
+ struct list_head preassoc_sta_list;
+
+ /* inactivity */
+ u16 max_idle_period;
+};
+
+#define to_ieee80211_vif(v) \
+ container_of((void *)v, struct ieee80211_vif, drv_priv)
+
+#define to_i_vif(v) ((struct eswin_vif *) (v)->drv_priv)
+
+static inline int hw_vifindex(struct ieee80211_vif *vif)
+{
+ struct eswin_vif *i_vif;
+
+ if (vif == NULL)
+ return 0;
+
+ i_vif = to_i_vif(vif);
+ return i_vif->index;
+}
+
+/* sta driver data structure */
+struct eswin_sta {
+ struct eswin *tr;
+ struct ieee80211_vif *vif;
+ /*struct ieee80211_sta *sta;*/
+
+ enum ieee80211_sta_state state;
+ struct list_head list;
+
+ /* keys */
+ struct ieee80211_key_conf *ptk;
+ struct ieee80211_key_conf *gtk;
+
+ /* BSS max idle period */
+ struct eswin_capabilities cap;
+ struct eswin_max_idle max_idle;
+};
+
+#define to_ieee80211_sta(s) \
+ container_of((void *)s, struct ieee80211_sta, drv_priv)
+
+#define to_i_sta(s) ((struct eswin_sta *) (s)->drv_priv)
+
+
+
+struct eswin_sta_handler {
+ int (*sta_state)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ enum ieee80211_sta_state old_state,
+ enum ieee80211_sta_state new_state);
+
+};
+
+#if 0
+#define STAH(fn) \
+ static struct eswin_sta_handler __stah_ ## fn \
+ __attribute((__used__)) \
+ __attribute((__section__("nrc.sta"))) = { \
+ .sta_state = fn, \
+ }
+
+extern struct eswin_sta_handler __sta_h_start, __sta_h_end;
+#endif
+
+
+/* trx */
+
+struct eswin_trx_data {
+ struct eswin *tr;
+ struct ieee80211_vif *vif;
+ struct ieee80211_sta *sta;
+ struct sk_buff *skb;
+ int result;
+};
+
+
+#define NL80211_IFTYPE_ALL (BIT(NUM_NL80211_IFTYPES)-1)
+
+
+
+int eswin_core_register(struct eswin *tr);
+void eswin_core_unregister(struct eswin *tr);
+
+struct eswin * eswin_core_create(size_t priv_size, struct device *dev,
+ const struct sdio_ops * ops);
+
+void eswin_core_destroy(struct eswin *tr);
+
+
+extern int power_save;
+extern int disable_cqm;
+#ifdef CONFIG_ECRNX_WIFO_CAIL
+extern bool amt_mode;
+#endif
+extern bool set_gain;
+
+#endif
diff --git a/drivers/net/wireless/eswin/sdio/debug.c b/drivers/net/wireless/eswin/sdio/debug.c
new file mode 100644
index 000000000000..83420fab3016
--- /dev/null
+++ b/drivers/net/wireless/eswin/sdio/debug.c
@@ -0,0 +1,202 @@
+/**
+ ******************************************************************************
+ *
+ * @file debug.c
+ *
+ * @brief sdio driver debug function definitions
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+
+#include <linux/timer.h>
+#include "ecrnx_utils.h"
+#include "ecrnx_defs.h"
+#include "ecrnx_msg_rx.h"
+#include "ecrnx_sdio.h"
+#include "core.h"
+#include "debug.h"
+#include "sdio.h"
+
+
+extern int ecrnx_rx_callback(void *priv, struct sk_buff *skb);
+
+//kernal timer param
+static struct timer_list tm;
+static int counter = 0;
+
+//rx param
+struct sk_buff *skb = NULL;
+struct ecrnx_hw *g_ecrnx_hw = NULL;
+
+//tx param
+struct ecrnx_vif vif;
+struct ecrnx_sta sta;
+struct cfg80211_mgmt_tx_params params;
+
+static void test_timer_handler(struct timer_list * lt);
+
+void ecrnx_hw_set(void* init_ecrnx_hw)
+{
+ g_ecrnx_hw = (struct ecrnx_hw *)init_ecrnx_hw;
+}
+
+static int sdio_rx_param_init(void)
+{
+ struct rxu_stat_mm rxu_state;
+ struct rx_hd rx_head;
+ struct ethhdr eth_hd;
+ int res = 0, index = 0;
+ uint8_t *ptr = NULL;
+ uint16_t head_len = sizeof(struct ethhdr);
+ ECRNX_DBG("%s entry!!", __func__);
+ memset(&rxu_state, 0, sizeof(struct rxu_stat_mm));
+ memset(&rx_head, 0, sizeof(struct rx_hd));
+ memset(&eth_hd, 0, sizeof(struct ethhdr));
+
+ rxu_state.comm_hd.frm_type = SDIO_FRM_TYPE_RXDESC;
+ //rxu_state.comm_hd.frm_type = SDIO_FRM_TYPE_MSG;
+ if(rxu_state.comm_hd.frm_type == SDIO_FRM_TYPE_RXDESC)
+ {
+ head_len += sizeof(struct rxu_stat_mm) + sizeof(struct rx_hd);
+ }
+ else
+ {
+ head_len += sizeof(dispatch_hdr_t);
+ }
+
+ skb = dev_alloc_skb(FRAME_SIZE + head_len);
+ skb_reserve(skb, head_len);
+ ptr = skb_put(skb, FRAME_SIZE); //ptr is skb tail
+ memset(skb->data, 0x0f, FRAME_SIZE); //payload
+ skb_push(skb, sizeof(struct ethhdr));
+
+ for( index = 0; index < ETH_ALEN; index++)
+ {
+ eth_hd.h_dest[index] = index;
+ eth_hd.h_source[index] = index;
+ }
+
+ eth_hd.h_proto = ETH_P_80221; //ETHERTYPE_IP;
+ memcpy(skb->data, &eth_hd, sizeof(struct ethhdr));
+
+ if(rxu_state.comm_hd.frm_type == SDIO_FRM_TYPE_RXDESC)
+ {
+ //data frame, need header, rxu state
+ //rx head
+ skb_push(skb, sizeof(struct rx_hd));
+ rx_head.frmlen = FRAME_SIZE + head_len;
+ rx_head.ampdu_stat_info = 0;
+ //...
+ memcpy(skb->data , &rx_head, sizeof(struct rx_hd));
+
+ //rxu state
+ skb_push(skb, sizeof(struct rxu_stat_mm));
+ rxu_state.msdu_mode = 0x01;
+ rxu_state.host_id = 0x0001;
+ rxu_state.frame_len = rx_head.frmlen;
+ rxu_state.status = RX_STAT_MONITOR;
+ //rxu_state.phy_info.info1 = 1 | (1 << 8) | (2450 << 16);
+ //rxu_state.phy_info.info2 = 2450 | (2450 << 16);
+#ifdef CONFIG_ECRNX_FULLMAC
+ //rxu_state.flags = 0;
+#endif
+ //rxu_state.pattern = ecrnx_rxbuff_pattern;
+ memcpy(skb->data, &rxu_state, sizeof(struct rxu_stat_mm));
+ }
+ else
+ {
+ //message frame, don't need header
+ skb_push(skb, sizeof(dispatch_hdr_t)); //rxu state
+ memcpy(skb->data, &rxu_state.comm_hd, sizeof(dispatch_hdr_t));
+ }
+
+ for(index = 0; index < head_len; index++)
+ {
+ ECRNX_DBG("0x%x ", skb->data[index]);
+ }
+
+ ECRNX_DBG("%s exit, skb_len:%d, type: %d!!", __func__, skb->len, (skb->data[1] << 8) | skb->data[0]);
+
+ return res;
+}
+
+extern int ecrnx_start_mgmt_xmit(struct ecrnx_vif *vif, \
+ struct ecrnx_sta *sta, \
+ struct cfg80211_mgmt_tx_params *params, \
+ bool offchan, \
+ u64 *cookie);
+
+/*
+struct cfg80211_mgmt_tx_params {
+ struct ieee80211_channel *chan;
+ bool offchan;
+ unsigned int wait;
+ const u8 *buf;
+ size_t len;
+ bool no_cck;
+ bool dont_wait_for_ack;
+ int n_csa_offsets;
+ const u16 *csa_offsets;
+};
+*/
+static void sdio_tx_param_init(void)
+{
+ u8 send_buf[FRAME_SIZE] = {0x00, 0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09};
+
+ params.len = FRAME_SIZE;
+ params.buf = (const u8 *)send_buf;
+ params.n_csa_offsets = 10;
+ params.csa_offsets = (const u16 *)send_buf;
+ params.no_cck = 0;
+
+ vif.ecrnx_hw = g_ecrnx_hw;
+}
+
+void sdio_rx_tx_test_schedule(void)
+{
+ ECRNX_DBG("%s entry!!", __func__);
+
+ //wangc add
+ //tm.function = test_timer_handler;
+ tm.expires = jiffies + HZ * 10;
+ add_timer(&tm);
+
+ sdio_rx_param_init();
+ sdio_tx_param_init();
+ ECRNX_DBG("%s exit!!", __func__);
+}
+
+static void test_timer_handler(struct timer_list * lt)
+{
+ ECRNX_DBG("%s, counter:%d\n", __FUNCTION__, counter);
+
+ if(counter%2)
+ {
+ u64 cookie;
+ //ecrnx_start_mgmt_xmit(&vif, NULL, &params, false, &cookie);
+ }
+ else
+ {
+ ecrnx_rx_callback(g_ecrnx_hw, skb);
+ }
+
+ if(lt)
+ {
+ counter++;
+ }
+
+ if(counter < 5)
+ {
+ //tm.expires = jiffies +1 * HZ / 1000/10; //100us
+ tm.expires = jiffies +1 * HZ;
+ add_timer(&tm);
+ }
+ else
+ {
+ counter = 0;
+ del_timer(&tm);
+ }
+}
+
diff --git a/drivers/net/wireless/eswin/sdio/debug.h b/drivers/net/wireless/eswin/sdio/debug.h
new file mode 100644
index 000000000000..ec53824115fc
--- /dev/null
+++ b/drivers/net/wireless/eswin/sdio/debug.h
@@ -0,0 +1,31 @@
+/**
+ ******************************************************************************
+ *
+ * @file debug.h
+ *
+ * @brief sdio driver debug definitions
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+
+#ifndef _ECRNX_DBG_
+#define _ECRNX_DBG_
+#include <net/mac80211.h>
+
+//#define CONFIG_HIF_PRINT_TX_DATA
+#define CONFIG_USE_TXQ
+
+//#define CONFIG_NRC_HIF_PRINT_FLOW_CONTROL
+//#define CONFIG_SHOW_TX_SPEED
+//#define CONFIG_SHOW_RX_SPEED
+
+#define FRAME_SIZE 512
+
+void eswin_dump_wim(struct sk_buff *skb);
+void eswin_init_debugfs(struct eswin *tr);
+void sdio_rx_tx_test_schedule(void);
+void ecrnx_hw_set(void* init_ecrnx_hw);
+
+#endif
diff --git a/drivers/net/wireless/eswin/sdio/ecrnx_sdio.c b/drivers/net/wireless/eswin/sdio/ecrnx_sdio.c
new file mode 100644
index 000000000000..fc8cd1f864f8
--- /dev/null
+++ b/drivers/net/wireless/eswin/sdio/ecrnx_sdio.c
@@ -0,0 +1,559 @@
+/**
+ ******************************************************************************
+ *
+ * @file ecrnx_sdio.c
+ *
+ * @brief ECRNX sdio init and management function
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+
+#include <linux/module.h>
+#include <linux/kthread.h>
+#include <linux/mmc/sdio_func.h>
+#include <linux/mmc/sdio_ids.h>
+#include "core.h"
+//#include "debug.h"
+#include "ecrnx_utils.h"
+#include "ecrnx_cmds.h"
+#include "ecrnx_defs.h"
+#include "ecrnx_msg_rx.h"
+#include "ipc_host.h"
+#include "ipc_shared.h"
+#include "ecrnx_events.h"
+#include "ecrnx_sdio.h"
+#ifdef CONFIG_TEST_ESWIN_SDIO
+#include "debug.h"
+#endif
+#ifdef CONFIG_ECRNX_WIFO_CAIL
+#include "ecrnx_amt.h"
+#endif
+
+
+#define SDIO_ADDR_DATA (unsigned int)(0x200)
+
+#ifdef CONFIG_ECRNX_SOFTMAC
+#define FW_STR "lmac"
+#elif defined CONFIG_ECRNX_FULLMAC
+#define FW_STR "fmac"
+#endif
+
+sdio_rx_buf_t sdio_rx_buff;
+
+extern const int nx_txdesc_cnt_msk[];
+
+struct vendor_radiotap_hdr {
+ u8 oui[3];
+ u8 subns;
+ u16 len;
+ u8 data[];
+};
+
+/**
+ * @brief: sdio_rx_buf_init: Initialization the sdio rx buffer
+ * @param {void} void
+ * @return: none
+ */
+static void sdio_rx_buf_init(void)
+{
+ uint8_t i = 0;
+ ECRNX_DBG("%s entry!!", __func__);
+ memset(&sdio_rx_buff, 0, sizeof(sdio_rx_buf_t));
+
+ for(i = 0; i < SDIO_RXBUF_CNT; i++)
+ {
+ sdio_rx_buff.ecrnx_sdio_rx_skb[i].skb = dev_alloc_skb(SDIO_RXBUF_SIZE);
+ skb_put(sdio_rx_buff.ecrnx_sdio_rx_skb[i].skb, SDIO_RXBUF_SIZE);
+ }
+#ifdef CONFIG_TEST_ESWIN_SDIO
+ sdio_rx_tx_test_schedule();
+#endif
+ ECRNX_DBG("%s exit!!", __func__);
+}
+
+/**
+ * @brief: sdio_rx_buf_deinit: Deinitialization the sdio rx buffer
+ * @param {void} void
+ * @return: none
+ */
+static void sdio_rx_buf_deinit(void)
+{
+ uint8_t i = 0;
+
+ memset(&sdio_rx_buff, 0, sizeof(sdio_rx_buf_t));
+
+ for(i = 0; i < SDIO_RXBUF_CNT; i++)
+ {
+ if(sdio_rx_buff.ecrnx_sdio_rx_skb[i].skb)
+ {
+ dev_kfree_skb(sdio_rx_buff.ecrnx_sdio_rx_skb[i].skb);
+ }
+ }
+}
+
+/**
+ * @brief: sdio_rx_buf_push: push a skb element to sdio rx buffer
+ * @param {skb} skb data need to push
+ * @param {recv_len} skb data length
+ * @return: sk_buff
+ */
+struct sk_buff * sdio_rx_buf_push(struct sk_buff *skb, uint16_t recv_len)
+{
+ uint8_t index = 0;
+
+ ECRNX_DBG("%s enter, skb: %d, skb_data:%d, skb_len:%d", __func__, skb, skb->data, recv_len);
+ if(atomic_read(&sdio_rx_buff.suspend))
+ {
+ return NULL;
+ }
+
+ if(sdio_rx_buff.sdio_host_rx_buff_used >= SDIO_RXBUF_CNT)
+ {
+ ECRNX_PRINT("no enough space \n");
+ return NULL;
+ }
+
+ if((!skb) || (!recv_len) || (recv_len > SDIO_RXBUF_SIZE))
+ {
+ ECRNX_PRINT("rx data is error \n");
+ return NULL;
+ }
+
+ atomic_set(&sdio_rx_buff.suspend, 1);
+
+ do
+ {
+ if(!sdio_rx_buff.ecrnx_sdio_rx_skb[index].flag) //find a index to store the data
+ {
+ break;
+ }
+ index++;
+ }while(index < SDIO_RXBUF_CNT);
+
+ sdio_rx_buff.sdio_host_rx_buff_used++;
+ sdio_rx_buff.ecrnx_sdio_rx_skb[index].flag = true;
+ sdio_rx_buff.ecrnx_sdio_rx_skb[index].data_len = recv_len;
+ memcpy(sdio_rx_buff.ecrnx_sdio_rx_skb[index].skb->data, skb->data, recv_len);
+ atomic_set(&sdio_rx_buff.suspend, 0);
+
+ return sdio_rx_buff.ecrnx_sdio_rx_skb[index].skb;
+}
+
+/**
+ * @brief: sdio_rx_buf_pop: pop a skb element from sdio rx buffer
+ * @param {void}
+ * @return: sk_buff
+ */
+struct sk_buff* sdio_rx_buf_pop(void)
+{
+ uint8_t index = 0;
+ struct sk_buff *skb= NULL;
+
+ if(atomic_read(&sdio_rx_buff.suspend))
+ {
+ ECRNX_PRINT("sdio suspend! \n");
+ return NULL;
+ }
+
+ if(sdio_rx_buff.sdio_host_rx_buff_used <= 0)
+ {
+ ECRNX_PRINT("no data in memory \n");
+ return NULL;
+ }
+
+ atomic_set(&sdio_rx_buff.suspend, 1);
+
+ do
+ {
+ if(sdio_rx_buff.ecrnx_sdio_rx_skb[index].flag) //find a index to pop the data
+ {
+ break;
+ }
+ index++;
+ }while(index < SDIO_RXBUF_CNT);
+
+ if(index != SDIO_RXBUF_CNT)
+ {
+ skb = sdio_rx_buff.ecrnx_sdio_rx_skb[index].skb;
+ sdio_rx_buff.ecrnx_sdio_rx_skb[index].flag = false;
+ sdio_rx_buff.sdio_host_rx_buff_used = (sdio_rx_buff.sdio_host_rx_buff_used > 0)? sdio_rx_buff.sdio_host_rx_buff_used-1: 0;
+ }
+ atomic_set(&sdio_rx_buff.suspend, 0);
+
+ return skb;
+}
+
+/**
+ * @brief: ipc_host_rxdesc_handler: Handle the reception of a Rx Descriptor
+ * Called from general IRQ handler when status %IPC_IRQ_E2A_RXDESC is set
+ * @param {env} pointer to the sdio Host environment
+ * @param {skb} received skb data
+ * @return: none
+ */
+static int sdio_host_rxdesc_handler(struct ipc_host_env_tag *env, struct sk_buff *skb)
+{
+ u8 ret = 0;
+ // LMAC has triggered an IT saying that a reception has occurred.
+ // Then we first need to check the validity of the current hostbuf, and the validity
+ // of the next hostbufs too, because it is likely that several hostbufs have been
+ // filled within the time needed for this irq handling
+#ifdef CONFIG_ECRNX_FULLMAC
+ // call the external function to indicate that a RX descriptor is received
+ ret = env->cb.recv_data_ind(env->pthis, skb);
+#else
+ // call the external function to indicate that a RX packet is received
+ ret = env->cb.recv_data_ind(env->pthis, skb);
+#endif //(CONFIG_ECRNX_FULLMAC)
+ ECRNX_DBG("%s exit!!", __func__);
+ return ret;
+}
+
+/**
+ * @brief: sdio_host_radar_handler Handle the reception of radar events
+ * @param {env} pointer to the sdio Host environment
+ * @param {skb} received skb data
+ * @return: none
+ */
+static int sdio_host_radar_handler(struct ipc_host_env_tag *env, struct sk_buff *skb)
+{
+ int ret = 0;
+
+#ifdef CONFIG_ECRNX_RADAR
+ // LMAC has triggered an IT saying that a radar event has been sent to upper layer.
+ // Then we first need to check the validity of the current msg buf, and the validity
+ // of the next buffers too, because it is likely that several buffers have been
+ // filled within the time needed for this irq handling
+ // call the external function to indicate that a RX packet is received
+ spin_lock(&((struct ecrnx_hw *)env->pthis)->radar.lock);
+ ret = env->cb.recv_radar_ind(env->pthis, skb);
+ spin_unlock(&((struct ecrnx_hw *)env->pthis)->radar.lock);
+#endif /* CONFIG_ECRNX_RADAR */
+ return ret;
+}
+
+/**
+ * @brief: sdio_host_unsup_rx_vec_handler Handle the reception of unsupported rx vector
+ * @param {env} pointer to the sdio Host environment
+ * @param {skb} received skb data
+ * @return: none
+ */
+static int sdio_host_unsup_rx_vec_handler(struct ipc_host_env_tag *env, struct sk_buff *skb)
+{
+ return env->cb.recv_unsup_rx_vec_ind(env->pthis, skb);
+}
+
+/**
+ * @brief: sdio_host_msg_handler Handler for firmware message
+ * @param {env} pointer to the sdio Host environment
+ * @param {skb} received skb data
+ * @return: none
+ */
+static int sdio_host_msg_handler(struct ipc_host_env_tag *env, struct sk_buff *skb)
+{
+ // LMAC has triggered an IT saying that a message has been sent to upper layer.
+ // Then we first need to check the validity of the current msg buf, and the validity
+ // of the next buffers too, because it is likely that several buffers have been
+ // filled within the time needed for this irq handling
+ // call the external function to indicate that a RX packet is received
+ return env->cb.recv_msg_ind(env->pthis, skb->data);
+}
+
+/**
+ * @brief: sdio_host_msgack_handler Handle the reception of message acknowledgement
+ * @param {env} pointer to the sdio Host environment
+ * @param {skb} received skb data
+ * @return: none
+ */
+static int sdio_host_msgack_handler(struct ipc_host_env_tag *env, struct sk_buff *skb)
+{
+ uint64_t hostid = *(uint64_t *)skb->data;
+
+ ASSERT_ERR(hostid);
+
+ env->msga2e_hostid = NULL;
+ env->cb.recv_msgack_ind(env->pthis, hostid);
+
+ return 0;
+}
+
+/**
+ * @brief: sdio_host_dbg_handler Handle the reception of Debug event
+ * @param {env} pointer to the sdio Host environment
+ * @param {skb} received skb data
+ * @return: none
+ */
+static int sdio_host_dbg_handler(struct ipc_host_env_tag *env, struct sk_buff *skb)
+{
+ // LMAC has triggered an IT saying that a DBG message has been sent to upper layer.
+ // Then we first need to check the validity of the current buffer, and the validity
+ // of the next buffers too, because it is likely that several buffers have been
+ // filled within the time needed for this irq handling
+ // call the external function to indicate that a RX packet is received
+ return env->cb.recv_dbg_ind(env->pthis, skb);
+}
+
+/**
+ * @brief: sdio_host_tx_cfm_handler Handle the reception of TX confirmation
+ * @param {env} pointer to the sdio Host environment
+ * @param {queue_idx} index of the hardware on which the confirmation has been received
+ * @param {user_pos} index of the user position
+ * @return: none
+ */
+static int sdio_host_tx_cfm_handler(struct ipc_host_env_tag *env, struct sk_buff *skb)
+{
+ ptr_addr host_id;
+ struct sk_buff *skb_cfm;
+ struct ecrnx_txhdr *txhdr;
+
+ ECRNX_DBG("%s, skb: 0x%08x \n", __func__, skb);
+#ifdef CONFIG_ECRNX_FULLMAC
+ struct tx_cfm_tag *cfm;
+
+ cfm = (struct tx_cfm_tag *)skb->data;
+ memcpy((uint8_t *)&host_id, (uint8_t *)cfm->hostid, sizeof(ptr_addr));
+ ECRNX_DBG("--%s--hostid 0x%08x, skb: 0x%x \n", __func__, host_id, skb);
+ if (host_id == 0) {
+ return 0;
+ }
+
+ skb_cfm = (struct sk_buff *)host_id;
+ txhdr = (struct ecrnx_txhdr *)skb_cfm->data;
+ memcpy(&txhdr->hw_hdr.cfm, cfm, sizeof(*cfm));
+#elif defined CONFIG_ECRNX_SOFTMAC
+ //TODO:
+#endif
+
+ return env->cb.send_data_cfm(env->pthis, host_id);
+ //return 0;
+}
+
+#ifdef CONFIG_ECRNX_WIFO_CAIL
+/**
+ * @brief: sdio_host_rx_handler Handle the reception of rx frame
+ * @param {frm_type} received frame type
+ * @param {skb} received skb data
+ * @return: none
+**/
+void sdio_host_amt_rx_handler(uint32_t frm_type, struct sk_buff *skb)
+{
+ int need_free = 0;
+
+ ECRNX_PRINT("%s enter, frame type: %d, len %d.!!", __func__, frm_type, skb->len);
+ if (frm_type != SDIO_FRM_TYPE_RXDESC)
+ {
+ skb_pull(skb, SKB_DATA_COM_HD_OFFSET); //delete the frame common header
+ }
+
+ switch (frm_type)
+ {
+ case SDIO_FRM_TYPE_IWPRIV:
+ {
+ /*print_hex_dump(KERN_INFO, "iwpriv-cfm:", DUMP_PREFIX_ADDRESS, 32, 1,
+ skb->data, skb->len, false);*/
+ amt_vif.rxlen = skb->len;
+ memset(amt_vif.rxdata, 0, ECRNX_RXSIZE);
+ memcpy(amt_vif.rxdata, skb->data, skb->len > ECRNX_RXSIZE ? ECRNX_RXSIZE : skb->len);
+ amt_vif.rxdatas = 1;
+ wake_up(&amt_vif.rxdataq);
+ need_free = 1;
+ break;
+ }
+
+ default:
+ need_free = 1;
+ break;
+ }
+
+ if (need_free && skb) { // free the skb
+ ECRNX_DBG("skb free: 0x%x, \n", skb);
+ dev_kfree_skb(skb);
+ }
+ ECRNX_DBG("%s exit!!", __func__);
+ return;
+}
+#endif
+
+/**
+ * @brief: sdio_host_rx_handler Handle the reception of rx frame
+ * @param {frm_type} received frame type
+ * @param {env} pointer to the sdio Host environment
+ * @param {skb} received skb data
+ * @return: none
+ */
+void sdio_host_rx_handler(uint32_t frm_type, struct ipc_host_env_tag *env, struct sk_buff *skb)
+{
+ int ret = 1;
+
+ ECRNX_DBG("%s enter, frame type: %d!!", __func__, frm_type);
+ if (frm_type != SDIO_FRM_TYPE_RXDESC)
+ {
+ skb_pull(skb, SKB_DATA_COM_HD_OFFSET); //delete the frame common header
+ }
+
+ switch (frm_type)
+ {
+ case SDIO_FRM_TYPE_RXDESC:
+ {
+ sdio_host_rxdesc_handler(env, skb);
+ break;
+ }
+
+ case SDIO_FRM_TYPE_MSG_ACK:
+ {
+ ret = sdio_host_msgack_handler(env, skb);
+ break;
+ }
+
+ case SDIO_FRM_TYPE_MSG:
+ {
+ ret = sdio_host_msg_handler(env, skb);
+ break;
+ }
+
+ case SDIO_FRM_TYPE_TXCFM:
+ {
+ /* add the spinlock which was missed during porting. */
+ spin_lock_bh(&((struct ecrnx_hw *)env->pthis)->tx_lock);
+ while(skb->len > sizeof(struct tx_cfm_tag))
+ {
+ ret = sdio_host_tx_cfm_handler(env, skb);
+ skb_pull(skb, sizeof(struct tx_cfm_tag));
+ }
+ ret = sdio_host_tx_cfm_handler(env, skb);
+ spin_unlock_bh(&((struct ecrnx_hw *)env->pthis)->tx_lock);
+ break;
+ }
+
+ case SDIO_FRM_TYPE_UNSUP_RX_VEC:
+ {
+ // handle the unsupported rx vector reception
+ ret = sdio_host_unsup_rx_vec_handler(env, skb);
+ break;
+ }
+
+ case SDIO_FRM_TYPE_RADAR:
+ {
+ // handle the radar event reception
+ ret = sdio_host_radar_handler(env, skb);
+ break;
+ }
+
+ case SDIO_FRM_TYPE_TBTT_SEC:
+ {
+ env->cb.sec_tbtt_ind(env->pthis);
+ break;
+ }
+
+ case SDIO_FRM_TYPE_TBTT_PRIM:
+ {
+ env->cb.prim_tbtt_ind(env->pthis);
+ break;
+ }
+
+ case SDIO_FRM_TYPE_DBG:
+ {
+ ret = sdio_host_dbg_handler(env, skb);
+ break;
+ }
+
+ case SDIO_FRM_TYPE_UPDATE:
+ {
+ ret = 0;
+ break;
+ }
+ default:
+ ret = 0;
+ break;
+ }
+
+ if (!ret && skb) { // free the skb
+ ECRNX_DBG("skb free: 0x%x, ret: %d!! \n", skb, ret);
+ dev_kfree_skb(skb);
+ }
+ ECRNX_DBG("%s exit!!", __func__);
+ return;
+}
+
+/**
+ * @brief: rcv_skb_convert convert the sdio received skb to the ecrnx handled skb.
+ * @param {src_rxu_state} received rxu state in skb
+ * @param {src_rxu_state} received rx header in skb
+ * @param {src_rxu_state} handled hw rxhdr in skb
+ * @param {src_rxu_state} handled rx desc tag in skb
+ * @return: u8
+ */
+u8 rcv_skb_convert(struct rxu_stat_mm* src_rxu_state, \
+ struct rx_hd* src_rx_hd, \
+ struct sk_buff *skb, \
+ struct hw_rxhdr* dst_hw_rxhdr,\
+ struct rxdesc_tag* dst_rxdesc_tag)
+{
+ ECRNX_DBG("%s enter!!", __func__);
+#if 0
+ uint16_t index = 0;
+
+ if (!skb || !skb->data){
+ ECRNX_PRINT("RX data invalid \n");
+ return -1;
+ }
+
+ //copy the rxu state and rx header, repush process need to used them
+ memcpy(src_rxu_state, skb->data, sizeof(struct rxu_stat_mm));
+ memcpy(src_rx_hd, skb->data + sizeof(struct rxu_stat_mm), sizeof(struct rx_hd));
+
+ /*copy the hw vector and rxu state */
+ memcpy(dst_hw_rxhdr, skb->data + sizeof(struct rxu_stat_mm), sizeof(struct rx_hd));
+ memcpy(&dst_hw_rxhdr->phy_info, \
+ skb->data + SKB_DATA_TAG_OFFSET, \
+ sizeof(struct rxu_stat_mm) - SKB_DATA_TAG_OFFSET);
+
+ memcpy(dst_rxdesc_tag, skb->data + SKB_DATA_COM_HD_OFFSET + 2, sizeof(struct rxdesc_tag)); //two byte msdu_mode
+ /*recove the hdr to skb data*/
+ skb_pull(skb, SKB_DATA_HD_OFFSET); //delete the frame type and amsdu flag
+ skb_push(skb, sizeof(struct hw_rxhdr));
+ memcpy(skb->data, dst_hw_rxhdr, sizeof(struct hw_rxhdr)); //put hw header to skb
+ skb_push(skb, sizeof(struct rxdesc_tag));
+ memcpy(skb->data, dst_rxdesc_tag, sizeof(struct rxdesc_tag)); //put rx desc tag to skb
+
+ for(index = 0; index < sizeof(struct hw_rxhdr) + sizeof(struct rxdesc_tag); index++)
+ {
+ ECRNX_DBG("0x%x ", skb->data[index]);
+ }
+#endif
+ ECRNX_DBG("%s exit!!", __func__);
+ return 0;
+}
+
+/**
+ * @brief: ecrnx_sdio_init Initialize sdio interface.
+ * @param {ecrnx_hw} Main driver data
+ * @return: u8
+ */
+u8 ecrnx_sdio_init(struct ecrnx_hw *ecrnx_hw)
+{
+ ECRNX_DBG("%s entry!!", __func__);
+ // Save the pointer to the register base
+ ecrnx_hw->ipc_env->pthis = (void*)ecrnx_hw;
+
+#ifdef CONFIG_TEST_ESWIN_SDIO
+ ecrnx_hw_set((void*)ecrnx_hw);
+#endif
+ ECRNX_DBG("%s exit!!", __func__);
+ return 0;
+}
+
+/**
+ * @brief: ecrnx_sdio_deinit DeInitialize sdio interface.
+ * @param {ecrnx_hw} Main driver data
+ * @return: none
+ */
+void ecrnx_sdio_deinit(struct ecrnx_hw *ecrnx_hw)
+{
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ sdio_rx_buf_deinit();
+ memset(ecrnx_hw, 0, sizeof(struct ecrnx_hw));
+ memset(&sdio_rx_buff, 0, sizeof(sdio_rx_buf_t));
+}
diff --git a/drivers/net/wireless/eswin/sdio/ecrnx_sdio.h b/drivers/net/wireless/eswin/sdio/ecrnx_sdio.h
new file mode 100644
index 000000000000..d2b210bf0bde
--- /dev/null
+++ b/drivers/net/wireless/eswin/sdio/ecrnx_sdio.h
@@ -0,0 +1,173 @@
+ /**
+ ******************************************************************************
+ *
+ * @file ecrnx_sdio.h
+ *
+ * @brief ecrnx sdio header file
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+
+#ifndef _ECRNX_SDIO_H_
+#define _ECRNX_SDIO_H_
+
+#include "ecrnx_rx.h"
+#include "eswin_utils.h"
+#include "ecrnx_defs.h"
+/*
+ * Number of Host buffers available for Data Rx handling
+ */
+#define SDIO_RXBUF_CNT 32
+#define SDIO_RXBUF_SIZE 12288
+
+enum{
+ SDIO_FRM_TYPE_RXDESC =1,
+ SDIO_FRM_TYPE_MSG,
+ SDIO_FRM_TYPE_DBG,
+ SDIO_FRM_TYPE_UNSUP_RX_VEC,
+ SDIO_FRM_TYPE_RADAR,
+ SDIO_FRM_TYPE_TBTT_SEC,
+ SDIO_FRM_TYPE_TBTT_PRIM,
+ SDIO_FRM_TYPE_MSG_ACK,
+ SDIO_FRM_TYPE_TXCFM,
+ SDIO_FRM_TYPE_IWPRIV,
+ SDIO_FRM_TYPE_UPDATE
+};
+
+
+#define SKB_DATA_COM_HD_OFFSET sizeof(dispatch_hdr_t)
+//#define SKB_DATA_TAG_OFFSET offsetof(struct rxu_stat_mm, phy_info) //two byte msdu flag
+//#define SKB_DATA_HD_OFFSET sizeof(struct rxu_stat_mm) + sizeof(struct rx_hd)
+
+
+struct sdio_rx_skb{
+ bool flag;
+ u16 data_len;
+ struct sk_buff *skb;
+};
+
+typedef struct{
+ atomic_t suspend;
+ uint8_t sdio_host_rx_buff_used;
+ struct sdio_rx_skb ecrnx_sdio_rx_skb[SDIO_RXBUF_CNT];
+}sdio_rx_buf_t;
+
+/// Structure containing the information about the PHY channel that is used
+// typedef uint32_t u32_l;
+struct phy_channel_info_sdio
+{
+ /// PHY channel information 1
+ uint32_t info1;
+ /// PHY channel information 2
+ uint32_t info2;
+};
+
+/*调整phy info〠pattern,因为这些信æ¯å¿…é¡»è¦æ”¾åˆ°å¦å¤–一个buff中传输*/
+/// Element in the pool of RX header descriptor.
+struct rx_hd
+{
+ /// Total length of the received MPDU
+ uint16_t frmlen;
+ /// AMPDU status information
+ uint16_t ampdu_stat_info;
+ /// TSF Low
+ uint32_t tsflo;
+ /// TSF High
+ uint32_t tsfhi;
+ /// Rx Vector 1
+ struct rx_vector_1 rx_vec_1;
+ /// Rx Vector 2
+ struct rx_vector_2 rx_vec_2;
+ /// MPDU status information
+ uint32_t statinfo;
+};
+
+/*调整phy info〠pattern,因为这些信æ¯å¿…é¡»è¦æ”¾åˆ°å¦å¤–一个buff中传输*/
+struct rxu_stat_mm
+{
+ /*type of msg/dbg/frm etc.*/
+ dispatch_hdr_t frm_type;
+ uint32_t fragment_flag : 1;
+ uint32_t is_qos : 1;
+ uint32_t need_reord : 1;
+ uint32_t need_pn_check : 1;
+ uint32_t is_ga : 1;
+ uint32_t flags_rsvd0 : 3;
+ uint32_t tid : 8;
+ uint16_t sn;
+ /// Length
+ uint16_t frame_len;
+ /// Status (@ref rx_status_bits)
+ uint16_t status;
+ uint16_t real_offset; /* dma address align 4, real data - real_offset */
+};
+
+
+extern sdio_rx_buf_t sdio_rx_buff;
+
+/**
+ * @brief: ecrnx_sdio_init Initialize sdio interface.
+ * @param {ecrnx_hw} Main driver data
+ * @return: u8
+ */
+u8 ecrnx_sdio_init(struct ecrnx_hw *ecrnx_hw);
+
+/**
+ * @brief: ecrnx_sdio_deinit DeInitialize sdio interface.
+ * @param {ecrnx_hw} Main driver data
+ * @return: none
+ */
+void ecrnx_sdio_deinit(struct ecrnx_hw *ecrnx_hw);
+
+/**
+ * @brief: sdio_rx_buf_push: push a skb element to sdio rx buffer
+ * @param {skb} skb data need to push
+ * @param {recv_len} skb data length
+ * @return: sk_buff
+ */
+struct sk_buff * sdio_rx_buf_push(struct sk_buff *skb, uint16_t recv_len);
+
+
+/**
+ * @brief: sdio_rx_buf_pop: pop a skb element from sdio rx buffer
+ * @param {void}
+ * @return: sk_buff
+ */
+struct sk_buff* sdio_rx_buf_pop(void);
+
+#ifdef CONFIG_ECRNX_WIFO_CAIL
+/**
+ * @brief: sdio_host_rx_handler Handle the reception of rx frame
+ * @param {frm_type} received frame type
+ * @param {skb} received skb data
+ * @return: none
+ */
+void sdio_host_amt_rx_handler(uint32_t frm_type, struct sk_buff *skb);
+#endif
+
+/**
+ * @brief: sdio_host_rx_handler Handle the reception of rx frame
+ * @param {frm_type} received frame type
+ * @param {env} pointer to the sdio Host environment
+ * @param {skb} received skb data
+ * @return: none
+ */
+void sdio_host_rx_handler(uint32_t frm_type, struct ipc_host_env_tag *env, struct sk_buff *skb);
+
+/**
+ * @brief: rcv_skb_convert convert the sdio received skb to the ecrnx handled skb.
+ * @param {src_rxu_state} received rxu state in skb
+ * @param {src_rxu_state} received rx header in skb
+ * @param {src_rxu_state} handled hw rxhdr in skb
+ * @param {src_rxu_state} handled rx desc tag in skb
+ * @return: u8
+ */
+u8 rcv_skb_convert(struct rxu_stat_mm* src_rxu_state, \
+ struct rx_hd* src_rx_hd, \
+ struct sk_buff *skb, \
+ struct hw_rxhdr* dst_hw_rxhdr,\
+ struct rxdesc_tag* dst_rxdesc_tag);
+
+#endif /* __ECRNX_SDIO_H */
diff --git a/drivers/net/wireless/eswin/sdio/fw.c b/drivers/net/wireless/eswin/sdio/fw.c
new file mode 100644
index 000000000000..9d6bab28d7e8
--- /dev/null
+++ b/drivers/net/wireless/eswin/sdio/fw.c
@@ -0,0 +1,158 @@
+/**
+******************************************************************************
+*
+* @file fw.c
+*
+* @brief ecrnx sdio firmware download functions
+*
+* Copyright (C) ESWIN 2015-2020
+*
+******************************************************************************
+*/
+
+#include <linux/firmware.h>
+#include "core.h"
+#include "sdio.h"
+#include "fw_head_check.h"
+
+extern char *fw_name;
+
+
+void eswin_fw_file_download(struct eswin *tr)
+{
+ int ret;
+ unsigned int length_all;
+ unsigned char length_str[9]={0};
+ unsigned int lengthLeft, lengthSend, offset = HEAD_SIZE;
+ const u8 * dataAddr;
+ struct sk_buff *skb;
+ int file_num = 0;
+ unsigned int file_load_addr[3] = {0x10000U, 0x60800U, 0x80000U}; // ilm addr; dlm addr offset 0x800 for bootrom log; iram0 addr
+
+ char str_sync[4] = {0x63, 0x6E, 0x79, 0x73};
+ char str_cfg[] = {0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00}; // default for sync
+
+ skb = dev_alloc_skb(1024);
+
+ ECRNX_PRINT("%s entry!!", __func__);
+
+
+ /* 1 sync */
+ memcpy(skb->data, str_sync, 4);
+ tr->ops->write(tr, skb->data, 4);
+ ret = tr->ops->wait_ack(tr);
+ ECRNX_PRINT("dl-fw >> sync, ret: %d\n", ret);
+
+
+ dataAddr = tr->fw->data;
+ length_all = tr->fw->size - offset;
+
+ while(length_all)
+ {
+ memcpy(length_str, dataAddr + offset, 8);
+ ECRNX_PRINT("-------------------------------------%s\n", length_str);
+ offset+=8;
+ length_all-=8;
+ ret = kstrtol(length_str, 10, (long*)&lengthLeft);
+ //ECRNX_PRINT("dl-fw >> file len, ret: %d len:%d\n", ret, lengthLeft);
+ if(ret==0 && lengthLeft)
+ {
+ length_all-=lengthLeft;
+
+ /* 2 cfg addr and length */
+ str_cfg[4] = (char)((file_load_addr[file_num]) & 0xFF);
+ str_cfg[5] = (char)(((file_load_addr[file_num])>>8) & 0xFF);
+ str_cfg[6] = (char)(((file_load_addr[file_num])>>16) & 0xFF);
+ str_cfg[7] = (char)(((file_load_addr[file_num])>>24) & 0xFF);
+ file_num++;
+ str_cfg[8] = (char)((lengthLeft) & 0xFF);
+ str_cfg[9] = (char)(((lengthLeft)>>8) & 0xFF);
+ str_cfg[10] = (char)(((lengthLeft)>>16) & 0xFF);
+ str_cfg[11] = (char)(((lengthLeft)>>24) & 0xFF);
+
+
+ memcpy(skb->data, &str_cfg[0], 12);
+ tr->ops->write(tr, skb->data, 12);
+ ret = tr->ops->wait_ack(tr);
+ //ECRNX_PRINT("dl-fw >> cfg, ret: %d\n", ret);
+
+
+ /* 3 load fw */
+ do {
+ lengthSend = (lengthLeft >= 1024) ? 1024 : lengthLeft; //ECO3 supprot 64K buff
+ if(lengthLeft%512==0)
+ {
+ memcpy(skb->data, dataAddr + offset, lengthSend);
+ tr->ops->write(tr, skb->data, lengthSend);
+
+ //ECRNX_PRINT("dl-fw >> ld(%d), ret: %d\n", offset/1024, ret);
+
+ ret = tr->ops->wait_ack(tr);
+ }
+ else
+ {
+ memcpy(skb->data, dataAddr + offset, lengthSend&0xFFFFFE00U);
+ tr->ops->write(tr, skb->data, lengthSend&0xFFFFFE00U);
+ //ECRNX_PRINT("dl-fw >> ld(%d), ret: %d\n", offset/1024, ret);
+ ret = tr->ops->wait_ack(tr);
+
+ memcpy(skb->data, dataAddr + offset + (int)(lengthLeft&0xFFFFFE00U), lengthSend&0x1FFU);
+ tr->ops->write(tr, skb->data, lengthSend&0x1FFU);
+ //ECRNX_PRINT("dl-fw >> ld(%d), ret: %d\n", offset/1024, ret);
+ ret = tr->ops->wait_ack(tr);
+ }
+
+ //ECRNX_PRINT("dl-fw >> ld-ack(%d), ret: %d\n", offset/1024, ret);
+ offset += lengthSend;
+ lengthLeft -= lengthSend;
+ } while(lengthLeft);
+ //ECRNX_PRINT("dl-fw >> ld, ret: %d\n", ret);
+ }
+ }
+
+ /* 4 start up */
+ memset(skb->data, 0, 12);
+ tr->ops->write(tr, skb->data, 12);
+ tr->ops->wait_ack(tr);
+
+ dev_kfree_skb(skb);
+}
+
+
+
+
+
+bool eswin_fw_file_chech(struct eswin *tr)
+{
+ int status;
+
+ if (fw_name == NULL)
+ goto err_fw;
+
+ if (tr->fw)
+ return true;
+
+ ECRNX_PRINT("%s, Checking firmware... (%s)\n", __func__, fw_name);
+
+ status = request_firmware((const struct firmware **)&tr->fw, fw_name, tr->dev);
+ if (status != 0) {
+ ECRNX_PRINT("%s, error status = %d\n", __func__, status);
+ goto err_fw;
+ }
+
+ ECRNX_PRINT("%s, request fw OK and size is %d\n",
+ __func__, tr->fw->size);
+
+ if(fw_check_head(tr) == false)
+ {
+ goto err_fw;
+ }
+ return true;
+
+
+err_fw:
+ tr->fw = NULL;
+ return false;
+}
+
+
diff --git a/drivers/net/wireless/eswin/sdio/fw.h b/drivers/net/wireless/eswin/sdio/fw.h
new file mode 100644
index 000000000000..f3af4b91b7e5
--- /dev/null
+++ b/drivers/net/wireless/eswin/sdio/fw.h
@@ -0,0 +1,19 @@
+/**
+******************************************************************************
+*
+* @file fw.h
+*
+* @brief ecrnx sdio firmware download definitions
+*
+* Copyright (C) ESWIN 2015-2020
+*
+******************************************************************************
+*/
+
+#ifndef _FW_H_
+#define _FW_H_
+
+void eswin_fw_file_download(struct eswin *tr);
+bool eswin_fw_file_chech(struct eswin *tr);
+
+#endif
diff --git a/drivers/net/wireless/eswin/sdio/sdio.c b/drivers/net/wireless/eswin/sdio/sdio.c
new file mode 100644
index 000000000000..94a8b26b5ae9
--- /dev/null
+++ b/drivers/net/wireless/eswin/sdio/sdio.c
@@ -0,0 +1,925 @@
+/**
+ ******************************************************************************
+ *
+ * @file sdio.c
+ *
+ * @brief sdio driver function definitions
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+
+#include <linux/module.h>
+#include <linux/kthread.h>
+#include <linux/mmc/sdio_func.h>
+#include <linux/mmc/sdio_ids.h>
+#include "core.h"
+#include <uapi/linux/sched/types.h>
+//#include "debug.h"
+#include "ecrnx_defs.h"
+#include "ecrnx_sdio.h"
+#include "sdio.h"
+
+#include "sdio_host_interface.h"
+
+#define SDIO_DEBUG 1
+
+
+#define SDIO_ADDR_INFO (unsigned int)(0x081)
+#define SDIO_ADDR_INFO_ASYNC (unsigned int)(0x082)
+#define SDIO_ADDR_DATA (unsigned int)(0x080)
+#define NEXT_BUF_SZ_OFFSET (0)
+#define SLAVE_BUF_SZ_OFFSET (4)
+#define SDIO_AVL_NOTIFY_FLAG (0x5A5A5A5A)
+
+static atomic_t suspend;
+int stop_tx= 0;
+
+#ifdef CONFIG_SHOW_TX_SPEED
+unsigned long sdio_tx_last_jiffies;
+unsigned long sdio_tx_len_totol;
+unsigned long sdio_tx_error_cnt;
+#endif
+
+#ifdef CONFIG_SHOW_RX_SPEED
+unsigned long sdio_rx_last_jiffies;
+unsigned long sdio_rx_len_totol;
+unsigned long sdio_rx_error_cnt;
+#endif
+
+
+struct eswin_sdio * g_sdio;
+
+#if SDIO_DEBUG
+static struct eswin_sdio *trS;
+static struct dentry *p_debugfs_sdio;
+
+static int debugfs_sdio_read(void *data, u64 *val)
+{
+ struct eswin_sdio *tr_sdio = trS;
+
+ int pdata[10];
+
+ ECRNX_PRINT("%s\n", __func__);
+ ECRNX_PRINT("%s, func: 0x%x!!", __func__, tr_sdio->func);
+ ECRNX_PRINT("%s, func: 0x%x!!", __func__, tr_sdio->slave_avl_buf);
+ ECRNX_PRINT("%s, data addr: 0x%x, sdio_info: 0x%x, 0x%x !!", __func__, pdata, &tr_sdio->sdio_info, &(tr_sdio->sdio_info));
+#if 0
+ sdio_claim_host(tr_sdio->func);
+ /* replace the sdio_memcpy_fromio with sdio_readsb. in which the op code is 0, the addr will not change during transmittion */
+ ret = sdio_readsb(tr_sdio->func, pdata/* &tr_sdio->sdio_info */, SDIO_ADDR_INFO, 4);
+ //ret = 0;
+ if (ret < 0) {
+ ECRNX_PRINT(" debugfs_sdio_read failde, ret: %d\n", ret);
+ //print_hex_dump(KERN_DEBUG, "status - 1 ", DUMP_PREFIX_NONE, 16, 1, &priv->sdio_info, 32, false);
+ sdio_release_host(tr_sdio->func);
+ return ret;
+ }
+ print_hex_dump(KERN_DEBUG, "status - 1 ", DUMP_PREFIX_NONE, 16, 1, pdata, 16, false);
+ sdio_release_host(tr_sdio->func);
+#endif
+ return 0;
+}
+
+static int debugfs_sdio_write(void *data1, u64 val)
+{
+ int ret = 0;
+ //int cmd = (int)val;
+ char cmd[16] = {1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7};
+ struct sk_buff * skb;
+ char * p;
+
+#if 1
+ struct eswin_sdio *tr_sdio = trS;
+
+ ECRNX_PRINT(" %s, tr_sdio->func: %x\n", __func__, g_sdio->func);
+
+ sdio_claim_host(tr_sdio->func);
+
+ /* replace the sdio_memcpy_xxio with sdio_xxxxsb. in sdio_xxxxsb the op code is 0, the addr will not change during transmittion */
+ ret = sdio_writesb(tr_sdio->func, 0x100, &cmd, 16);
+ if (ret < 0) {
+ ECRNX_PRINT(" debugfs_sdio_write failde, ret: %d\n", ret);
+ }
+
+ sdio_release_host(tr_sdio->func);
+#else
+
+ skb = dev_alloc_skb(4096);
+ memset(skb->data, 0x34, 4096);
+
+ p = (char *)(skb->data);
+ for (ret=0; ret<4096; ret++) {
+ *p++ = (char)ret;
+ }
+
+ sdio_host_send(skb->data, 4096, 0x100);
+#endif
+ return ret;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(debugfs_sdio,
+ debugfs_sdio_read,
+ debugfs_sdio_write,
+ "%llu\n");
+
+void debugfs_sdio_init(void)
+{
+ ECRNX_PRINT("%s\n", __func__);
+ p_debugfs_sdio = debugfs_create_file("sdiod", 0777, NULL, NULL, &debugfs_sdio);
+}
+
+void debug_sdio_rx_callback(struct sk_buff *skb)
+{
+ ECRNX_PRINT("rx-callback: %d\n", skb->len);
+ print_hex_dump(KERN_DEBUG, DBG_PREFIX_SDIO_RX, DUMP_PREFIX_NONE, 16, 1, skb->data, skb->len, false);
+ dev_kfree_skb(skb);
+}
+#endif
+
+
+static inline u16 sdio_num_slots(struct eswin_sdio *tr_sdio, int dir)
+{
+ return (tr_sdio->slot[dir].head - tr_sdio->slot[dir].tail);
+}
+
+#if 0
+static void sdio_credit_skb(struct eswin_sdio *tr_sdio)
+{
+ struct sk_buff *skb;
+ struct hif *hif;
+ struct wim *wim;
+ struct wim_credit_report *cr;
+ u8 *p;
+ int i;
+ int size = sizeof(*hif) + sizeof(struct wim) + sizeof(*cr);
+
+ if (!once) {
+ once = true;
+ return;
+ }
+
+ skb = dev_alloc_skb(size);
+
+ p = skb->data;
+ hif = (void *)p;
+ hif->type = HIF_TYPE_WIM;
+ hif->subtype = HIF_WIM_SUB_EVENT;
+ hif->vifindex = 0;
+ hif->len = sizeof(*wim) + sizeof(*cr);
+
+ p += sizeof(*hif);
+ wim = (void *)p;
+ wim->event = WIM_EVENT_CREDIT_REPORT;
+
+ p += sizeof(*wim);
+ cr = (void *)p;
+ cr->h.type = WIM_TLV_AC_CREDIT_REPORT;
+ cr->h.len = sizeof(struct wim_credit_report_param);
+
+ cr->v.change_index = 0;
+
+ for (i = 0; i < CREDIT_QUEUE_MAX; i++) {
+ u8 room = tr_sdio->front[i] - tr_sdio->rear[i];
+
+ room = min(tr_sdio->credit_max[i], room);
+ cr->v.ac[i] = tr_sdio->credit_max[i] - room;
+
+#ifdef CONFIG_NRC_HIF_PRINT_FLOW_CONTROL
+ ECRNX_PRINT(" credit[%d] %d f:%d, r:%d\n",
+ i, cr->v.ac[i], tr_sdio->front[i],
+ tr_sdio->rear[i]);
+#endif
+ }
+
+ skb_put(skb, hif->len+sizeof(*hif));
+
+ if (tr_sdio->tr->rx_callback) {
+ tr_sdio->tr->rx_callback(tr_sdio->tr->umac_priv, skb);
+ } else {
+ dev_kfree_skb(skb);
+ }
+}
+#endif
+
+static int sdio_update_status(struct eswin_sdio *tr_sdio)
+{
+ u32 rear;
+ int ac, ret;
+ u8 * buf = kmalloc(4,GFP_ATOMIC);
+
+ //ECRNX_PRINT("%s\n", __func__);
+ sdio_claim_host(tr_sdio->func);
+ trS = tr_sdio;
+ /* replace the sdio_memcpy_fromio with sdio_readsb. in which the op code is 0, the addr will not change during transmittion */
+ ret = sdio_readsb(tr_sdio->func, buf, SDIO_ADDR_INFO_ASYNC, 4);
+ if (ret < 0) {
+ ECRNX_PRINT(" sdio_update_status, ret: %d\n", ret);
+ kfree(buf);
+ //print_hex_dump(KERN_DEBUG, "status - 1 ", DUMP_PREFIX_NONE, 16, 1, &priv->sdio_info, 32, false);
+ sdio_release_host(tr_sdio->func);
+ return ret;
+ }
+
+ /*
+ if (slave_avl_buf < tr_sdio->curr_tx_size)
+ {
+ sdio_release_host(tr_sdio->func);
+ return -1;
+ }
+ */
+ spin_lock(&tr_sdio->lock);
+ tr_sdio->slave_avl_buf = *(unsigned int*)buf;
+ spin_unlock(&tr_sdio->lock);
+ kfree(buf);
+ sdio_release_host(tr_sdio->func);
+
+ return 0;
+}
+
+static void sdio_poll_status(struct work_struct *work)
+{
+ struct eswin_sdio *tr_sdio = container_of(to_delayed_work(work), struct eswin_sdio, work);
+
+ if( sdio_update_status(tr_sdio)) {
+ schedule_delayed_work(&tr_sdio->work, msecs_to_jiffies(1000));
+ } else {
+ wake_up_interruptible(&tr_sdio->wait);
+ }
+}
+
+int continue_transfer = 0;
+
+static struct sk_buff *sdio_rx_skb(struct eswin_sdio *tr_sdio)
+{
+ struct sk_buff *skb = NULL;
+ int ret;
+ int recv_len;
+ unsigned int slave_avl_buf;
+ //unsigned int slave_avl_buf_last;
+ //unsigned int real_length;
+
+ if(tr_sdio->next_rx_size)
+ {
+ //clear next rx buffer size
+ tr_sdio->next_rx_size = 0;
+ }
+ else
+ {
+ /* Wait until at least one rx slot is non-empty */
+ ret = wait_event_interruptible(tr_sdio->wait, (tr_sdio->recv_len > 1 || kthread_should_stop()));
+ if (ret < 0)
+ goto fail;
+ }
+
+
+
+ if (kthread_should_stop())
+ goto fail;
+
+ recv_len = tr_sdio->recv_len;
+ skb = dev_alloc_skb(recv_len);
+
+ sdio_claim_host(tr_sdio->func);
+
+ /* replace the sdio_memcpy_fromio with sdio_readsb. in which the op code is 0, the addr will not change during transmittion */
+ ret = sdio_readsb(tr_sdio->func, skb->data, SDIO_ADDR_DATA, recv_len);
+ if (ret){
+ print_hex_dump(KERN_DEBUG, DBG_PREFIX_SDIO_RX, DUMP_PREFIX_NONE, 16, 1, skb->data, recv_len, false);
+ ECRNX_PRINT("[eswin-err] rx-len: %d, ret %d\n", recv_len, ret);
+ stop_tx = 1;
+ sdio_release_host(tr_sdio->func);
+ goto fail;
+ }
+
+ //get next rx size
+ tr_sdio->next_rx_size=*(unsigned int *)&skb->data[NEXT_BUF_SZ_OFFSET];
+ if(tr_sdio->next_rx_size > 1)
+ {
+ tr_sdio->recv_len = tr_sdio->next_rx_size;
+ }
+ else
+ {
+ tr_sdio->recv_len = 1;
+ }
+ //get slave avl buf cnt
+ slave_avl_buf = *(unsigned int *)&skb->data[SLAVE_BUF_SZ_OFFSET] >> 16;
+ spin_lock(&tr_sdio->lock);
+ tr_sdio->slave_avl_buf = slave_avl_buf;
+ spin_unlock(&tr_sdio->lock);
+
+ skb_put(skb, recv_len);
+ skb_queue_tail(&tr_sdio->skb_rx_list,skb);
+
+ sdio_release_host(tr_sdio->func);
+
+ if (slave_avl_buf > (SDIO_PKG_MAX_CNT-1))
+ {
+ if (atomic_read(&tr_sdio->slave_buf_suspend))
+ {
+ wake_up_interruptible(&tr_sdio->wait);
+ }
+ }
+
+ //ECRNX_PRINT("rx-len: %d, skb: 0x%x \n", skb->len, skb);
+ return skb;
+
+fail:
+ if (skb)
+ dev_kfree_skb(skb);
+ return NULL;
+}
+
+
+#include <linux/sched.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 0)
+#include <uapi/linux/sched/types.h>
+#endif
+//extern int sched_setscheduler(struct task_struct *, int, const struct sched_param *);
+
+static int sdio_rx_unpack_thread(void *data)
+{
+ struct eswin_sdio *tr_sdio = (struct eswin_sdio *)data;
+ struct eswin *tr = tr_sdio->tr;
+ struct sk_buff *skb;
+ struct sk_buff *skb_frag;
+ struct sdio_rx_head_t * rx_head;
+ int ret;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 9, 0)
+ struct sched_param param = { .sched_priority = 1 };
+ param.sched_priority = 56;
+ sched_setscheduler(get_current(), SCHED_FIFO, &param);
+#else
+ sched_set_fifo(get_current());
+#endif
+ ECRNX_PRINT("rx unpack thread entry\n");
+
+ while (!kthread_should_stop())
+ {
+ ret = wait_event_interruptible(tr_sdio->wait_unpack, skb_peek(&tr_sdio->skb_rx_list)
+ || kthread_should_stop() );
+ if(kthread_should_stop())
+ continue;
+ if (ret < 0)
+ {
+ ECRNX_ERR("rx unpack thread error!\n");
+ return 0;
+ }
+ while(NULL != (skb = skb_dequeue(&tr_sdio->skb_rx_list)))
+ {
+ while (skb->len > 8)//valid data must contain 8 byte head
+ {
+ rx_head = (struct sdio_rx_head_t *)skb->data;
+ if(rx_head->data_len == 0)
+ break;
+ if (*(unsigned int *)&skb->data[8] == SDIO_AVL_NOTIFY_FLAG && rx_head->data_len == 12)
+ {
+ skb_pull(skb, ALIGN(rx_head->data_len, 4));
+ continue;
+ }
+ skb_frag = dev_alloc_skb(rx_head->data_len);
+ memcpy(skb_frag->data,skb->data,rx_head->data_len);
+ skb_put(skb_frag,rx_head->data_len);
+
+ if (skb_frag->len > skb->len)
+ {
+ ECRNX_ERR("skb len error!!! frag len:%d skb->len:%d\n",skb_frag->len,skb->len);
+ print_hex_dump(KERN_DEBUG, DBG_PREFIX_SDIO_RX, DUMP_PREFIX_NONE, 16, 1, skb->data, skb->len, false);
+ BUG_ON(1);
+ }
+ tr->rx_callback(tr->umac_priv, skb_frag);
+ skb_pull(skb,ALIGN(rx_head->data_len, 4));
+ }
+ dev_kfree_skb(skb);
+ }
+ }
+ ECRNX_PRINT("rx unpack thread exit\n");
+ return 0;
+}
+
+static int sdio_rx_thread(void *data)
+{
+ struct eswin_sdio *tr_sdio = (struct eswin_sdio *)data;
+ struct eswin *tr = tr_sdio->tr;
+ struct sk_buff *skb;
+ struct sk_buff *skb_frag;
+ struct sdio_rx_head_t * rx_head;
+ int is_suspend;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 9, 0)
+ struct sched_param param = { .sched_priority = 1 };
+ param.sched_priority = 56;
+ sched_setscheduler(get_current(), SCHED_FIFO, &param);
+#else
+ sched_set_fifo(get_current());
+#endif
+ ECRNX_PRINT("rx thread entry, loopbakc: %d\n", tr->loopback);
+
+ while (!kthread_should_stop())
+ {
+ skb = sdio_rx_skb(tr_sdio);
+ is_suspend = atomic_read(&suspend);
+ ECRNX_DBG("rx_cb: 0x%x, skb: 0x%x, is_suspend: 0x%x \n", tr->rx_callback, skb, is_suspend);
+ if ((!tr->rx_callback) || (skb && is_suspend)){
+ dev_kfree_skb(skb);
+ }
+ else if (skb){
+ wake_up_interruptible(&tr_sdio->wait_unpack);
+#if 0
+ while (skb->len > 8)//valid data must contain 8 byte head
+ {
+ //printk("skb len 0:%d",skb->len);
+ rx_head = (struct sdio_rx_head_t *)skb->data;
+ if(rx_head->data_len == 0)
+ break;
+ skb_frag = dev_alloc_skb(rx_head->data_len);
+ memcpy(skb_frag->data,skb->data,rx_head->data_len);
+ skb_put(skb_frag,rx_head->data_len);
+ //printk("skb_frag len 1:%d",skb_frag->len);
+
+ if (skb_frag->len > skb->len)
+ {
+ printk("skb len error!!! frag len:%d skb->len:%d\n",skb_frag->len,skb->len);
+ print_hex_dump(KERN_DEBUG, "eswin-rx: ", DUMP_PREFIX_NONE, 16, 1, skb->data, skb->len, false);
+ BUG_ON(1);
+ }
+
+ tr->rx_callback(tr->umac_priv, skb_frag);
+ skb_pull(skb,ALIGN(rx_head->data_len, 4));
+ //printk("skb len 1:%d",skb->len);
+ //break;
+
+ }
+ dev_kfree_skb(skb);
+ //dev_alloc_skb
+ //tr->rx_callback(tr->umac_priv, skb);
+ //debug_sdio_rx_callback(skb);
+#endif
+ }
+ else {
+ ECRNX_ERR("rx-head: %d, rx-tail: %d, rx-cnt: %d\n",
+ tr_sdio->slot[RX_SLOT].head, tr_sdio->slot[RX_SLOT].tail, tr_sdio->sdio_info.info_rd);
+ break;
+ }
+ }
+ ECRNX_PRINT("rx thread exit\n");
+
+ return 0;
+}
+
+#if 0
+static int sdio_noop(struct eswin *tr, struct sk_buff *skb)
+{
+ ECRNX_PRINT("%s exit!!", __func__);
+ return 0;
+}
+#endif
+
+void dump_sdio_buf(struct tx_buff_node *node)
+{
+ int i = 0;
+
+ ECRNX_PRINT("sdio tx len %d\n", node?node->len:-1);
+ /*for (i = 0; i < node->len; ++i) {
+ if (i % 16 == 0 && i) {
+ printk("\n");
+ }
+ printk("%02x ", ((unsigned char *)(node->buff))[i]);
+ }*/
+
+}
+
+static int sdio_xmit(struct eswin *tr, struct tx_buff_pkg_node *node)
+{
+ struct eswin_sdio *tr_sdio = (struct eswin_sdio *)tr->drv_priv;
+ struct eswin * tr1 = tr_sdio->tr;
+ int ret,flag;
+ unsigned int slave_avl_buf = 0;
+
+ //dump_sdio_buf(node);
+ spin_lock(&tr_sdio->lock);
+ slave_avl_buf = tr_sdio->slave_avl_buf;
+ spin_unlock(&tr_sdio->lock);
+ if(slave_avl_buf < node->node_cnt && (node->flag & FLAG_MSG_TYPE_MASK) == TX_FLAG_TX_DESC)
+ {
+ atomic_set(&tr_sdio->slave_buf_suspend, 1);
+ ret = wait_event_interruptible(tr_sdio->wait, node->node_cnt < tr_sdio->slave_avl_buf);
+ if (ret < 0)
+ {
+ ECRNX_PRINT("[transa] sdio_xmit, wait_event_interruptible fail, ret = %d slave_avl_buf=%d\n", ret, tr_sdio->slave_avl_buf);
+ return ret;
+ }
+ cancel_delayed_work_sync(&tr_sdio->work);
+ atomic_set(&tr_sdio->slave_buf_suspend, 0);
+ }
+ sdio_claim_host(tr_sdio->func);
+ /* replace the sdio_memcpy_xxio with sdio_xxxxsb. in sdio_xxxxsb the op code is 0, the addr will not change during transmittion */
+ ret = sdio_writesb(tr_sdio->func, node->flag, node->buff, node->len);
+ if(ret) {
+ //stop_tx = 1;
+ ECRNX_ERR("sdio_xmit error, len = %d, ret:%d slave_avl_buf=%d ,node->node_cnt :%d\n",\
+ node->len, ret, tr_sdio->slave_avl_buf,node->node_cnt);
+ print_hex_dump(KERN_DEBUG, DBG_PREFIX_SDIO_TX, DUMP_PREFIX_NONE, 32, 1,
+ node->buff, 64, false);
+ }
+
+ spin_lock(&tr_sdio->lock);
+ if ((node->flag & FLAG_MSG_TYPE_MASK) == TX_FLAG_TX_DESC)
+ {
+ tr_sdio->slave_avl_buf -= node->node_cnt;
+ }
+ spin_unlock(&tr_sdio->lock);
+
+ sdio_release_host(tr_sdio->func);
+ ECRNX_DBG(" sdio_xmit ok, len = %d, flag: 0x%02x!\n", node->len, node->flag);
+ return ret;
+}
+
+static int sdio_start(struct eswin *tr)
+{
+ int ret=0;
+
+ struct eswin_sdio *tr_sdio = (struct eswin_sdio *)tr->drv_priv;
+
+ ECRNX_PRINT("%s\n", __func__);
+
+ /* Start rx thread */
+ //if(tr->loopback == 1)
+ // return 0;
+ tr_sdio->curr_tx_size = 0;
+ ret = sdio_update_status(tr_sdio);
+ INIT_DELAYED_WORK(&tr_sdio->work, sdio_poll_status);
+
+ tr_sdio->kthread = kthread_run(sdio_rx_thread, tr_sdio, "sdio-rx");
+ tr_sdio->kthread_unpack = kthread_run(sdio_rx_unpack_thread, tr_sdio, "sdio-rx-unpack");
+
+ atomic_set(&suspend, 0);
+ atomic_set(&tr_sdio->slave_buf_suspend, 0);
+ spin_lock_init(&tr_sdio->lock);
+
+ return ret;
+}
+
+
+static int sdio_suspend(struct eswin *tr)
+{
+ atomic_set(&suspend, 1);
+ return 0;
+}
+
+static int sdio_resume(struct eswin *tr)
+{
+ atomic_set(&suspend, 0);
+ return 0;
+}
+
+
+static int sdio_raw_write(struct eswin *tr, const void *data, const u32 len)
+{
+ int ret;
+ struct eswin_sdio *tr_sdio = (struct eswin_sdio *)tr->drv_priv;
+
+ ECRNX_PRINT(" %s, entry~", __func__);
+
+ sdio_claim_host(tr_sdio->func);
+ /* replace the sdio_memcpy_xxio with sdio_xxxxsb. in sdio_xxxxsb the op code is 0, the addr will not change during transmittion */
+ ret = sdio_writesb(tr_sdio->func, SDIO_ADDR_DATA, data, len);
+ sdio_release_host(tr_sdio->func);
+
+ return ret;
+}
+
+
+static int sdio_wait_ack(struct eswin *tr)
+{
+ int data = 0;
+ u8 * buf = kmalloc(4,GFP_ATOMIC);
+ struct eswin_sdio *tr_sdio = (struct eswin_sdio *)tr->drv_priv;
+
+ ECRNX_PRINT(" %s, entry~", __func__);
+
+ sdio_claim_host(tr_sdio->func);
+ /* replace the sdio_memcpy_xxio with sdio_xxxxsb. in sdio_xxxxsb the op code is 0, the addr will not change during transmittion */
+ sdio_readsb(tr_sdio->func, buf, SDIO_ADDR_DATA, 1);
+ sdio_release_host(tr_sdio->func);
+ data = *(int *)buf;
+ kfree(buf);
+
+ return data;
+}
+
+
+
+static struct sdio_ops eswin_sdio_ops = {
+ .start = sdio_start,
+ .xmit = sdio_xmit,
+ .suspend = sdio_suspend,
+ .resume = sdio_resume,
+ .write = sdio_raw_write,
+ .wait_ack = sdio_wait_ack,
+};
+
+
+
+static void eswin_sdio_irq_handler(struct sdio_func *func)
+{
+ struct eswin_sdio *tr_sdio = sdio_get_drvdata(func);
+ u32 rear;
+ unsigned char lowbyte, highbyte;
+ int ret, ac;
+
+ //printk(" %s, entry~\n", __func__);
+
+ sdio_claim_host(tr_sdio->func);
+ lowbyte = sdio_readb(tr_sdio->func, 0x00, &ret);
+ highbyte = sdio_readb(tr_sdio->func, 0x01, &ret);
+ tr_sdio->recv_len = (highbyte << 8) | lowbyte;
+ //printk("%s %u, %hhu, %hhu!", __func__, tr_sdio->recv_len, highbyte, lowbyte);
+
+#if 0
+ //_info(" eswin_sdio_irq_handler, len: %d\n", tr_sdio->recv_len);
+ if(tr_sdio->recv_len == 1) {
+
+ ret = sdio_memcpy_fromio(tr_sdio->func, &tr_sdio->sdio_info, SDIO_ADDR_INFO, 0x10 /*priv->recv_len*/);
+ if (ret < 0) {
+ ECRNX_PRINT(" eswin_sdio_irq_handler, info-ret: %d\n", ret);
+ //print_hex_dump(KERN_DEBUG, "status - 2 ", DUMP_PREFIX_NONE, 16, 1, &priv->sdio_info, 32, false);
+ sdio_release_host(tr_sdio->func);
+ return;
+ }
+ //ECRNX_PRINT(" get info\n");
+ //ECRNX_PRINT(" eswin_sdio_irq_handler, info-wr: %#x, info-rd: %#x\n", priv->sdio_info.info_wr, priv->sdio_info.info_rd);
+
+ tr_sdio->slot[TX_SLOT].head = tr_sdio->sdio_info.info_wr;
+ tr_sdio->slot[RX_SLOT].head = tr_sdio->sdio_info.info_rd;
+
+
+ //if (hdev->nw->loopback)
+ // return;
+
+ if((tr_sdio->sdio_info.credit_vif0 != tr_sdio->credit_vif0)
+ ||(tr_sdio->sdio_info.credit_vif1 != tr_sdio->credit_vif1)){
+ /* Update VIF0 credit */
+ rear = tr_sdio->sdio_info.credit_vif0;
+ tr_sdio->credit_vif0 = rear;
+ for (ac = 0; ac < 4; ac++)
+ tr_sdio->rear[ac] = (rear >> 8*ac) & 0xff;
+
+
+ /* Update VIF1 credit */
+ rear = tr_sdio->sdio_info.credit_vif1;
+ tr_sdio->credit_vif1 = rear;
+ for (ac = 0; ac < 4; ac++)
+ tr_sdio->rear[6+ac] = (rear >> 8*ac) & 0xff;
+
+ //need_credit = 1;
+ //sdio_release_host(tr_sdio->func);
+ //sdio_credit_skb(tr_sdio);
+ //sdio_claim_host(func);
+ }
+#if 0
+ printk("irq: wr: %d, rd: %d, credit:%d/%d, %d/%d, %d/%d, %d/%d\n",
+ priv->sdio_info.info_wr, priv->sdio_info.info_rd,
+ priv->rear[3], priv->front[3],
+ priv->rear[2], priv->front[2],
+ priv->rear[1], priv->front[1],
+ priv->rear[0], priv->front[0]);
+#endif
+
+#ifdef CONFIG_NRC_HIF_PRINT_FLOW_CONTROL
+ //nrc_dbg(NRC_DBG_HIF, "-%s\n", __func__);
+#endif
+
+ }
+ else
+ {
+ //ECRNX_PRINT(" get data len: %d\n", priv->recv_len);
+ }
+#endif
+ sdio_release_host(tr_sdio->func);
+ wake_up_interruptible(&tr_sdio->wait);
+}
+
+static void eswin_sdio_irq2_handler(struct sdio_func *func)
+{
+ struct eswin_sdio *tr_sdio = g_sdio;
+ unsigned char lowbyte, highbyte;
+ int ret, recv_len;
+ struct sk_buff * skb;
+
+ ECRNX_PRINT(" %s, entry~\n", __func__);
+
+ sdio_claim_host(func);
+ lowbyte = sdio_readb(func, 0x00, &ret);
+ highbyte = sdio_readb(func, 0x01, &ret);
+ sdio_release_host(func);
+
+ recv_len = (highbyte << 8) | lowbyte;
+ skb = dev_alloc_skb(recv_len);
+ skb_put(skb, recv_len);
+
+ sdio_claim_host(func);
+ /* replace the sdio_memcpy_fromio with sdio_readsb. in which the op code is 0, the addr will not change during transmittion */
+ ret = sdio_readsb(func, skb->data, SDIO_ADDR_DATA, recv_len);
+ if (ret){
+ ECRNX_ERR("[eswin-err] rx-len: %d\n", skb->len);
+ }
+
+ sdio_release_host(func);
+
+ print_hex_dump(KERN_DEBUG, DBG_PREFIX_SDIO_RX, DUMP_PREFIX_NONE, 16, 1, skb->data, skb->len, false);
+ dev_kfree_skb(skb);
+}
+
+struct device *eswin_sdio_get_dev(void *plat)
+{
+ struct eswin* tr = (struct eswin*)plat;
+
+ return tr->dev;
+}
+
+
+static int eswin_sdio_probe(struct sdio_func *func, const struct sdio_device_id *id)
+{
+ int ret;
+ struct eswin_sdio * tr_sdio;
+ struct eswin* tr;
+
+ ECRNX_PRINT("%s entry, func: %d!!", __func__, func->num);
+
+ if (func->num == 1) {
+ tr = eswin_core_create(sizeof(* tr_sdio), &func->dev, &eswin_sdio_ops);
+ if(!tr) {
+ dev_err(&func->dev, "failed to allocate core\n");
+ return -ENOMEM;
+ }
+
+ tr_sdio = (struct eswin_sdio *)tr->drv_priv;
+ g_sdio = tr_sdio;
+ tr_sdio->tr = tr;
+ tr_sdio->func = func;
+ } else {
+ g_sdio->func2 = func;
+
+ sdio_claim_host(func);
+ sdio_enable_func(func);
+ func->max_blksize = ESWIN_SDIO_BLK_SIZE;
+ sdio_set_block_size(func, func->max_blksize);
+ sdio_claim_irq(func, eswin_sdio_irq2_handler);
+ sdio_release_host(func);
+ return 0;
+ }
+
+ tr_sdio->slot[TX_SLOT].size = 456;
+ tr_sdio->slot[RX_SLOT].size = 492;
+
+ tr_sdio->credit_max[0] = CREDIT_AC0;
+ tr_sdio->credit_max[1] = CREDIT_AC1;
+ tr_sdio->credit_max[2] = CREDIT_AC2;
+ tr_sdio->credit_max[3] = CREDIT_AC3;
+
+ tr_sdio->credit_max[6] = CREDIT_AC0;
+ tr_sdio->credit_max[7] = CREDIT_AC1;
+ tr_sdio->credit_max[8] = CREDIT_AC2;
+ tr_sdio->credit_max[9] = CREDIT_AC3;
+
+ init_waitqueue_head(&tr_sdio->wait);
+ init_waitqueue_head(&tr_sdio->wait_unpack);
+
+ func->max_blksize = ESWIN_SDIO_BLK_SIZE;
+
+ skb_queue_head_init(&tr_sdio->skb_rx_list);
+ //skb_queue_head_init(tr_sdio->skb_rx_unpack_list);
+
+ sdio_claim_host(func);
+
+ ret = sdio_enable_func(func);
+ if(ret) {
+ ECRNX_PRINT("failed to enable sdio func\n");
+ goto release;
+ }
+
+ ret = sdio_set_block_size(func, func->max_blksize);
+ if(ret) {
+ ECRNX_PRINT("failed to set sdio func block size\n");
+ goto release;
+ }
+
+ ret = sdio_claim_irq(func, eswin_sdio_irq_handler);
+ if(ret) {
+ ECRNX_PRINT("failed to claim sdio irq\n");
+ goto release;
+ }
+
+ sdio_release_host(func);
+ sdio_set_drvdata(func, tr_sdio);
+
+#ifdef CONFIG_SHOW_TX_SPEED
+ sdio_tx_last_jiffies = jiffies;
+ sdio_tx_len_totol = 0;
+ sdio_tx_error_cnt = 0;
+#endif
+
+#ifdef CONFIG_SHOW_RX_SPEED
+ sdio_rx_last_jiffies = jiffies;
+ sdio_rx_len_totol = 0;
+ sdio_rx_error_cnt = 0;
+#endif
+
+
+ ret = eswin_core_register(tr);
+ if(ret) {
+ ECRNX_PRINT("failed to register core\n");
+
+ }
+
+ debugfs_sdio_init();
+
+ ECRNX_PRINT("%s exit!!", __func__);
+ return 0;
+
+release:
+ sdio_release_host(func);
+ return ret;
+}
+
+static void eswin_sdio_remove(struct sdio_func *func)
+{
+ struct eswin_sdio *tr_sdio = sdio_get_drvdata(func);
+ struct eswin *tr = tr_sdio->tr;
+
+
+ ECRNX_PRINT(" %s entry!!\n", __func__);
+ debugfs_remove_recursive(p_debugfs_sdio);
+
+ eswin_core_unregister(tr);
+
+ sdio_claim_host(func);
+ sdio_release_irq(func);
+ sdio_disable_func(func);
+ sdio_release_host(func);
+
+ kthread_stop(tr_sdio->kthread);
+ wake_up_interruptible(&tr_sdio->wait);
+ kthread_stop(tr_sdio->kthread_unpack);
+ wake_up_interruptible(&tr_sdio->wait_unpack);
+
+ eswin_core_destroy(tr);
+ ECRNX_PRINT(" %s exit!!\n", __func__);
+}
+
+
+static const struct sdio_device_id eswin_sdio_dev[] =
+{
+ { SDIO_DEVICE(ESWIN_SDIO_VENDER, ESWIN_SDIO_DEVICE) },
+ {},
+};
+
+static struct sdio_driver eswin_sdio_driver =
+{
+ .name = "eswin_sdio",
+ .id_table = eswin_sdio_dev,
+ .probe = eswin_sdio_probe,
+ .remove = eswin_sdio_remove,
+};
+
+static int __init eswin_sdio_init(void)
+{
+ int ret;
+ ECRNX_PRINT(" %s entry!!", __func__);
+
+ ret = sdio_register_driver(&eswin_sdio_driver);
+ if (ret)
+ ECRNX_PRINT("sdio driver registration failed: %d\n", ret);
+
+ ECRNX_PRINT(" %s exit!!", __func__);
+ return ret;
+}
+
+static void __exit eswin_sdio_exit(void)
+{
+ ECRNX_PRINT(" %s entry!!", __func__);
+ sdio_unregister_driver(&eswin_sdio_driver);
+ ECRNX_PRINT(" %s exit!!", __func__);
+}
+
+int ecrnx_sdio_register_drv(void)
+{
+ return eswin_sdio_init();
+}
+
+void ecrnx_sdio_unregister_drv(void)
+{
+ return eswin_sdio_exit();
+}
+
+//module_init(eswin_sdio_init);
+//module_exit(eswin_sdio_exit);
+
+//MODULE_AUTHOR("Transa-Semi");
+//MODULE_LICENSE("Dual BSD/GPL");
+//MODULE_DESCRIPTION("Driver support for Transa-Semi 802.11 WLAN SDIO driver");
+//MODULE_SUPPORTED_DEVICE("Transa-Semi 802.11 devices");
+
diff --git a/drivers/net/wireless/eswin/sdio/sdio.h b/drivers/net/wireless/eswin/sdio/sdio.h
new file mode 100644
index 000000000000..6dde7c1e546d
--- /dev/null
+++ b/drivers/net/wireless/eswin/sdio/sdio.h
@@ -0,0 +1,156 @@
+/**
+ ******************************************************************************
+ *
+ * @file sdio.h
+ *
+ * @brief sdio driver definitions
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+
+#ifndef __SDIO_H
+#define __SDIO_H
+
+#include "ecrnx_defs.h"
+#include "core.h"
+
+#define ESWIN_SDIO_VENDER 0x0296
+#define ESWIN_SDIO_DEVICE 0x5347
+
+#define ESWIN_SDIO_BLK_SIZE 512
+
+#define TX_SLOT 0
+#define RX_SLOT 1
+
+#define CREDIT_QUEUE_MAX (12)
+
+
+#define TCN (3*2)
+#define TCNE (0)
+
+#define CREDIT_AC0 4//(TCN*4+TCNE) /* BK */
+#define CREDIT_AC1 30//(TCN*3+TCNE) /* BE */
+#define CREDIT_AC2 4//(TCN*2+TCNE) /* VI */
+#define CREDIT_AC3 4//(TCN*1+TCNE) /* VO */
+
+struct sdio_sys_reg {
+ u8 wakeup; /* 0x0 */
+ u8 status; /* 0x1 */
+ u16 chip_id; /* 0x2-0x3 */
+ u32 modem_id; /* 0x4-0x7 */
+ u32 sw_id; /* 0x8-0xb */
+ u32 board_id; /* 0xc-0xf */
+} __packed;
+
+struct sdio_status_reg {
+ struct {
+ u8 mode;
+ u8 enable;
+ u8 latched_status;
+ u8 status;
+ } eirq;
+ u8 txq_status[6];
+ u8 rxq_status[6];
+ u32 msg[4];
+
+#define EIRQ_IO_ENABLE (1<<2)
+#define EIRQ_EDGE (1<<1)
+#define EIRQ_ACTIVE_LO (1<<0)
+
+#define EIRQ_DEV_SLEEP (1<<3)
+#define EIRQ_DEV_READY (1<<2)
+#define EIRQ_RXQ (1<<1)
+#define EIRQ_TXQ (1<<0)
+
+#define TXQ_ERROR (1<<7)
+#define TXQ_SLOT_COUNT (0x7F)
+#define RXQ_SLOT_COUNT (0x7F)
+
+} __packed;
+
+struct sdio_rx_head_t {
+ unsigned int next_rx_len;
+ unsigned short data_len;
+ unsigned short avl_len;
+};
+
+struct sdio_data_t {
+ unsigned int credit_vif0;
+ unsigned int credit_vif1;
+ unsigned int info_wr;
+ unsigned int info_rd;
+};
+
+struct eswin_sdio {
+ struct eswin * tr;
+ struct sdio_func *func;
+ struct sdio_func *func2;
+
+ /* work, kthread, ... */
+ struct delayed_work work;
+ struct task_struct *kthread;
+ wait_queue_head_t wait; /* wait queue */
+
+ struct task_struct *kthread_unpack;
+ wait_queue_head_t wait_unpack;
+
+ struct {
+ struct sdio_sys_reg sys;
+ struct sdio_status_reg status;
+ } hw;
+
+ spinlock_t lock;
+ struct {
+ unsigned int head;
+ unsigned int tail;
+ unsigned int size;
+ unsigned int count;
+ } slot[2];
+ /* VIF0(AC0~AC3), BCN, CONC, VIF1(AC0~AC3), padding*/
+ u8 front[CREDIT_QUEUE_MAX];
+ u8 rear[CREDIT_QUEUE_MAX];
+ u8 credit_max[CREDIT_QUEUE_MAX];
+
+/*
+ unsigned long loopback_prev_cnt;
+ unsigned long loopback_total_cnt;
+ unsigned long loopback_last_jiffies;
+ unsigned long loopback_read_usec;
+ unsigned long loopback_write_usec;
+ unsigned long loopback_measure_cnt;
+*/
+ //struct eswin_sdio_ops_t *ops;
+ unsigned int recv_len;
+ unsigned int recv_num;
+// struct dentry *debugfs;
+
+ unsigned int credit_vif0;
+ unsigned int credit_vif1;
+
+ struct sdio_data_t sdio_info;
+ unsigned int slave_avl_buf;
+ atomic_t slave_buf_suspend;
+ unsigned int curr_tx_size;
+ unsigned int next_rx_size;
+// struct sk_buff *skb_tx_last;
+
+ struct sk_buff_head skb_rx_list;
+ //struct sk_buff_head *skb_rx_unpack_list;
+
+};
+
+struct sdio_ops {
+ int (*start)(struct eswin *tr);
+ int (*xmit)(struct eswin *tr, struct tx_buff_pkg_node * node);
+ int (*suspend)(struct eswin *tr);
+ int (*resume)(struct eswin *tr);
+ int (*write)(struct eswin *tr, const void* data, const u32 len);
+ int (*wait_ack)(struct eswin *tr);
+};
+
+extern int ecrnx_sdio_register_drv(void);
+extern void ecrnx_sdio_unregister_drv(void);
+
+#endif /* __SDIO_H */
diff --git a/drivers/net/wireless/eswin/sdio/sdio_host_interface.h b/drivers/net/wireless/eswin/sdio/sdio_host_interface.h
new file mode 100644
index 000000000000..146047976868
--- /dev/null
+++ b/drivers/net/wireless/eswin/sdio/sdio_host_interface.h
@@ -0,0 +1,29 @@
+/**
+ ******************************************************************************
+ *
+ * @file sdio_host_interface.h
+ *
+ * @brief sdio host interface definitions
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+
+#ifndef _SDIO_HOST_INTERFACE_H
+#define _SDIO_HOST_INTERFACE_H
+/*******************************************************************************
+ * Function: sdio_xmit
+ * Description:send buff from host to slave
+ * Parameters:
+ * Input: void *buff, int len, int flag
+ *
+ * Output:
+ *
+ * Returns: 0
+ *
+ *
+ * Others:
+ ********************************************************************************/
+int sdio_host_send(void *buff, int len, int flag);
+#endif \ No newline at end of file
diff --git a/drivers/net/wireless/eswin/slave_log_buf.c b/drivers/net/wireless/eswin/slave_log_buf.c
new file mode 100644
index 000000000000..99009e4372d8
--- /dev/null
+++ b/drivers/net/wireless/eswin/slave_log_buf.c
@@ -0,0 +1,149 @@
+#include "slave_log_buf.h"
+
+spinlock_t lock;
+
+uint32_t ring_buffer_init(struct ring_buffer* ring_buf, uint32_t size)
+{
+ if(ring_buf== NULL)
+ return false;
+
+ if (!is_power_of_2(size))
+ {
+ printk("size must be power of 2.\n");
+ return false;
+ }
+
+ memset(ring_buf, 0, sizeof(struct ring_buffer));
+ spin_lock_init(&lock);
+ ring_buf->buffer = kmalloc(size,GFP_KERNEL);
+ ring_buf->size = size;
+ ring_buf->write_point = 0;
+ ring_buf->read_point = 0;
+ ring_buf->f_lock = &lock;
+ ring_buf->init = true;
+ ring_buf->cover = false;
+ ring_buf->show = false;
+ return true;
+}
+
+void ring_buffer_deinit(struct ring_buffer *ring_buf)
+{
+ memset(ring_buf, 0, sizeof(struct ring_buffer));
+ if(ring_buf->buffer != NULL)
+ {
+ kfree(ring_buf->buffer);
+ ring_buf->buffer = NULL;
+ }
+
+}
+
+
+uint32_t __ring_buffer_len(const struct ring_buffer *ring_buf)
+{
+ if(ring_buf->cover == false)
+ {
+ return ring_buf->write_point;
+ }
+ if(ring_buf->show == true)
+ {
+ if(ring_buf->write_point < ring_buf->read_point)
+ return (ring_buf->write_point + ring_buf->size - ring_buf->read_point);
+ else
+ return (ring_buf->write_point - ring_buf->read_point);
+ }
+
+ return ring_buf->size;
+}
+
+
+uint32_t __ring_buffer_get(struct ring_buffer *ring_buf, void * buffer, uint32_t size)
+{
+ if((ring_buf== NULL) || (buffer== NULL))
+ return 0;
+
+ uint32_t copy_len = 0;
+ uint32_t read_len = 0;
+ if(ring_buf->write_point < ring_buf->read_point)
+ read_len = (ring_buf->write_point + ring_buf->size - ring_buf->read_point);
+ else
+ read_len = (ring_buf->write_point - ring_buf->read_point);
+
+ size = min(size, read_len);
+ /* first get the data from fifo->read_point until the end of the buffer */
+ copy_len = min(size, ring_buf->size - ring_buf->read_point);
+ memcpy(buffer, ring_buf->buffer + ring_buf->read_point, copy_len);
+ /* then get the rest (if any) from the beginning of the buffer */
+ if(size - copy_len > 0)
+ {
+ memcpy(buffer + copy_len, ring_buf->buffer, size - copy_len);
+ }
+
+ ring_buf->read_point += size;
+ ring_buf->read_point = (ring_buf->read_point & (ring_buf->size - 1));
+
+ return size;
+}
+//Ïò»º³åÇøÖдæ·ÅÊý¾Ý
+uint32_t __ring_buffer_put(struct ring_buffer *ring_buf, void *buffer, uint32_t size)
+{
+ if((ring_buf == NULL) || (buffer == NULL))
+ {
+ return 0;
+ }
+ uint32_t copy_len = 0;
+
+ /* first put the data starting from fifo->write_point to buffer end */
+ copy_len = min(size, ring_buf->size - ring_buf->write_point);
+
+ memcpy(ring_buf->buffer + ring_buf->write_point, buffer, copy_len);
+ /* then put the rest (if any) at the beginning of the buffer */
+ if(size - copy_len > 0)
+ {
+ memcpy(ring_buf->buffer, buffer + copy_len, size - copy_len);
+ ring_buf->cover = true;
+ }
+
+ ring_buf->write_point += size;
+ ring_buf->write_point = (ring_buf->write_point & (ring_buf->size - 1));
+ return size;
+}
+
+uint32_t ring_buffer_len(const struct ring_buffer *ring_buf)
+{
+ uint32_t len = 0;
+ spin_lock_irq(ring_buf->f_lock);
+ len = __ring_buffer_len(ring_buf);
+ spin_unlock_irq(ring_buf->f_lock);
+ return len;
+}
+
+uint32_t ring_buffer_get(struct ring_buffer *ring_buf, void *buffer, uint32_t size)
+{
+ uint32_t ret;
+ spin_lock_irq(ring_buf->f_lock);
+ ret = __ring_buffer_get(ring_buf, buffer, size);
+ spin_unlock_irq(ring_buf->f_lock);
+ return ret;
+}
+
+uint32_t ring_buffer_put(struct ring_buffer *ring_buf, void *buffer, uint32_t size)
+{
+ uint32_t ret;
+ spin_lock_irq(ring_buf->f_lock);
+ ret = __ring_buffer_put(ring_buf, buffer, size);
+ spin_unlock_irq(ring_buf->f_lock);
+ return ret;
+}
+uint32_t ring_buffer_scrolling_display(struct ring_buffer *ring_buf, char show)
+{
+ uint32_t ret = true;
+ spin_lock_irq(ring_buf->f_lock);
+ ring_buf->show = show;
+ if((ring_buf->cover == true)&&(show == true))
+ {
+ ring_buf->read_point = (ring_buf->write_point & (ring_buf->size - 1)) + 1;
+ }
+ spin_unlock_irq(ring_buf->f_lock);
+ return ret;
+}
+
diff --git a/drivers/net/wireless/eswin/slave_log_buf.h b/drivers/net/wireless/eswin/slave_log_buf.h
new file mode 100644
index 000000000000..bd756e7278c7
--- /dev/null
+++ b/drivers/net/wireless/eswin/slave_log_buf.h
@@ -0,0 +1,37 @@
+#ifndef _SLAVE_LOG_BUF_H_
+#define _SLAVE_LOG_BUF_H_
+#include <linux/firmware.h>
+#include "core.h"
+#include "ecrnx_debug.h"
+
+
+#ifndef uint32_t
+#define uint32_t unsigned int
+#endif
+
+
+#define is_power_of_2(x) ((x) != 0 && (((x) & ((x) - 1)) == 0))
+
+//#define min(a, b) (((a) < (b)) ? (a) : (b))
+
+struct ring_buffer
+{
+ void *buffer;
+ uint32_t size;
+ uint32_t write_point;
+ uint32_t read_point;
+ char cover;
+ char show;
+ char init;
+ spinlock_t *f_lock;
+};
+
+uint32_t ring_buffer_get(struct ring_buffer *ring_buf, void *buffer, uint32_t size);
+uint32_t ring_buffer_put(struct ring_buffer *ring_buf, void *buffer, uint32_t size);
+uint32_t ring_buffer_init(struct ring_buffer* ring_buf, uint32_t size);
+void ring_buffer_deinit(struct ring_buffer *ring_buf);
+uint32_t ring_buffer_scrolling_display(struct ring_buffer *ring_buf, char show);
+uint32_t ring_buffer_len(const struct ring_buffer *ring_buf);
+
+
+#endif
diff --git a/drivers/net/wireless/eswin/usb/core.c b/drivers/net/wireless/eswin/usb/core.c
new file mode 100644
index 000000000000..89b33b1cf45b
--- /dev/null
+++ b/drivers/net/wireless/eswin/usb/core.c
@@ -0,0 +1,212 @@
+#include <linux/firmware.h>
+#include "core.h"
+#include "fw.h"
+//#include "debug.h"
+#include "ecrnx_platform.h"
+#include "usb.h"
+#include "ecrnx_rx.h"
+#include "eswin_utils.h"
+#include "ecrnx_defs.h"
+#include "usb_host_interface.h"
+
+bool loopback;
+module_param(loopback, bool, S_IRUSR | S_IWUSR);
+MODULE_PARM_DESC(loopback, "HIF loopback");
+
+int power_save;
+module_param(power_save, int, S_IRUSR | S_IWUSR);
+MODULE_PARM_DESC(power_save, "Power Save(0: disable, 1:enable)");
+
+int disable_cqm = 0;
+module_param(disable_cqm, int, S_IRUSR | S_IWUSR);
+MODULE_PARM_DESC(disable_cqm, "Disable CQM (0: disable, 1:enable)");
+
+
+int listen_interval = 0;
+module_param(listen_interval, int, S_IRUSR | S_IWUSR);
+MODULE_PARM_DESC(listen_interval, "Listen Interval");
+
+int bss_max_idle = 0;
+module_param(bss_max_idle, int, S_IRUSR | S_IWUSR);
+MODULE_PARM_DESC(bss_max_idle, "BSS Max Idle");
+
+
+bool dl_fw = true;
+module_param(dl_fw, bool, S_IRUSR | S_IWUSR);
+MODULE_PARM_DESC(dl_fw, "download firmware");
+
+
+#ifdef CONFIG_ECRNX_WIFO_CAIL
+bool amt_mode;
+module_param(amt_mode, bool, S_IRUSR | S_IWUSR);
+MODULE_PARM_DESC(amt_mode, "calibrate mode");
+#endif
+
+bool set_gain;
+module_param(set_gain, bool, S_IRUSR | S_IWUSR);
+MODULE_PARM_DESC(set_gain, "set gain delta");
+
+char *fw_name = NULL;
+struct eswin *pEswin = NULL;
+bool usb_status = false;
+
+module_param(fw_name, charp, S_IRUGO);
+MODULE_PARM_DESC(fw_name, "Firmware file name");
+
+extern int ecrnx_data_cfm_callback(void *priv, void *host_id);
+extern int ecrnx_msg_cfm_callback(void *priv, void *host_id);
+static void eswin_core_register_work(struct work_struct *work)
+{
+ int ret;
+ struct eswin *tr = container_of(work, struct eswin, register_work.work);
+
+ ECRNX_DBG("%s entry, dl_fw = %d!!", __func__, dl_fw);
+
+ if(dl_fw){
+ if (fw_name) {
+ ECRNX_PRINT("fw file name: %s\n",fw_name);
+ }
+ else {
+ fw_name = "ECR6600U_transport.bin";
+ }
+ }
+
+ if (dl_fw && eswin_fw_file_chech(tr)) {
+ ECRNX_DBG("%s entry, start fw download!!", __func__);
+ if( eswin_fw_file_download(tr) < 0)
+ {
+ release_firmware(tr->fw);
+ return;
+ }
+ release_firmware(tr->fw);
+ dl_fw = false;
+ ECRNX_DBG("%s entry, finish and stop fw download!!", __func__);
+ schedule_delayed_work(&tr->register_work, msecs_to_jiffies(1000));
+ return;
+ }
+
+#ifdef CONFIG_ECRNX_WIFO_CAIL
+ ECRNX_DBG("%s entry, amt_mode = %d!!", __func__, amt_mode);
+#endif
+
+ tr->rx_callback = ecrnx_rx_callback;
+ tr->data_cfm_callback = ecrnx_data_cfm_callback;
+ tr->msg_cfm_callback = ecrnx_msg_cfm_callback;
+ tr->ops->start(tr);
+ ret = ecrnx_platform_init(tr, &tr->umac_priv);
+ set_bit(ESWIN_FLAG_CORE_REGISTERED, &tr->dev_flags);
+
+ ECRNX_DBG("%s exit!!", __func__);
+
+ return;
+}
+bool register_status = false;
+
+int eswin_core_register(struct eswin *tr)
+{
+ if(register_status == true)
+ {
+ return 0;
+ }
+ register_status = true;
+ ECRNX_DBG("%s entry!!", __func__);
+ schedule_delayed_work(&tr->register_work, msecs_to_jiffies(1));
+ return 0;
+}
+
+void eswin_core_unregister(struct eswin *tr)
+{
+ ECRNX_DBG("%s entry!!", __func__);
+ if(register_status == false)
+ {
+ return;
+ }
+ register_status = false;
+ msleep(20);
+ cancel_delayed_work_sync(&tr->register_work);
+
+ if (!test_bit(ESWIN_FLAG_CORE_REGISTERED, &tr->dev_flags))
+ return;
+ tr->rx_callback = NULL;
+ ecrnx_platform_deinit(tr->umac_priv);
+ eswin_core_destroy(tr);
+}
+
+int usb_host_send(void *buff, int len, int flag)
+{
+ int ret = -1;
+ struct eswin * tr= pEswin;
+ struct sk_buff *skb = NULL;
+
+ //ECRNX_DBG("%s-%d: flag:0x%08x, mask:0x%x, desc:0x%x, type:0x%x \n", __func__, __LINE__, flag, FLAG_MSG_TYPE_MASK, TX_FLAG_TX_DESC, (flag & FLAG_MSG_TYPE_MASK));
+ if((u8_l)(flag & FLAG_MSG_TYPE_MASK) == TX_FLAG_TX_DESC)
+ {
+ skb = (struct sk_buff*)buff;
+ }
+ else
+ {
+ skb = dev_alloc_skb(len + sizeof(int)); //add the flag length (len + 4)
+
+ memcpy(skb->data, (char*)&flag, sizeof(int));
+ memcpy((char*)skb->data + sizeof(int), buff, len); //put rx desc tag to skb
+ skb->len = len + sizeof(int);
+ }
+
+ ECRNX_DBG("usb_host_send, skb:0x%08x, skb_len:%d, frame_len:%d, flag:0x%08x \n", skb, skb->len, len, flag);
+
+ if (tr->ops->xmit) {
+ ret = tr->ops->xmit(tr, skb);
+ } else {
+ ECRNX_ERR("eswin_sdio_work error, ops->xmit is null\n");
+ }
+
+ return ret;
+}
+
+extern void ecrnx_send_handle_register(void * fn);
+struct eswin * eswin_core_create(size_t priv_size, struct device *dev, struct usb_ops * ops)
+{
+ struct eswin * tr;
+
+ tr = (struct eswin *)kzalloc(sizeof(struct eswin) + priv_size, GFP_KERNEL);
+ if(!tr) {
+ return NULL;
+ }
+
+ pEswin = tr;
+
+ tr->dev = dev;
+ tr->ops = ops;
+
+ ecrnx_send_handle_register(usb_host_send);
+
+ if(usb_status == false)
+ {
+ INIT_DELAYED_WORK(&tr->register_work, eswin_core_register_work);
+ usb_status = true;
+ }
+
+ tr->state = ESWIN_STATE_INIT;
+
+ ECRNX_DBG(" %s exit!!", __func__);
+ return tr;
+
+}
+
+void eswin_core_destroy(struct eswin *tr)
+{
+ tr->state = ESWIN_STATE_CLOSEED;
+
+ ECRNX_DBG("%s entry!!", __func__);
+ usb_status = false;
+
+ //flush_workqueue(tr->workqueue);
+ //destroy_workqueue(tr->workqueue);
+ //TODO:
+ //eswin_mac_destroy(tr);
+}
+
+
+//MODULE_AUTHOR("Transa-Semi");
+//MODULE_LICENSE("Dual BSD/GPL");
+//MODULE_DESCRIPTION("Core module for Transa-Semi 802.11 WLAN SDIO driver");
diff --git a/drivers/net/wireless/eswin/usb/core.h b/drivers/net/wireless/eswin/usb/core.h
new file mode 100644
index 000000000000..e99074fdf0f4
--- /dev/null
+++ b/drivers/net/wireless/eswin/usb/core.h
@@ -0,0 +1,297 @@
+#ifndef _CORE_H_
+#define _CORE_H_
+
+
+#include <linux/completion.h>
+#include <linux/if_ether.h>
+
+#include <net/mac80211.h>
+#include "ecrnx_compat.h"
+
+
+#define ESWIN_FLAG_CORE_REGISTERED 1
+
+#define ESWIN_NR_VIF 2
+#define ESWIN_NR_VIF_HW_QUEUE 4
+
+
+#define ESWIN_STATE_BOOT 1
+#define ESWIN_STATE_INIT 2
+#define ESWIN_STATE_STOP 3
+#define ESWIN_STATE_CLOSING 4
+#define ESWIN_STATE_CLOSEED 5
+#define ESWIN_STATE_START 6
+#define ESWIN_STATE_RUNNING 7
+
+
+#define WIM_RESP_TIMEOUT (msecs_to_jiffies(100))
+
+enum ESWIN_SCAN_MODE {
+ ESWIN_SCAN_MODE_IDLE = 0,
+ ESWIN_SCAN_MODE_SCANNING,
+ ESWIN_SCAN_MODE_ABORTING,
+};
+
+
+
+struct fwinfo_t {
+ uint32_t ready;
+ uint32_t version;
+ uint32_t tx_head_size;
+ uint32_t rx_head_size;
+ uint32_t payload_align;
+ uint32_t buffer_size;
+};
+
+struct eswin_capabilities {
+ uint64_t cap_mask;
+ uint16_t listen_interval;
+ uint16_t bss_max_idle;
+ uint8_t bss_max_idle_options;
+};
+
+struct eswin_max_idle {
+ bool enable;
+ u16 period;
+ u16 scale_factor;
+ u8 options;
+ struct timer_list keep_alive_timer;
+
+ unsigned long idle_period; /* jiffies */
+ struct timer_list timer;
+};
+
+
+/* Private txq driver data structure */
+struct eswin_txq {
+ u16 hw_queue; /* 0: AC_BK, 1: AC_BE, 2: AC_VI, 3: AC_VO */
+ struct list_head list;
+ struct sk_buff_head queue; /* own queue */
+ unsigned long nr_fw_queueud;
+ unsigned long nr_push_allowed;
+ struct ieee80211_vif vif;
+ struct ieee80211_sta sta;
+};
+
+struct tx_buff_node {
+ struct tx_buff_node * next;
+ void * buff;
+ int len;
+ int flag;
+};
+struct tx_buff_queue {
+ struct tx_buff_node * head;
+ struct tx_buff_node * tail;
+ int count;
+ spinlock_t lock;
+};
+
+#define ESWIN_QUEUE_MAX (ESWIN_NR_VIF_HW_QUEUE*ESWIN_NR_VIF + 3)
+
+typedef int (*usb_rx_cb_t)(void *priv, struct sk_buff *skb, unsigned char ep);
+typedef int (*usb_data_cfm_cb_t)(void *priv, void *host_id);
+struct eswin {
+
+ void *umac_priv; //mac drv data.
+ struct ieee80211_hw *hw;
+ struct ieee80211_vif *vif[ESWIN_NR_VIF];
+ struct device *dev;
+ int nr_active_vif;
+ int state;
+ bool promisc;
+
+ bool loopback;
+ bool ampdu_supported;
+ int lb_count;
+ bool amsdu_supported;
+ bool block_frame;
+ bool ampdu_reject;
+
+ char alpha2[2];
+ u64 tsf_offset;
+
+ struct usb_ops *ops;
+ usb_rx_cb_t rx_callback;
+ usb_data_cfm_cb_t data_cfm_callback;
+ usb_data_cfm_cb_t msg_cfm_callback;
+
+ //struct sx_buff_queue queue[ESWIN_NR_VIF]; /* 0: frame, 1: wim */
+ struct tx_buff_queue tx_queue;
+ struct tx_buff_node tx_node[64];
+ struct tx_buff_node * tx_node_head;
+ spinlock_t tx_lock;
+ int tx_node_num;
+ struct work_struct work;
+
+ struct eswin_txq ntxq[ESWIN_QUEUE_MAX];
+
+
+ struct mutex state_mtx;
+ enum ESWIN_SCAN_MODE scan_mode;
+ struct workqueue_struct *workqueue;
+ struct delayed_work scan_timeout;
+
+ //struct work_struct register_work;
+ struct delayed_work register_work;
+
+
+ unsigned long dev_flags;
+ struct mac_address mac_addr[ESWIN_NR_VIF];
+ struct ieee80211_supported_band bands[NUM_NL80211_BANDS];
+
+
+
+ /* Move to vif or sta driver data */
+ u8 frame_seqno;
+ u8 wim_seqno;
+ u8 band;
+ u16 center_freq;
+ u16 aid;
+ u32 cipher_pairwise;
+ u32 cipher_group;
+
+ struct fwinfo_t fwinfo;
+ struct eswin_capabilities cap;
+
+ /* power management */
+ enum ps_mode {
+ PS_DISABLED,
+ PS_ENABLED,
+ PS_AUTO_POLL,
+ PS_MANUAL_POLL
+ } ps;
+ bool ps_poll_pending;
+ bool ps_enabled;
+
+
+
+ /* tx */
+ spinlock_t txq_lock;
+ struct list_head txq;
+ /* 0: AC_BK, 1: AC_BE, 2: AC_VI, 3: AC_VO */
+ atomic_t tx_credit[IEEE80211_NUM_ACS*3];
+ atomic_t tx_pend[IEEE80211_NUM_ACS*3];
+
+// struct completion wim_responded;
+// struct sk_buff *last_wim_responded;
+
+
+ struct delayed_work roc_finish;
+
+ struct firmware *fw;
+
+ struct dentry *debugfs;
+
+ /* must be last */
+ u8 drv_priv[0] __aligned(sizeof(void *));
+};
+
+
+/* vif driver data structure */
+struct eswin_vif {
+ struct eswin *tr;
+ int index;
+ struct net_device *dev;
+
+ /* scan */
+ struct delayed_work scan_timeout;
+
+ /* power save */
+ bool ps_polling;
+
+ /* MLME */
+ spinlock_t preassoc_sta_lock;
+ struct list_head preassoc_sta_list;
+
+ /* inactivity */
+ u16 max_idle_period;
+};
+
+#define to_ieee80211_vif(v) \
+ container_of((void *)v, struct ieee80211_vif, drv_priv)
+
+#define to_i_vif(v) ((struct eswin_vif *) (v)->drv_priv)
+
+static inline int hw_vifindex(struct ieee80211_vif *vif)
+{
+ struct eswin_vif *i_vif;
+
+ if (vif == NULL)
+ return 0;
+
+ i_vif = to_i_vif(vif);
+ return i_vif->index;
+}
+
+/* sta driver data structure */
+struct eswin_sta {
+ struct eswin *tr;
+ struct ieee80211_vif *vif;
+ /*struct ieee80211_sta *sta;*/
+
+ enum ieee80211_sta_state state;
+ struct list_head list;
+
+ /* keys */
+ struct ieee80211_key_conf *ptk;
+ struct ieee80211_key_conf *gtk;
+
+ /* BSS max idle period */
+ struct eswin_capabilities cap;
+ struct eswin_max_idle max_idle;
+};
+
+#define to_ieee80211_sta(s) \
+ container_of((void *)s, struct ieee80211_sta, drv_priv)
+
+#define to_i_sta(s) ((struct eswin_sta *) (s)->drv_priv)
+
+
+
+struct eswin_sta_handler {
+ int (*sta_state)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ enum ieee80211_sta_state old_state,
+ enum ieee80211_sta_state new_state);
+
+};
+
+#if 0
+#define STAH(fn) \
+ static struct eswin_sta_handler __stah_ ## fn \
+ __attribute((__used__)) \
+ __attribute((__section__("nrc.sta"))) = { \
+ .sta_state = fn, \
+ }
+
+extern struct eswin_sta_handler __sta_h_start, __sta_h_end;
+#endif
+
+
+/* trx */
+
+struct eswin_trx_data {
+ struct eswin *tr;
+ struct ieee80211_vif *vif;
+ struct ieee80211_sta *sta;
+ struct sk_buff *skb;
+ int result;
+};
+
+
+#define NL80211_IFTYPE_ALL (BIT(NUM_NL80211_IFTYPES)-1)
+int eswin_core_register(struct eswin *tr);
+void eswin_core_unregister(struct eswin *tr);
+struct eswin * eswin_core_create(size_t priv_size, struct device *dev, struct usb_ops * ops);
+void eswin_core_destroy(struct eswin *tr);
+
+extern int power_save;
+extern int disable_cqm;
+#ifdef CONFIG_ECRNX_WIFO_CAIL
+extern bool amt_mode;
+#endif
+extern bool set_gain;
+extern bool dl_fw;
+extern bool register_status;
+
+#endif
diff --git a/drivers/net/wireless/eswin/usb/debug.c b/drivers/net/wireless/eswin/usb/debug.c
new file mode 100644
index 000000000000..f5a3469d1d6a
--- /dev/null
+++ b/drivers/net/wireless/eswin/usb/debug.c
@@ -0,0 +1,187 @@
+#include <linux/timer.h>
+#include "ecrnx_utils.h"
+#include "ecrnx_defs.h"
+#include "ecrnx_msg_rx.h"
+#include "ecrnx_usb.h"
+#include "core.h"
+#include "debug.h"
+#include "usb.h"
+#include "ecrnx_rx.h"
+
+
+//kernal timer param
+static struct timer_list tm;
+static int counter = 0;
+
+//rx param
+struct sk_buff *skb = NULL;
+struct ecrnx_hw *g_ecrnx_hw = NULL;
+
+//tx param
+struct ecrnx_vif vif;
+struct ecrnx_sta sta;
+struct cfg80211_mgmt_tx_params params;
+
+static void test_timer_handler(struct timer_list * lt);
+
+void ecrnx_hw_set(void* init_ecrnx_hw)
+{
+ g_ecrnx_hw = (struct ecrnx_hw *)init_ecrnx_hw;
+}
+
+static int sdio_rx_param_init(void)
+{
+ struct rxu_stat_mm rxu_state;
+ struct rx_hd rx_head;
+ struct ethhdr eth_hd;
+ int res, index = 0;
+ uint8_t *ptr = NULL;
+ uint16_t head_len = sizeof(struct ethhdr);
+ ECRNX_DBG("%s entry!!", __func__);
+ memset(&rxu_state, 0, sizeof(struct rxu_stat_mm));
+ memset(&rx_head, 0, sizeof(struct rx_hd));
+ memset(&eth_hd, 0, sizeof(struct ethhdr));
+
+ rxu_state.comm_hd.frm_type = USB_FRM_TYPE_RXDESC;
+ //rxu_state.comm_hd.frm_type = USB_FRM_TYPE_MSG;
+ if(rxu_state.comm_hd.frm_type == USB_FRM_TYPE_RXDESC)
+ {
+ head_len += sizeof(struct rxu_stat_mm) + sizeof(struct rx_hd);
+ }
+ else
+ {
+ head_len += sizeof(dispatch_hdr_t);
+ }
+
+ skb = dev_alloc_skb(FRAME_SIZE + head_len);
+ skb_reserve(skb, head_len);
+ ptr = skb_put(skb, FRAME_SIZE); //ptr is skb tail
+ memset(skb->data, 0x0f, FRAME_SIZE); //payload
+ skb_push(skb, sizeof(struct ethhdr));
+
+ for( index = 0; index < ETH_ALEN; index++)
+ {
+ eth_hd.h_dest[index] = index;
+ eth_hd.h_source[index] = index;
+ }
+
+ eth_hd.h_proto = ETH_P_80221; //ETHERTYPE_IP;
+ memcpy(skb->data, &eth_hd, sizeof(struct ethhdr));
+
+ if(rxu_state.comm_hd.frm_type == USB_FRM_TYPE_RXDESC)
+ {
+ //data frame, need header, rxu state
+ //rx head
+ skb_push(skb, sizeof(struct rx_hd));
+ rx_head.frmlen = FRAME_SIZE + head_len;
+ rx_head.ampdu_stat_info = 0;
+ //...
+ memcpy(skb->data , &rx_head, sizeof(struct rx_hd));
+
+ //rxu state
+ skb_push(skb, sizeof(struct rxu_stat_mm));
+ rxu_state.msdu_mode = 0x01;
+ rxu_state.host_id = 0x0001;
+ rxu_state.frame_len = rx_head.frmlen;
+ rxu_state.status = RX_STAT_MONITOR;
+ //rxu_state.phy_info.info1 = 1 | (1 << 8) | (2450 << 16);
+ //rxu_state.phy_info.info2 = 2450 | (2450 << 16);
+#ifdef CONFIG_ECRNX_FULLMAC
+ //rxu_state.flags = 0;
+#endif
+ //rxu_state.pattern = ecrnx_rxbuff_pattern;
+ memcpy(skb->data, &rxu_state, sizeof(struct rxu_stat_mm));
+ }
+ else
+ {
+ //message frame, don't need header
+ skb_push(skb, sizeof(dispatch_hdr_t)); //rxu state
+ memcpy(skb->data, &rxu_state.comm_hd, sizeof(dispatch_hdr_t));
+ }
+
+ for(index = 0; index < head_len; index++)
+ {
+ ECRNX_DBG("0x%x ", skb->data[index]);
+ }
+
+ ECRNX_DBG("%s exit, skb_len:%d, type: %d!!", __func__, skb->len, (skb->data[1] << 8) | skb->data[0]);
+
+ return res;
+}
+
+extern int ecrnx_start_mgmt_xmit(struct ecrnx_vif *vif, \
+ struct ecrnx_sta *sta, \
+ struct cfg80211_mgmt_tx_params *params, \
+ bool offchan, \
+ u64 *cookie);
+
+/*
+struct cfg80211_mgmt_tx_params {
+ struct ieee80211_channel *chan;
+ bool offchan;
+ unsigned int wait;
+ const u8 *buf;
+ size_t len;
+ bool no_cck;
+ bool dont_wait_for_ack;
+ int n_csa_offsets;
+ const u16 *csa_offsets;
+};
+*/
+static void sdio_tx_param_init(void)
+{
+ u8 send_buf[FRAME_SIZE] = {0x00, 0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09};
+
+ params.len = FRAME_SIZE;
+ params.buf = (const u8 *)send_buf;
+ params.n_csa_offsets = 10;
+ params.csa_offsets = (const u16 *)send_buf;
+ params.no_cck = 0;
+
+ vif.ecrnx_hw = g_ecrnx_hw;
+}
+
+void sdio_rx_tx_test_schedule(void)
+{
+ ECRNX_DBG("%s entry!!", __func__);
+ tm.function = test_timer_handler;
+ tm.expires = jiffies + HZ * 10;
+ add_timer(&tm);
+
+ sdio_rx_param_init();
+ sdio_tx_param_init();
+ ECRNX_DBG("%s exit!!", __func__);
+}
+
+static void test_timer_handler(struct timer_list * lt)
+{
+ ECRNX_DBG("%s, counter:%d\n", __FUNCTION__, counter);
+
+ if(counter%2)
+ {
+ u64 cookie;
+ //ecrnx_start_mgmt_xmit(&vif, NULL, &params, false, &cookie);
+ }
+ else
+ {
+ ecrnx_rx_callback(g_ecrnx_hw, skb, 1);
+ }
+
+ if(lt)
+ {
+ counter++;
+ }
+
+ if(counter < 5)
+ {
+ //tm.expires = jiffies +1 * HZ / 1000/10; //100us
+ tm.expires = jiffies +1 * HZ;
+ add_timer(&tm);
+ }
+ else
+ {
+ counter = 0;
+ del_timer(&tm);
+ }
+}
+
diff --git a/drivers/net/wireless/eswin/usb/debug.h b/drivers/net/wireless/eswin/usb/debug.h
new file mode 100644
index 000000000000..1f5025842d58
--- /dev/null
+++ b/drivers/net/wireless/eswin/usb/debug.h
@@ -0,0 +1,21 @@
+
+
+#ifndef _ECRNX_DBG
+#define _ECRNX_DBG
+#include <net/mac80211.h>
+
+//#define CONFIG_HIF_PRINT_TX_DATA
+#define CONFIG_USE_TXQ
+
+//#define CONFIG_NRC_HIF_PRINT_FLOW_CONTROL
+//#define CONFIG_SHOW_TX_SPEED
+//#define CONFIG_SHOW_RX_SPEED
+
+#define FRAME_SIZE 512
+
+void eswin_dump_wim(struct sk_buff *skb);
+void eswin_init_debugfs(struct eswin *tr);
+void sdio_rx_tx_test_schedule(void);
+void ecrnx_hw_set(void* init_ecrnx_hw);
+
+#endif
diff --git a/drivers/net/wireless/eswin/usb/ecrnx_usb.c b/drivers/net/wireless/eswin/usb/ecrnx_usb.c
new file mode 100644
index 000000000000..4bfe655e5505
--- /dev/null
+++ b/drivers/net/wireless/eswin/usb/ecrnx_usb.c
@@ -0,0 +1,447 @@
+/**
+ ******************************************************************************
+ *
+ * @file ecrnx_usb.c
+ *
+ * @brief ECRNX usb init and management function
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+
+#include <linux/module.h>
+#include <linux/kthread.h>
+
+#include "core.h"
+//#include "debug.h"
+#include "ecrnx_utils.h"
+#include "ecrnx_cmds.h"
+#include "ecrnx_defs.h"
+#include "ecrnx_msg_rx.h"
+#include "ipc_host.h"
+#include "ipc_shared.h"
+#include "ecrnx_events.h"
+#include "ecrnx_usb.h"
+#ifdef CONFIG_TEST_ESWIN_USB
+#include "debug.h"
+#endif
+#ifdef CONFIG_ECRNX_WIFO_CAIL
+#include "ecrnx_amt.h"
+#endif
+
+
+#define USB_ADDR_DATA (unsigned int)(0x200)
+
+#ifdef CONFIG_ECRNX_SOFTMAC
+#define FW_STR "lmac"
+#elif defined CONFIG_ECRNX_FULLMAC
+#define FW_STR "fmac"
+#endif
+
+#if defined(CONFIG_ECRNX_DEBUGFS_CUSTOM)
+#include "ecrnx_debugfs_func.h"
+#endif
+
+extern const int nx_txdesc_cnt_msk[];
+
+struct vendor_radiotap_hdr {
+ u8 oui[3];
+ u8 subns;
+ u16 len;
+ u8 data[];
+};
+
+#ifdef CONFIG_WEXT_PRIV
+extern void priv_copy_data_wakeup(struct ecrnx_hw *ecrnx_hw, struct sk_buff *skb);
+#endif
+
+/**
+ * @brief: ipc_host_rxdesc_handler: Handle the reception of a Rx Descriptor
+ * Called from general IRQ handler when status %IPC_IRQ_E2A_RXDESC is set
+ * @param {env} pointer to the usb Host environment
+ * @param {skb} received skb data
+ * @return: none
+ */
+static int usb_host_rxdesc_handler(struct ipc_host_env_tag *env, struct sk_buff *skb)
+{
+ u8 ret = 0;
+ // LMAC has triggered an IT saying that a reception has occurred.
+ // Then we first need to check the validity of the current hostbuf, and the validity
+ // of the next hostbufs too, because it is likely that several hostbufs have been
+ // filled within the time needed for this irq handling
+#ifdef CONFIG_ECRNX_FULLMAC
+ // call the external function to indicate that a RX descriptor is received
+ ret = env->cb.recv_data_ind(env->pthis, skb);
+#else
+ // call the external function to indicate that a RX packet is received
+ ret = env->cb.recv_data_ind(env->pthis, skb);
+#endif //(CONFIG_ECRNX_FULLMAC)
+ //ECRNX_DBG("%s exit!!", __func__);
+ return ret;
+}
+
+/**
+ * @brief: usb_host_radar_handler Handle the reception of radar events
+ * @param {env} pointer to the usb Host environment
+ * @param {skb} received skb data
+ * @return: none
+ */
+static int usb_host_radar_handler(struct ipc_host_env_tag *env, struct sk_buff *skb)
+{
+ int ret = 0;
+
+#ifdef CONFIG_ECRNX_RADAR
+ // LMAC has triggered an IT saying that a radar event has been sent to upper layer.
+ // Then we first need to check the validity of the current msg buf, and the validity
+ // of the next buffers too, because it is likely that several buffers have been
+ // filled within the time needed for this irq handling
+ // call the external function to indicate that a RX packet is received
+ spin_lock(&((struct ecrnx_hw *)env->pthis)->radar.lock);
+ ret = env->cb.recv_radar_ind(env->pthis, skb);
+ spin_unlock(&((struct ecrnx_hw *)env->pthis)->radar.lock);
+#endif /* CONFIG_ECRNX_RADAR */
+ return ret;
+}
+
+/**
+ * @brief: usb_host_unsup_rx_vec_handler Handle the reception of unsupported rx vector
+ * @param {env} pointer to the usb Host environment
+ * @param {skb} received skb data
+ * @return: none
+ */
+static int usb_host_unsup_rx_vec_handler(struct ipc_host_env_tag *env, struct sk_buff *skb)
+{
+ return env->cb.recv_unsup_rx_vec_ind(env->pthis, skb);
+}
+
+/**
+ * @brief: usb_host_msg_handler Handler for firmware message
+ * @param {env} pointer to the usb Host environment
+ * @param {skb} received skb data
+ * @return: none
+ */
+static int usb_host_msg_handler(struct ipc_host_env_tag *env, struct sk_buff *skb)
+{
+ // LMAC has triggered an IT saying that a message has been sent to upper layer.
+ // Then we first need to check the validity of the current msg buf, and the validity
+ // of the next buffers too, because it is likely that several buffers have been
+ // filled within the time needed for this irq handling
+ // call the external function to indicate that a RX packet is received
+ return env->cb.recv_msg_ind(env->pthis, skb->data);
+}
+
+/**
+ * @brief: usb_host_msgack_handler Handle the reception of message acknowledgement
+ * @param {env} pointer to the usb Host environment
+ * @param {skb} received skb data
+ * @return: none
+ */
+static int usb_host_msgack_handler(struct ipc_host_env_tag *env, struct sk_buff *skb)
+{
+ ptr_addr hostid = *(ptr_addr *)skb->data;
+
+ ASSERT_ERR(hostid);
+
+ env->msga2e_hostid = NULL;
+ env->cb.recv_msgack_ind(env->pthis, (void*)hostid);
+
+ return 0;
+}
+
+/**
+ * @brief: usb_host_dbg_handler Handle the reception of Debug event
+ * @param {env} pointer to the usb Host environment
+ * @param {skb} received skb data
+ * @return: none
+ */
+static int usb_host_dbg_handler(struct ipc_host_env_tag *env, struct sk_buff *skb)
+{
+ // LMAC has triggered an IT saying that a DBG message has been sent to upper layer.
+ // Then we first need to check the validity of the current buffer, and the validity
+ // of the next buffers too, because it is likely that several buffers have been
+ // filled within the time needed for this irq handling
+ // call the external function to indicate that a RX packet is received
+ return env->cb.recv_dbg_ind(env->pthis, skb);
+}
+
+/**
+ * @brief: usb_host_tx_cfm_handler Handle the reception of TX confirmation
+ * @param {env} pointer to the usb Host environment
+ * @param {queue_idx} index of the hardware on which the confirmation has been received
+ * @param {user_pos} index of the user position
+ * @return: none
+ */
+static int usb_host_tx_cfm_handler(struct ipc_host_env_tag *env, struct sk_buff *skb)
+{
+ ptr_addr host_id;
+ struct sk_buff *skb_cfm;
+ struct ecrnx_txhdr *txhdr;
+
+#ifdef CONFIG_ECRNX_FULLMAC
+ struct tx_cfm_tag *cfm;
+
+ cfm = (struct tx_cfm_tag *)skb->data;
+ //host_id = cfm->hostid;
+ memcpy((uint8_t *)&host_id, (uint8_t *)cfm->hostid, sizeof(ptr_addr));
+ if (host_id == 0) {
+ return 0;
+ }
+
+ ECRNX_DBG("%s:hostid(tx_skb):0x%08x, rx_skb: 0x%x \n", __func__, host_id, skb);
+ skb_cfm = (struct sk_buff *)host_id;
+ txhdr = (struct ecrnx_txhdr *)(*((ptr_addr*)skb_cfm->data - 1));
+ memcpy(&txhdr->hw_hdr.cfm, cfm, sizeof(*cfm));
+#elif defined CONFIG_ECRNX_SOFTMAC
+ //TODO:
+#endif
+
+ return env->cb.send_data_cfm(env->pthis, (void*)txhdr);
+}
+
+#ifdef CONFIG_ECRNX_WIFO_CAIL
+/**
+ * @brief: usb_host_amt_rx_handler Handle the reception of rx frame
+ * @param {frm_type} received frame type
+ * @param {skb} received skb data
+ * @return: none
+ */
+void usb_host_amt_rx_handler(uint32_t frm_type, struct sk_buff *skb)
+{
+ int need_free = 0;
+
+ ECRNX_DBG("%s enter, frame type: %d!!", __func__, frm_type);
+ if(!skb)
+ {
+ ECRNX_ERR("usb_host_amt_rx_handler input param error!! \n!");
+ return;
+ }
+
+ if (frm_type != USB_FRM_TYPE_RXDESC)
+ {
+ skb_pull(skb, SKB_DATA_COM_HD_OFFSET); //delete the frame common header
+ }
+
+ ECRNX_DBG("skb:0x%08x, skb_len:%d, frame type: %d!!", skb->data,skb->len, frm_type);
+
+ switch (frm_type)
+ {
+ case USB_FRM_TYPE_IWPRIV:
+ {
+ /*printk("vif_start:%d, vif_monitor:%d \n", ecrnx_hw->vif_started, ecrnx_hw->monitor_vif);
+ print_hex_dump(KERN_INFO, "iwpriv-cfm:", DUMP_PREFIX_ADDRESS, 32, 1,
+ skb->data, skb->len, false);*/
+ amt_vif.rxlen = skb->len;
+ memset(amt_vif.rxdata, 0, ECRNX_RXSIZE);
+ memcpy(amt_vif.rxdata, skb->data, skb->len);
+ amt_vif.rxdatas = 1;
+ wake_up(&amt_vif.rxdataq);
+ need_free = 1;
+ break;
+ }
+ default:
+ need_free = 1;
+ break;
+ }
+
+ if (need_free && skb) { // free the skb
+ ECRNX_DBG("skb free: 0x%x !! \n", skb);
+ dev_kfree_skb(skb);
+ }
+ ECRNX_DBG("%s exit!!", __func__);
+ return;
+}
+#endif
+
+
+/**
+ * @brief: usb_host_rx_handler Handle the reception of rx frame
+ * @param {frm_type} received frame type
+ * @param {env} pointer to the usb Host environment
+ * @param {skb} received skb data
+ * @return: none
+ */
+void usb_host_rx_handler(uint32_t frm_type, struct ipc_host_env_tag *env, struct sk_buff *skb)
+{
+ int ret = 1;
+
+ //ECRNX_DBG("%s enter, frame type: %d!!", __func__, frm_type);
+ if(!env || !skb)
+ {
+ ECRNX_ERR("usb_host_rx_handler input param error!! \n!");
+ return;
+ }
+
+ ((struct ecrnx_hw *)env->pthis)->usb_rx++;
+
+ if (frm_type != USB_FRM_TYPE_RXDESC)
+ {
+ skb_pull(skb, SKB_DATA_COM_HD_OFFSET); //delete the frame common header
+ }
+
+ //ECRNX_DBG("skb:0x%08x, skb_len:%d, frame type: %d!!", skb->data,skb->len, frm_type);
+
+ switch (frm_type)
+ {
+ case USB_FRM_TYPE_RXDESC:
+ {
+ // handle the RX descriptor reception
+ usb_host_rxdesc_handler(env, skb); //just for current only one endpoint test
+ break;
+ }
+
+ case USB_FRM_TYPE_MSG_ACK:
+ {
+ if(1 == skb->len)
+ {
+ ECRNX_PRINT("MSG_ACK len: 1");
+ break;
+ }
+ ret = usb_host_msgack_handler(env, skb);
+ break;
+ }
+
+ case USB_FRM_TYPE_MSG:
+ {
+ ret = usb_host_msg_handler(env, skb);
+ break;
+ }
+
+ case USB_FRM_TYPE_TXCFM:
+ {
+ if(!env->pthis)
+ {
+ ECRNX_ERR("env->pthis ptr error!! \n!");
+ break;
+ }
+
+ /* add the spinlock which was missed during porting.
+ when skb->len more than 24 and skb contans more than one data cfm.
+ data cfm structure length is 24 byte.
+ */
+ spin_lock_bh(&((struct ecrnx_hw *)env->pthis)->tx_lock);
+ while(skb->len > sizeof(struct tx_cfm_tag))
+ {
+ ret = usb_host_tx_cfm_handler(env, skb);
+ skb_pull(skb, sizeof(struct tx_cfm_tag));
+ }
+ ret = usb_host_tx_cfm_handler(env, skb);
+ spin_unlock_bh(&((struct ecrnx_hw *)env->pthis)->tx_lock);
+ break;
+ }
+
+ case USB_FRM_TYPE_UNSUP_RX_VEC:
+ {
+ // handle the unsupported rx vector reception
+ ret = usb_host_unsup_rx_vec_handler(env, skb);
+ break;
+ }
+
+ case USB_FRM_TYPE_RADAR:
+ {
+ // handle the radar event reception
+ ret = usb_host_radar_handler(env, skb);
+ break;
+ }
+
+ case USB_FRM_TYPE_TBTT_SEC:
+ {
+ env->cb.sec_tbtt_ind(env->pthis);
+ break;
+ }
+
+ case USB_FRM_TYPE_TBTT_PRIM:
+ {
+ env->cb.prim_tbtt_ind(env->pthis);
+ break;
+ }
+
+ case USB_FRM_TYPE_DBG:
+ {
+ ECRNX_DBG("--%s:USB_FRM_TYPE_DBG, len:%d, slave:%s \n", __func__, skb->len, skb->data);
+ ret = usb_host_dbg_handler(env, skb);
+ break;
+ }
+ case USB_FRM_TYPE_IWPRIV:
+ {
+#ifdef CONFIG_WEXT_PRIV
+#if 0
+ struct ecrnx_hw *ecrnx_hw = (struct ecrnx_hw *)env->pthis;
+ struct ecrnx_vif* ecrnx_vif = ecrnx_hw->vif_table[0];
+
+ printk("vif_start:%d, vif_monitor:%d \n", ecrnx_hw->vif_started, ecrnx_hw->monitor_vif);
+ print_hex_dump(KERN_INFO, "iwpriv-cfm:", DUMP_PREFIX_ADDRESS, 32, 1,
+ skb->data, skb->len, false);
+ ecrnx_vif->rxlen = skb->len;
+ memcpy(ecrnx_vif->rxdata, skb->data, skb->len);
+ ecrnx_vif->rxdatas = 1;
+ wake_up(&ecrnx_vif->rxdataq);
+#else
+ priv_copy_data_wakeup((struct ecrnx_hw *)env->pthis, skb);
+#endif
+
+#endif
+ ret = 0;
+ break;
+ }
+
+#if defined(CONFIG_ECRNX_DEBUGFS_CUSTOM)
+ case USB_FRM_DEBUG_FS:
+ {
+ uint32_t debugfs_type = ((uint32_t*)skb->data)[0];
+ debugfs_resp.debugfs_type = debugfs_type;
+
+
+ if((debugfs_type != SLAVE_LOG_LEVEL) && \
+ (debugfs_type < SLAVE_DEBUGFS_MAX)){
+
+ debugfs_resp.rxlen = skb->len-4;
+ memcpy(debugfs_resp.rxdata, skb->data+4, debugfs_resp.rxlen);
+
+ ECRNX_DBG("%s - wake_up()\n", __func__);
+ debugfs_resp.rxdatas = 1;
+ wake_up(&debugfs_resp.rxdataq);
+ }
+
+ break;
+ }
+#endif
+ default:
+ ret = 0;
+ break;
+ }
+
+ if (!ret && skb) { // free the skb
+ ECRNX_DBG("skb free: 0x%x, ret: %d!! \n", skb, ret);
+ dev_kfree_skb(skb);
+ }
+ //ECRNX_DBG("%s exit!!", __func__);
+ return;
+}
+
+/**
+ * @brief: ecrnx_usb_init Initialize usb interface.
+ * @param {ecrnx_hw} Main driver data
+ * @return: u8
+ */
+u8 ecrnx_usb_init(struct ecrnx_hw *ecrnx_hw)
+{
+ ECRNX_DBG("%s entry!!", __func__);
+ // Save the pointer to the register base
+ ecrnx_hw->ipc_env->pthis = (void*)ecrnx_hw;
+
+ ECRNX_DBG("%s exit!!", __func__);
+ return 0;
+}
+
+/**
+ * @brief: ecrnx_usb_deinit DeInitialize usb interface.
+ * @param {ecrnx_hw} Main driver data
+ * @return: none
+ */
+void ecrnx_usb_deinit(struct ecrnx_hw *ecrnx_hw)
+{
+ ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+ memset(ecrnx_hw, 0, sizeof(struct ecrnx_hw));
+}
diff --git a/drivers/net/wireless/eswin/usb/ecrnx_usb.h b/drivers/net/wireless/eswin/usb/ecrnx_usb.h
new file mode 100644
index 000000000000..98178a788360
--- /dev/null
+++ b/drivers/net/wireless/eswin/usb/ecrnx_usb.h
@@ -0,0 +1,110 @@
+/**
+******************************************************************************
+*
+* @file ecrnx_usb.h
+*
+* @brief ecrnx usb header file
+*
+* Copyright (C) ESWIN 2015-2020
+*
+******************************************************************************
+*/
+
+#ifndef _ECRNX_USB_H_
+#define _ECRNX_USB_H_
+
+#include "ecrnx_rx.h"
+#include "eswin_utils.h"
+
+enum{
+ USB_FRM_TYPE_RXDESC =1,
+ USB_FRM_TYPE_MSG,
+ USB_FRM_TYPE_DBG,
+ USB_FRM_TYPE_UNSUP_RX_VEC,
+ USB_FRM_TYPE_RADAR,
+ USB_FRM_TYPE_TBTT_SEC,
+ USB_FRM_TYPE_TBTT_PRIM,
+ USB_FRM_TYPE_MSG_ACK,
+ USB_FRM_TYPE_TXCFM,
+ USB_FRM_TYPE_IWPRIV,
+ USB_FRM_DEBUG_FS,
+};
+
+
+#define SKB_DATA_COM_HD_OFFSET sizeof(dispatch_hdr_t)
+
+/// Element in the pool of RX header descriptor.
+struct rx_hd
+{
+ /// Total length of the received MPDU
+ uint16_t frmlen;
+ /// AMPDU status information
+ uint16_t ampdu_stat_info;
+ /// TSF Low
+ uint32_t tsflo;
+ /// TSF High
+ uint32_t tsfhi;
+ /// Rx Vector 1
+ struct rx_vector_1 rx_vec_1;
+ /// Rx Vector 2
+ struct rx_vector_2 rx_vec_2;
+ /// MPDU status information
+ uint32_t statinfo;
+};
+
+/*adjust phy info,pattern, these infomation should be put to another buffer to trnasfer*/
+struct rxu_stat_mm
+{
+ /*type of msg/dbg/frm etc.*/
+ dispatch_hdr_t frm_type;
+ uint32_t fragment_flag : 1;
+ uint32_t is_qos : 1;
+ uint32_t need_reord : 1;
+ uint32_t need_pn_check : 1;
+ uint32_t is_ga : 1;
+ uint32_t flags_rsvd0 : 3;
+ uint32_t tid : 8;
+ uint16_t sn;
+ /// Length
+ uint16_t frame_len;
+ /// Status (@ref rx_status_bits)
+ uint16_t status;
+ uint16_t real_offset; /* dma address align 4, real data - real_offset */
+};
+
+
+
+/**
+ * @brief: ecrnx_usb_init Initialize usb interface.
+ * @param {ecrnx_hw} Main driver data
+ * @return: u8
+ */
+u8 ecrnx_usb_init(struct ecrnx_hw *ecrnx_hw);
+
+/**
+ * @brief: ecrnx_usb_deinit DeInitialize usb interface.
+ * @param {ecrnx_hw} Main driver data
+ * @return: none
+ */
+void ecrnx_usb_deinit(struct ecrnx_hw *ecrnx_hw);
+
+#ifdef CONFIG_ECRNX_WIFO_CAIL
+/**
+ * @brief: usb_host_rx_handler Handle the reception of rx frame
+ * @param {frm_type} received frame type
+ * @param {skb} received skb data
+ * @return: none
+ */
+void usb_host_amt_rx_handler(uint32_t frm_type, struct sk_buff *skb);
+#endif /* CONFIG_ECRNX_WIFO_CAIL */
+
+/**
+ * @brief: usb_host_rx_handler Handle the reception of rx frame
+ * @param {frm_type} received frame type
+ * @param {env} pointer to the usb Host environment
+ * @param {skb} received skb data
+ * @return: none
+ */
+void usb_host_rx_handler(uint32_t frm_type, struct ipc_host_env_tag *env, struct sk_buff *skb);
+
+#endif /* __ECRNX_USB_H */
diff --git a/drivers/net/wireless/eswin/usb/fw.c b/drivers/net/wireless/eswin/usb/fw.c
new file mode 100644
index 000000000000..66254d8f6a64
--- /dev/null
+++ b/drivers/net/wireless/eswin/usb/fw.c
@@ -0,0 +1,266 @@
+/**
+******************************************************************************
+*
+* @file fw.c
+*
+* @brief ecrnx usb firmware download functions
+*
+* Copyright (C) ESWIN 2015-2020
+*
+******************************************************************************
+*/
+
+#include <linux/firmware.h>
+#include "core.h"
+#include "usb.h"
+#include "fw_head_check.h"
+
+extern char *fw_name;
+
+#define MAX_PACKET_SIZE 512
+#define DATA_ADDR_O 0x20000
+
+#define DATA_ADDR_O_1 0x10000
+
+struct cfg_sync
+{
+ char magic;
+ char type;
+ short length;
+ char crc8;
+};
+
+struct cfg_msg
+{
+ char magic;
+ char type;
+ short length;
+ unsigned int data_addr;
+ unsigned int data_len;
+ char crc8;
+};
+
+typedef struct _ack_msg
+{
+ unsigned char magic;
+ unsigned char type;
+ unsigned short length;
+ char data[0];
+} ack_msg;
+
+typedef enum
+{
+ STATU_SUCCESS = 0,
+ STATU_ERR
+} ACK_STATUS;
+
+unsigned char eswin_crc8(unsigned char * buf, unsigned short length)
+{
+ unsigned char crc = 0, i = 0;
+ while(length--)
+ {
+ crc ^= *buf++;
+ for(i = 8; i > 0; i--)
+ {
+ if(crc & 0x80)
+ {
+ crc = (crc << 1) ^ 0x31;
+ }
+ else
+ {
+ crc <<= 1;
+ }
+ }
+ }
+ return crc;
+}
+
+char eswin_fw_ack_check(u8 * buff)
+{
+ ack_msg* msg = (ack_msg*)buff;
+ if(msg->magic != 0x5a)
+ {
+ ECRNX_PRINT("dl-fw ack fail, magic: %d\n", msg->magic);
+ return -1;
+ }
+ if(msg->data[0] != STATU_SUCCESS)
+ {
+ ECRNX_PRINT("dl-fw ack fail, status: %d\n", msg->data[0]);
+ return -1;
+ }
+ return 0;
+}
+
+char eswin_fw_file_download(struct eswin *tr)
+{
+ int ret,i;
+ unsigned int lengthLeft, lengthSend, offset = HEAD_SIZE, total_send_len = 0;
+ unsigned char length_str[9]={0};
+ const u8 * dataAddr;
+ u8 * buff;
+ struct sk_buff *skb;
+ //char str_msg[16];
+ char str_sync[4] = {0x63,0x6e,0x79,0x73};
+ unsigned int file_load_addr[3] = {0x20000U, 0x40000U, 0x50000U}; // ilm addr(start addr); dlm addr; iram0 addr
+
+ struct cfg_msg * p_msg;
+ struct cfg_sync * p_sync;
+ u8 prev_dl_pst = 0;
+
+ skb = dev_alloc_skb(MAX_PACKET_SIZE + 16);
+ buff = (u8 *)skb->data;
+
+ p_msg = (struct cfg_msg *)buff;
+ p_sync = (struct cfg_sync *)buff;
+
+ lengthLeft = tr->fw->size;
+ dataAddr = tr->fw->data;
+
+ /* 0 download sync*/
+ memcpy(buff,str_sync,4);
+ tr->ops->write(tr,buff,4);
+ ret = tr->ops->wait_ack(tr,buff,1);
+ ECRNX_PRINT("dl-fw >> sync, ret: %d\n", ret);
+ if((buff[0] != '3')||(ret < 0))
+ {
+ ECRNX_PRINT("dl-fw >> download sync fail, ack: %d\n", buff[0]);
+ dev_kfree_skb(skb);
+ return -1;
+ }
+ /* 1 usb sync */
+ p_sync->magic = 0xA5;
+ p_sync->type = 0x00;
+ p_sync->length = 0x00;
+ p_sync->crc8 = eswin_crc8(buff,sizeof(struct cfg_msg)-1);
+
+ tr->ops->write(tr, buff, sizeof(struct cfg_sync));
+ ret = tr->ops->wait_ack(tr, buff, 6);
+ ECRNX_PRINT("dl-fw >> sync, ret: %d\n", ret);
+ if((eswin_fw_ack_check(buff) < 0)||(ret < 0))
+ {
+ dev_kfree_skb(skb);
+ ECRNX_PRINT("fimeware download fail! \n");
+ return -1;
+ }
+
+ for(i=0;i<3;i++)
+ {
+ memcpy(length_str, dataAddr + offset, 8);
+ ret = kstrtoint(length_str, 10, (int*)&lengthLeft);
+ offset+=8;
+ /* 2 cfg */
+ p_msg->magic = 0xA5;
+ p_msg->type = 0x01;
+ p_msg->length = 0x08;
+ p_msg->data_addr = file_load_addr[i];
+ p_msg->data_len = lengthLeft;
+ p_msg->crc8 = 0x00;
+
+ tr->ops->write(tr, buff, sizeof(struct cfg_msg));
+ ret = tr->ops->wait_ack(tr, buff, 6);
+ if((eswin_fw_ack_check(buff) < 0)||(ret < 0))
+ {
+ dev_kfree_skb(skb);
+ ECRNX_PRINT("fimeware download fail! \n");
+ return -1;
+ }
+
+ buff[0] = 0xA5;
+ buff[1] = 0x01;
+
+ /* 3 load fw */
+ do {
+ lengthSend = (lengthLeft >= MAX_PACKET_SIZE) ? MAX_PACKET_SIZE : lengthLeft;
+ total_send_len += lengthSend;
+
+ buff[0] = 0xA5;
+ buff[1] = 0x01;
+ buff[2] = lengthSend & 0xFF;
+ buff[3] = (lengthSend>>8) & 0xFF;
+
+ memcpy(&buff[4], dataAddr + offset, lengthSend);
+ tr->ops->write(tr, buff, lengthSend + 5);
+
+ if(lengthSend >= MAX_PACKET_SIZE)
+ {
+ if(prev_dl_pst < total_send_len/(tr->fw->size/100))
+ {
+ prev_dl_pst = total_send_len/(tr->fw->size/100);
+ ECRNX_PRINT("firmware downloading %d%% \n", prev_dl_pst);
+ }
+ }
+ else if(i == 2)
+ {
+ ECRNX_PRINT("firmware downloading 100%% \n");
+ }
+
+ ret = tr->ops->wait_ack(tr, buff, 6);
+ if((eswin_fw_ack_check(buff) < 0)||(ret < 0))
+ {
+ dev_kfree_skb(skb);
+ ECRNX_PRINT("fimeware download fail! \n");
+ return -1;
+ }
+ offset += lengthSend;
+ lengthLeft -= lengthSend;
+ } while(lengthLeft);
+ }
+
+ /* 4 start up */
+ buff[0] = 0xA5;
+ buff[1] = 0x06;
+ buff[2] = 0x02;
+ buff[3] = 0x00;
+
+ buff[4] = (char)((file_load_addr[0]) & 0xFF);
+ buff[5] = (char)(((file_load_addr[0])>>8) & 0xFF);
+ buff[6] = (char)(((file_load_addr[0])>>16) & 0xFF);
+ buff[7] = (char)(((file_load_addr[0])>>24) & 0xFF);
+
+ tr->ops->write(tr, skb->data, 9);
+ tr->ops->wait_ack(tr, buff, 6);
+ if((eswin_fw_ack_check(buff) < 0)||(ret < 0))
+ {
+ dev_kfree_skb(skb);
+ ECRNX_PRINT("fimeware download fail! \n");
+ return -1;
+ }
+
+ ECRNX_PRINT("fimeware download successfully! \n");
+
+ dev_kfree_skb(skb);
+ return 0;
+}
+
+bool eswin_fw_file_chech(struct eswin *tr)
+{
+ int status;
+
+ if (fw_name == NULL)
+ goto err_fw;
+
+
+ //if (tr->fw)
+ // return true;
+
+ ECRNX_PRINT("%s, Checking firmware... (%s)\n", __func__, fw_name);
+
+ status = request_firmware((const struct firmware **)&tr->fw, fw_name, tr->dev);
+ if (status != 0) {
+ ECRNX_PRINT("%s, error status = %d\n", __func__, status);
+ goto err_fw;
+ }
+
+ ECRNX_PRINT("%s, request fw OK and size is %d\n", __func__, tr->fw->size);
+
+ if(fw_check_head(tr) == false)
+ {
+ goto err_fw;
+ }
+ return true;
+
+err_fw:
+ tr->fw = NULL;
+ return false;
+}
+
diff --git a/drivers/net/wireless/eswin/usb/fw.h b/drivers/net/wireless/eswin/usb/fw.h
new file mode 100644
index 000000000000..ab329e884d1e
--- /dev/null
+++ b/drivers/net/wireless/eswin/usb/fw.h
@@ -0,0 +1,19 @@
+/**
+******************************************************************************
+*
+* @file fw.h
+*
+* @brief ecrnx usb firmware download definitions
+*
+* Copyright (C) ESWIN 2015-2020
+*
+******************************************************************************
+*/
+
+#ifndef _FW_H_
+#define _FW_H_
+
+char eswin_fw_file_download(struct eswin *tr);
+bool eswin_fw_file_chech(struct eswin *tr);
+
+#endif
diff --git a/drivers/net/wireless/eswin/usb/usb.c b/drivers/net/wireless/eswin/usb/usb.c
new file mode 100644
index 000000000000..ddfda8e338b1
--- /dev/null
+++ b/drivers/net/wireless/eswin/usb/usb.c
@@ -0,0 +1,1041 @@
+/**
+ ******************************************************************************
+ *
+ * @file usb.c
+ *
+ * @brief usb driver function definitions
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/kthread.h>
+#include <linux/gpio.h>
+#include <linux/timer.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0)
+#include <uapi/linux/sched/types.h>
+#endif
+//#include <linux/usb.h>
+#include "usb.h"
+
+#include "core.h"
+//#include "debug.h"
+#include "ecrnx_usb.h"
+
+#include "usb_host_interface.h"
+#include "ecrnx_compat.h"
+#include "fw_head_check.h"
+#include "slave_log_buf.h"
+
+#define USB_RX_TIMER_TIMEOUT_US (200)
+//TCP PKG MAX LEN:1594; UDP PKG MAX LEN:1582
+#define USB_RX_LENGTH_THD (1594)
+#define USB_RX_UPLOAD_THD (12)
+
+static struct eswin_usb * g_usb;
+static unsigned int rx_packets =0;
+static struct timer_list usb_rx_timer = {0};
+
+static void usb_refill_recv_transfer(struct usb_infac_pipe * pipe);
+
+
+#define USB_LOG_MEM_SIZE (512*8)//(512*16)
+#define USB_LOG_MAX_SIZE 512
+
+struct ring_buffer buf_handle = {0};
+
+#if defined(CONFIG_ECRNX_DEBUGFS_CUSTOM)
+struct ring_buffer *usb_dbg_buf_get(void)
+{
+ if(buf_handle.init == false)
+ {
+ ring_buffer_init(&buf_handle, USB_LOG_MEM_SIZE);
+ }
+ return &buf_handle;
+}
+#endif
+
+void usb_dbg_printf(void * data, int len)
+{
+ if(buf_handle.init == false)
+ {
+ ring_buffer_init(&buf_handle, USB_LOG_MEM_SIZE);
+ }
+ ring_buffer_put(&buf_handle, data, len);
+}
+
+
+static struct usb_urb_context *
+usb_alloc_urb_from_pipe(struct usb_infac_pipe *pipe)
+{
+ struct usb_urb_context *urb_context = NULL;
+ unsigned long flags;
+
+ spin_lock_irqsave(&g_usb->cs_lock, flags);
+ if (!list_empty(&pipe->urb_list_head)) {
+ urb_context = list_first_entry(&pipe->urb_list_head,
+ struct usb_urb_context, link);
+ list_del(&urb_context->link);
+ pipe->urb_cnt--;
+ }
+ spin_unlock_irqrestore(&g_usb->cs_lock, flags);
+
+ return urb_context;
+}
+
+
+
+static void usb_free_urb_to_infac(struct usb_infac_pipe * pipe,
+ struct usb_urb_context *urb_context)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&g_usb->cs_lock, flags);
+ pipe->urb_cnt++;
+ list_add(&urb_context->link, &pipe->urb_list_head);
+ if(urb_context->urb)
+ {
+ usb_unanchor_urb(urb_context->urb);
+ usb_free_urb(urb_context->urb);
+ urb_context->urb = NULL;
+ }
+ spin_unlock_irqrestore(&g_usb->cs_lock, flags);
+}
+
+static void usb_cleanup_recv_urb(struct usb_urb_context *urb_context)
+{
+ dev_kfree_skb(urb_context->skb);
+ urb_context->skb = NULL;
+
+ usb_free_urb_to_infac(urb_context->pipe, urb_context);
+}
+
+static void usb_free_pipe_resources(struct usb_infac_pipe *pipe)
+{
+ struct usb_urb_context *urb_context;
+
+ for (;;) {
+ urb_context = usb_alloc_urb_from_pipe(pipe);
+
+ if (!urb_context)
+ break;
+ if(urb_context->urb)
+ {
+ usb_unanchor_urb(urb_context->urb);
+ usb_free_urb(urb_context->urb);
+ urb_context->urb = NULL;
+ }
+ kfree(urb_context);
+ }
+}
+
+static void usb_transmit_complete(struct urb *urb)
+{
+ struct usb_urb_context *urb_context = urb->context;
+ struct usb_infac_pipe *pipe = urb_context->pipe;
+ struct sk_buff *skb;
+ struct txdesc_api *tx_desc;
+ u32_l flag = 0;
+
+ //ECRNX_DBG(" %s entry, dir: %d!!", __func__, pipe->dir);
+
+ if (urb->status != 0) {
+ pipe->err_count++;
+ if(pipe->err_count%100 == 1)
+ {
+ ECRNX_PRINT( "pipe-dir: %d, failed:%d\n",
+ pipe->dir, urb->status);
+ }
+ if((pipe->err_status != urb->status)||(pipe->err_count == 10000))
+ {
+ pipe->err_status = urb->status;
+ pipe->err_count = 0;
+ }
+ }
+
+ skb = urb_context->skb;
+ urb_context->skb = NULL;
+ usb_free_urb_to_infac(urb_context->pipe, urb_context);
+
+ flag = *(u32_l *)skb->data;
+ if((u8_l)(flag & FLAG_MSG_TYPE_MASK) == TX_FLAG_TX_DESC)
+ {
+ tx_desc = (struct txdesc_api *)((u32_l *)skb->data + 1);
+ if (tx_desc->host.flags & TXU_CNTRL_MGMT)
+ {
+ return;
+ }
+ }
+
+ skb_queue_tail(&pipe->io_comp_queue, skb);
+#ifdef CONFIG_ECRNX_KTHREAD
+ wake_up_interruptible(&g_usb->wait_tx_comp);
+#endif
+#ifdef CONFIG_ECRNX_WORKQUEUE
+ schedule_work(&pipe->io_complete_work);
+#endif
+#ifdef CONFIG_ECRNX_TASKLET
+ tasklet_schedule(&pipe->tx_tasklet);
+#endif
+}
+
+
+void usb_rx_timer_handle(struct timer_list *time)
+{
+ if (rx_packets)
+ {
+ rx_packets = 0;
+ #ifdef CONFIG_ECRNX_KTHREAD
+ wake_up_interruptible(&g_usb->wait_rx_comp);
+ #endif
+ #ifdef CONFIG_ECRNX_WORKQUEUE
+ schedule_work(&g_usb->infac_data.pipe_rx.io_complete_work);
+ #endif
+ #ifdef CONFIG_ECRNX_TASKLET
+ tasklet_schedule(&g_usb->infac_data.pipe_rx.rx_tasklet);
+ #endif
+ }
+}
+
+static void usb_recv_complete(struct urb *urb)
+{
+ struct usb_urb_context *urb_context = urb->context;
+ struct usb_infac_pipe *pipe = urb_context->pipe;
+ struct sk_buff *skb;
+ int status = 0;
+
+ //ECRNX_PRINT(" %s entry, dir: %d!!", __func__, pipe->dir);
+
+
+ //ECRNX_PRINT( " usb recv pipe-dir %d stat %d len %d urb 0x%pK\n",
+ // pipe->dir, urb->status, urb->actual_length,
+ // urb);
+
+ if (urb->status != 0) {
+ status = -EIO;
+ switch (urb->status) {
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ /* no need to spew these errors when device
+ * removed or urb killed due to driver shutdown
+ */
+ status = -ECANCELED;
+ break;
+ default:
+ pipe->err_count++;
+ if(pipe->err_count%100 == 1)
+ {
+ ECRNX_PRINT("usb redcv pipe-dir %d failed: %d\n",
+ pipe->dir, urb->status);
+ }
+ if((pipe->err_status != status)||(pipe->err_count == 10000))
+ {
+ pipe->err_status = status;
+ pipe->err_count = 0;
+ }
+ break;
+ }
+ goto cleanup_recv_urb;
+ }
+
+ if (urb->actual_length == 0)
+ goto cleanup_recv_urb;
+
+ skb = urb_context->skb;
+
+ /* we are going to pass it up */
+ urb_context->skb = NULL;
+ skb_put(skb, urb->actual_length);
+
+ /* note: queue implements a lock */
+ skb_queue_tail(&pipe->io_comp_queue, skb);
+ usb_free_urb_to_infac(pipe, urb_context);
+
+#if 0
+ usb_rx_cnt++;
+ //printk("skb->len:%d %d %d\n", skb->len, usb_rx_cnt, urb->actual_length);
+ if (skb->len < 1500 ||usb_rx_cnt > 12)
+ {
+ usb_rx_cnt = 0;
+ schedule_work(&pipe->io_complete_work);
+ }
+#else
+
+ rx_packets++;
+ if (skb->len < USB_RX_LENGTH_THD || rx_packets > USB_RX_UPLOAD_THD)
+ {
+ rx_packets = 0;
+ #ifdef CONFIG_ECRNX_KTHREAD
+ wake_up_interruptible(&g_usb->wait_rx_comp);
+ #endif
+ #ifdef CONFIG_ECRNX_WORKQUEUE
+ schedule_work(&pipe->io_complete_work);
+ #endif
+ #ifdef CONFIG_ECRNX_TASKLET
+ tasklet_schedule(&pipe->rx_tasklet);
+ #endif
+ }
+ else
+ {
+ mod_timer(&usb_rx_timer, jiffies + usecs_to_jiffies(USB_RX_TIMER_TIMEOUT_US));
+ }
+#endif
+
+ //ECRNX_DBG(" entry %s, len: %d\n", __func__, urb->actual_length);
+ //print_hex_dump(KERN_INFO, "READ:", DUMP_PREFIX_ADDRESS, 32, 1, skb->data, urb->actual_length, false);
+
+ usb_refill_recv_transfer(pipe);
+ return;
+
+cleanup_recv_urb:
+ usb_cleanup_recv_urb(urb_context);
+}
+
+#ifdef CONFIG_ECRNX_TASKLET
+static void usb_tx_comp_tasklet(unsigned long data)
+{
+ struct usb_infac_pipe *pipe = (struct usb_infac_pipe *)data;
+ struct sk_buff *skb;
+ struct txdesc_api *tx_desc;
+ u32_l flag = 0;
+ ptr_addr host_id;
+
+ while ((skb = skb_dequeue(&pipe->io_comp_queue))) {
+ //ECRNX_DBG(" %s skb dequeue, skb:0x%08x, skb len: %d!!", __func__, skb, skb->len);
+
+ flag = *(u32_l *)skb->data;
+ if((u8_l)(flag & FLAG_MSG_TYPE_MASK) != TX_FLAG_TX_DESC)
+ {
+ dev_kfree_skb(skb);
+ continue;
+ }
+ tx_desc = (struct txdesc_api *)((u32_l *)skb->data + 1);
+ if (g_usb->p_eswin->data_cfm_callback && ((u8_l)(flag & FLAG_MSG_TYPE_MASK) == TX_FLAG_TX_DESC) && !(tx_desc->host.flags & TXU_CNTRL_MGMT))
+ {
+ memcpy(&host_id, tx_desc->host.packet_addr, sizeof(ptr_addr));
+ g_usb->p_eswin->data_cfm_callback(g_usb->p_eswin->umac_priv, (void*)host_id);
+ }
+ }
+}
+#endif
+
+#ifdef CONFIG_ECRNX_WORKQUEUE
+static void usb_tx_comp_work(struct work_struct *work)
+{
+ struct usb_infac_pipe *pipe = container_of(work,
+ struct usb_infac_pipe,
+ io_complete_work);
+ struct sk_buff *skb;
+ struct txdesc_api *tx_desc;
+ u32_l flag = 0;
+ ptr_addr host_id;
+
+ while ((skb = skb_dequeue(&pipe->io_comp_queue))) {
+ //ECRNX_DBG(" %s skb dequeue, skb:0x%08x, skb len: %d!!", __func__, skb, skb->len);
+
+ flag = *(u32_l *)skb->data;
+ if((u8_l)(flag & FLAG_MSG_TYPE_MASK) != TX_FLAG_TX_DESC)
+ {
+ dev_kfree_skb(skb);
+ continue;
+ }
+ tx_desc = (struct txdesc_api *)((u32_l *)skb->data + 1);
+ if (g_usb->p_eswin->data_cfm_callback && ((u8_l)(flag & FLAG_MSG_TYPE_MASK) == TX_FLAG_TX_DESC) && !(tx_desc->host.flags & TXU_CNTRL_MGMT))
+ {
+ memcpy(&host_id, tx_desc->host.packet_addr, sizeof(ptr_addr));
+ g_usb->p_eswin->data_cfm_callback(g_usb->p_eswin->umac_priv, (void*)host_id);
+ }
+ }
+}
+#endif
+
+#ifdef CONFIG_ECRNX_KTHREAD
+void usb_tx_comp_cb(struct sk_buff_head *queue)
+{
+ struct sk_buff *skb;
+ struct txdesc_api *tx_desc;
+ u32_l flag = 0;
+ ptr_addr host_id;
+
+ while ((skb = skb_dequeue(queue))) {
+ //ECRNX_DBG(" %s skb dequeue, skb:0x%08x, skb len: %d!!", __func__, skb, skb->len);
+
+ flag = *(u32_l *)skb->data;
+ if((u8_l)(flag & FLAG_MSG_TYPE_MASK) != TX_FLAG_TX_DESC)
+ {
+ dev_kfree_skb(skb);
+ continue;
+ }
+ tx_desc = (struct txdesc_api *)((u32_l *)skb->data + 1);
+ if (g_usb->p_eswin->data_cfm_callback && ((u8_l)(flag & FLAG_MSG_TYPE_MASK) == TX_FLAG_TX_DESC) && !(tx_desc->host.flags & TXU_CNTRL_MGMT))
+ {
+ memcpy(&host_id, tx_desc->host.packet_addr, sizeof(ptr_addr));
+ g_usb->p_eswin->data_cfm_callback(g_usb->p_eswin->umac_priv, (void*)host_id);
+ }
+ }
+}
+
+void usb_rx_comp_cb(struct usb_infac_pipe *pipe)
+{
+ struct sk_buff *skb;
+ while ((skb = skb_dequeue(&pipe->io_comp_queue))) {
+ if (g_usb->p_eswin->rx_callback)
+ {
+ g_usb->p_eswin->rx_callback(g_usb->p_eswin->umac_priv, skb, usb_pipeendpoint(pipe->usb_pipe_handle));
+ }
+ else
+ {
+ if (skb)
+ { // free the skb
+ ECRNX_DBG("%s, skb free: 0x%x !! \n",__func__);
+ dev_kfree_skb(skb);
+ }
+
+ }
+ }
+}
+
+static int eswin_tx_comp_thread(void *data)
+{
+ struct eswin_usb * p_usb = (struct eswin_usb *)data;
+ int ret;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 9, 0)
+ struct sched_param param = { .sched_priority = 1 };
+ param.sched_priority = 56;
+ sched_setscheduler(get_current(), SCHED_FIFO, &param);
+#else
+ sched_set_fifo(get_current());
+#endif
+ ECRNX_PRINT("eswin_tx_comp_thread entry\n");
+
+ while (!kthread_should_stop())
+ {
+ ret = wait_event_interruptible(p_usb->wait_tx_comp, skb_queue_len(&g_usb->infac_data.pipe_tx.io_comp_queue) != 0 ||
+ skb_queue_len(&g_usb->infac_msg.pipe_tx.io_comp_queue) != 0 || kthread_should_stop());
+ if (ret < 0)
+ {
+ ECRNX_ERR("usb tx pkg thread error!\n");
+ return 0;
+ }
+ if(kthread_should_stop())
+ {
+ continue;
+ }
+ usb_tx_comp_cb(&g_usb->infac_msg.pipe_tx.io_comp_queue);
+ usb_tx_comp_cb(&g_usb->infac_data.pipe_tx.io_comp_queue);
+ }
+ ECRNX_PRINT("tx pkg thread exit\n");
+ return 0;
+}
+
+static int eswin_rx_comp_thread(void *data)
+{
+ struct eswin_usb * p_usb = (struct eswin_usb *)data;
+ int ret;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 9, 0)
+ struct sched_param param = { .sched_priority = 1 };
+ param.sched_priority = 56;
+ sched_setscheduler(get_current(), SCHED_FIFO, &param);
+#else
+ sched_set_fifo(get_current());
+#endif
+ ECRNX_PRINT("eswin_rx_comp_thread entry\n");
+
+ //ECRNX_DBG(" %s entry, dir: %d!!", __func__, pipe->dir);
+ while (!kthread_should_stop())
+ {
+ ret = wait_event_interruptible(p_usb->wait_rx_comp, skb_queue_len(&g_usb->infac_data.pipe_rx.io_comp_queue) != 0 ||
+ skb_queue_len(&g_usb->infac_msg.pipe_rx.io_comp_queue) != 0|| kthread_should_stop());
+
+ if (ret < 0)
+ {
+ ECRNX_ERR("usb tx pkg thread error!\n");
+ return 0;
+ }
+ if(kthread_should_stop())
+ {
+ continue;
+ }
+ usb_rx_comp_cb(&g_usb->infac_msg.pipe_rx);
+ usb_rx_comp_cb(&g_usb->infac_data.pipe_rx);
+ }
+ ECRNX_PRINT("rx pkg thread exit\n");
+ return 0;
+}
+#endif
+
+#ifdef CONFIG_ECRNX_TASKLET
+static void usb_rx_comp_tasklet(unsigned long data)
+{
+ struct usb_infac_pipe *pipe = (struct usb_infac_pipe *)data;
+ struct sk_buff *skb;
+
+ //ECRNX_DBG(" %s entry, dir: %d!!", __func__, pipe->dir);
+
+ while ((skb = skb_dequeue(&pipe->io_comp_queue))) {
+ if (g_usb->p_eswin->rx_callback)
+ {
+ g_usb->p_eswin->rx_callback(g_usb->p_eswin->umac_priv, skb, usb_pipeendpoint(pipe->usb_pipe_handle));
+ }
+ else
+ {
+ if (skb)
+ { // free the skb
+ ECRNX_DBG("%s, skb free: 0x%x !! \n",__func__);
+ dev_kfree_skb(skb);
+ }
+
+ }
+
+ }
+}
+
+#endif
+
+#ifdef CONFIG_ECRNX_WORKQUEUE
+static void usb_rx_comp_work(struct work_struct *work)
+{
+ struct usb_infac_pipe *pipe = container_of(work,
+ struct usb_infac_pipe,
+ io_complete_work);
+ struct sk_buff *skb;
+ //ECRNX_DBG(" %s entry, dir: %d!!", __func__, pipe->dir);
+
+ while ((skb = skb_dequeue(&pipe->io_comp_queue))) {
+ if (g_usb->p_eswin->rx_callback)
+ {
+ g_usb->p_eswin->rx_callback(g_usb->p_eswin->umac_priv, skb, usb_pipeendpoint(pipe->usb_pipe_handle));
+ }
+ else
+ {
+ if (skb)
+ { // free the skb
+ ECRNX_DBG("%s, skb free: 0x%x !! \n",__func__);
+ dev_kfree_skb(skb);
+ }
+
+ }
+ }
+}
+#endif
+
+static void usb_flush_pipe(struct usb_infac_pipe * pipe)
+{
+ usb_kill_anchored_urbs(&pipe->urb_submitted);
+ if (pipe->dir == USB_DIR_TX) {
+ #ifdef CONFIG_ECRNX_KTHREAD
+ if(g_usb->kthread_tx_comp)
+ {
+ kthread_stop(g_usb->kthread_tx_comp);
+ wake_up_interruptible(&g_usb->wait_tx_comp);
+ g_usb->kthread_tx_comp = NULL;
+ }
+ #endif
+ #ifdef CONFIG_ECRNX_TASKLET
+ tasklet_kill(&pipe->tx_tasklet);
+ #endif
+ #ifdef CONFIG_ECRNX_WORKQUEUE
+ cancel_work_sync(&pipe->io_complete_work);
+ #endif
+ } else {
+ #ifdef CONFIG_ECRNX_KTHREAD
+ if(g_usb->kthread_rx_comp)
+ {
+ kthread_stop(g_usb->kthread_rx_comp);
+ wake_up_interruptible(&g_usb->wait_rx_comp);
+ g_usb->kthread_rx_comp = NULL;
+ }
+ #endif
+ #ifdef CONFIG_ECRNX_TASKLET
+ tasklet_kill(&pipe->rx_tasklet);
+ #endif
+ #ifdef CONFIG_ECRNX_WORKQUEUE
+ cancel_work_sync(&pipe->io_complete_work);
+ #endif
+ }
+}
+
+static void usb_flush_all(struct eswin_usb * p_usb)
+{
+ usb_flush_pipe(&p_usb->infac_data.pipe_rx);
+ usb_flush_pipe(&p_usb->infac_data.pipe_tx);
+ usb_flush_pipe(&p_usb->infac_msg.pipe_rx);
+ usb_flush_pipe(&p_usb->infac_msg.pipe_tx);
+}
+
+
+static void usb_refill_recv_transfer(struct usb_infac_pipe * pipe)
+{
+ struct usb_urb_context *urb_context;
+ struct urb *urb;
+ int usb_status;
+
+ for ( ;; ) {
+ urb_context = usb_alloc_urb_from_pipe(pipe);
+ if (!urb_context)
+ break;
+
+ urb_context->skb = dev_alloc_skb(USB_RX_MAX_BUF_SIZE);
+ if (!urb_context->skb)
+ goto err;
+
+ urb = usb_alloc_urb(0, GFP_ATOMIC);
+ if (!urb)
+ goto err;
+ urb_context->urb = urb;
+
+ usb_fill_bulk_urb(urb,
+ pipe->infac->udev,
+ pipe->usb_pipe_handle,
+ urb_context->skb->data,
+ USB_RX_MAX_BUF_SIZE,
+ usb_recv_complete, urb_context);
+
+ usb_anchor_urb(urb, &pipe->urb_submitted);
+ usb_status = usb_submit_urb(urb, GFP_ATOMIC);
+
+ if (usb_status) {
+ ECRNX_PRINT("usb_refill_recv_transfer, usb bulk recv failed: %d\n",
+ usb_status);
+ //usb_unanchor_urb(urb);
+ //usb_free_urb(urb);
+ goto err;
+ }
+ //usb_free_urb(urb);
+ }
+
+ return;
+
+err:
+ usb_cleanup_recv_urb(urb_context);
+}
+
+
+
+static int usb_hif_start(struct eswin *tr)
+{
+ struct eswin_usb * p_usb = (struct eswin_usb *)tr->drv_priv;
+
+ ECRNX_DBG("%s entry!!", __func__);
+
+ usb_refill_recv_transfer(&p_usb->infac_data.pipe_rx);
+ usb_refill_recv_transfer(&p_usb->infac_msg.pipe_rx);
+ timer_setup(&usb_rx_timer, usb_rx_timer_handle, 0);
+
+ return 0;
+}
+
+
+int usb_hif_xmit(struct eswin *tr, struct sk_buff *skb)
+{
+
+ unsigned int req_type = TX_FLAG_MAX;
+ struct eswin_usb * p_usb = (struct eswin_usb *)tr->drv_priv;
+ struct usb_infac_data_t * infac_data = NULL;
+ struct usb_infac_pipe * pipe = NULL;
+ struct usb_urb_context *urb_context;
+ struct urb *urb;
+ int ret = -1;
+
+ req_type = *((unsigned int*)(skb->data));
+ if((req_type & FLAG_MSG_TYPE_MASK) == TX_FLAG_TX_DESC)
+ {
+ infac_data = &p_usb->infac_data;
+ pipe = &infac_data->pipe_tx;
+ }
+ else
+ {
+ infac_data = &p_usb->infac_msg;
+ pipe = &infac_data->pipe_tx;
+ }
+
+ urb_context = usb_alloc_urb_from_pipe(pipe);
+ if (!urb_context) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ urb_context->skb = skb;
+
+ urb = usb_alloc_urb(0, GFP_ATOMIC);
+ if (!urb) {
+ ret = -ENOMEM;
+ goto err_free_urb_to_pipe;
+ }
+
+ urb_context->urb = urb;
+ usb_fill_bulk_urb(urb,
+ infac_data->udev,
+ pipe->usb_pipe_handle,
+ skb->data,
+ skb->len,
+ usb_transmit_complete, urb_context);
+
+ if (!(skb->len % infac_data->max_packet_size)) {
+ /* hit a max packet boundary on this pipe */
+ urb->transfer_flags |= URB_ZERO_PACKET;
+ }
+
+ usb_anchor_urb(urb, &pipe->urb_submitted);
+ ret = usb_submit_urb(urb, GFP_ATOMIC);
+ if (ret) {
+ ECRNX_PRINT("usb_hif_xmit, usb bulk transmit failed: %d\n", ret);
+ //usb_unanchor_urb(urb);
+ ret = -EINVAL;
+ goto err_free_urb_to_pipe;
+ }
+
+ //usb_free_urb(urb);
+
+ return 0;
+
+err_free_urb_to_pipe:
+ usb_free_urb_to_infac(urb_context->pipe, urb_context);
+err:
+ ECRNX_PRINT("usb_hif_xmit, pkg miss due to urb.\n");
+ skb_queue_tail(&pipe->io_comp_queue, skb);
+ #ifdef CONFIG_ECRNX_KTHREAD
+ wake_up_interruptible(&g_usb->wait_tx_comp);
+ #endif
+ #ifdef CONFIG_ECRNX_WORKQUEUE
+ schedule_work(&pipe->io_complete_work);
+ #endif
+ #ifdef CONFIG_ECRNX_TASKLET
+ tasklet_schedule(&pipe->tx_tasklet);
+ #endif
+ return ret;
+}
+
+static int usb_hif_write_raw(struct eswin *tr, const void* data, const u32 len)
+{
+ struct eswin_usb * p_usb = (struct eswin_usb *)tr->drv_priv;
+ struct usb_infac_data_t * infac_data = &p_usb->infac_data;
+ struct usb_infac_pipe * pipe = &infac_data->pipe_tx;
+
+ return usb_bulk_msg(infac_data->udev, pipe->usb_pipe_handle, (void*)data, len, NULL, 20000);
+}
+
+static int usb_hif_wait_ack(struct eswin *tr, void* data, const u32 len)
+{
+ struct eswin_usb * p_usb = (struct eswin_usb *)tr->drv_priv;
+ struct usb_infac_data_t * infac_data = &p_usb->infac_data;
+ struct usb_infac_pipe * pipe = &infac_data->pipe_rx;
+
+ return usb_bulk_msg(infac_data->udev, pipe->usb_pipe_handle, data, len, NULL, 20000);
+}
+
+#ifdef CONFIG_PM
+static int usb_hif_suspend(struct eswin *tr)
+{
+ return -EOPNOTSUPP;
+}
+
+static int usb_hif_resume(struct eswin *tr)
+{
+ return -EOPNOTSUPP;
+}
+#endif
+
+static struct usb_ops eswin_usb_hif_ops = {
+ .xmit = usb_hif_xmit,
+ .start = usb_hif_start,
+ .write = usb_hif_write_raw,
+ .wait_ack = usb_hif_wait_ack,
+ .suspend = usb_hif_suspend,
+ .resume = usb_hif_resume,
+};
+
+static int usb_create_pipe(struct usb_infac_pipe * pipe, int dir, bool flag)
+{
+ int i;
+ struct usb_urb_context *urb_context;
+ ECRNX_DBG("%s entry, dir: %d!!", __func__, dir);
+
+ pipe->dir = dir;
+ pipe->err_count = 0;
+ init_usb_anchor(&pipe->urb_submitted);
+ INIT_LIST_HEAD(&pipe->urb_list_head);
+
+ for (i = 0; i < (flag ? USB_MSG_URB_NUM : USB_DATA_URB_NUM); i++) {
+ urb_context = kzalloc(sizeof(*urb_context), GFP_KERNEL);
+ if (!urb_context)
+ return -ENOMEM;
+
+ urb_context->pipe = pipe;
+ //pipe->urb_cnt++;
+
+ usb_free_urb_to_infac(pipe, urb_context);
+ }
+
+ if (dir) {
+ #ifdef CONFIG_ECRNX_WORKQUEUE
+ INIT_WORK(&pipe->io_complete_work, usb_tx_comp_work);
+ #endif
+ #ifdef CONFIG_ECRNX_TASKLET
+ tasklet_init(&pipe->tx_tasklet, usb_tx_comp_tasklet, (unsigned long) pipe);
+ #endif
+ } else {
+ #ifdef CONFIG_ECRNX_WORKQUEUE
+ INIT_WORK(&pipe->io_complete_work, usb_rx_comp_work);
+ #endif
+ #ifdef CONFIG_ECRNX_TASKLET
+ tasklet_init(&pipe->rx_tasklet, usb_rx_comp_tasklet, (unsigned long) pipe);
+ #endif
+ }
+
+ skb_queue_head_init(&pipe->io_comp_queue);
+ return 0;
+}
+
+static int usb_create_infac(struct usb_interface *interface, struct usb_infac_data_t * p_infac, bool flag)
+{
+ struct usb_device *dev = interface_to_usbdev(interface);
+ struct usb_host_interface *iface_desc = interface->cur_altsetting;
+ struct usb_endpoint_descriptor *endpoint = NULL;
+
+ int i;
+ ECRNX_DBG("%s entry %d!!", __func__, iface_desc->desc.bNumEndpoints);
+
+ usb_get_dev(dev);
+ usb_set_intfdata(interface, p_infac);
+
+ p_infac->udev = dev;
+ p_infac->interface = interface;
+
+ for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
+ endpoint = &iface_desc->endpoint[i].desc;
+ if (endpoint->bEndpointAddress & USB_DIR_MASK) {
+ p_infac->pipe_rx.usb_pipe_handle = usb_rcvbulkpipe(dev, endpoint->bEndpointAddress);
+ p_infac->pipe_rx.infac = p_infac;
+ usb_create_pipe(&p_infac->pipe_rx, USB_DIR_RX, flag);
+ } else {
+ p_infac->pipe_tx.usb_pipe_handle = usb_sndbulkpipe(dev, endpoint->bEndpointAddress);
+ p_infac->pipe_tx.infac = p_infac;
+ usb_create_pipe(&p_infac->pipe_tx, USB_DIR_TX, flag);
+ }
+ }
+ p_infac->max_packet_size = le16_to_cpu(endpoint->wMaxPacketSize);;
+
+ return 0;
+}
+
+extern bool usb_status;
+extern struct eswin *pEswin;
+static int eswin_usb_probe(struct usb_interface *interface,
+ const struct usb_device_id *id)
+{
+ int ret;
+ struct eswin_usb * p_usb;
+ struct eswin* p_eswin;
+ struct usb_host_interface *iface_desc = interface->cur_altsetting;
+ struct usb_device *dev = interface_to_usbdev(interface);
+
+ if((usb_status == false) && dl_fw)
+ {
+ ECRNX_PRINT("%s entry, reset slave !!", __func__);
+ usb_control_msg(dev,
+ usb_sndctrlpipe(dev, 0),
+ 0x2,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0,
+ 0,
+ NULL, 0,10);
+ msleep(200);
+ }
+
+ ECRNX_PRINT("%s entry, func: %d, g_usb: %p !!", __func__, iface_desc->desc.bInterfaceNumber, g_usb);
+
+ if (iface_desc->desc.bInterfaceNumber == USB_INFAC_DATA) {
+ if (g_usb) {
+ p_usb = g_usb;
+ pEswin->dev = &interface->dev;
+ } else {
+ p_eswin = eswin_core_create(sizeof(struct eswin_usb), &interface->dev, &eswin_usb_hif_ops);
+ if(!p_eswin) {
+ dev_err(&interface->dev, "failed to allocate core\n");
+ return -ENOMEM;
+ }
+
+ p_usb = (struct eswin_usb *)p_eswin->drv_priv;
+ p_usb->p_eswin = p_eswin;
+ g_usb = p_usb;
+ spin_lock_init(&p_usb->cs_lock);
+ }
+
+ p_usb->dev = &interface->dev;
+
+ usb_create_infac(interface, &p_usb->infac_data, 0);
+ } else {
+ usb_create_infac(interface, &g_usb->infac_msg, 1);
+ //usb_hif_start(g_usb->p_eswin);
+
+ if (!g_usb->usb_enum_already) {
+ ret = eswin_core_register(g_usb->p_eswin);
+ if(ret) {
+ ECRNX_PRINT("failed to register core\n");
+ return ret;
+ }
+ } else {
+ g_usb->usb_enum_already = 1;
+ }
+ return 0;
+ }
+
+ #ifdef CONFIG_ECRNX_KTHREAD
+ if(iface_desc->desc.bInterfaceNumber == USB_INFAC_DATA)
+ {
+ init_waitqueue_head(&g_usb->wait_tx_comp);
+ init_waitqueue_head(&g_usb->wait_rx_comp);
+
+ g_usb->kthread_tx_comp = kthread_run(eswin_tx_comp_thread, g_usb, "tx_comp");
+ g_usb->kthread_rx_comp = kthread_run(eswin_rx_comp_thread, g_usb, "rx_comp");
+
+ if (IS_ERR(g_usb->kthread_tx_comp)) {
+ g_usb->kthread_tx_comp = NULL;
+ ECRNX_PRINT("kthread_tx_comp run fail\n");
+ }
+
+ if (IS_ERR(g_usb->kthread_rx_comp)) {
+ g_usb->kthread_rx_comp = NULL;
+ ECRNX_PRINT("kthread_rx_comp run fail\n");
+ }
+ ECRNX_PRINT("%s kthread_run success.", __func__);
+ }
+ #endif
+ ECRNX_PRINT("%s exit!!", __func__);
+ return 0;
+}
+
+static void eswin_usb_remove(struct usb_interface *interface)
+{
+ struct usb_infac_data_t * p_infac;
+ struct usb_host_interface *iface_desc = interface->cur_altsetting;
+
+ ECRNX_PRINT("%s entry!!", __func__);
+
+ p_infac = usb_get_intfdata(interface);
+
+ if (!p_infac) {
+ ECRNX_PRINT("%s, p_infa is null!!", __func__);
+ return;
+ }
+
+ if (iface_desc->desc.bInterfaceNumber == USB_INFAC_DATA) {
+ eswin_core_unregister(g_usb->p_eswin);
+ }
+
+ del_timer(&usb_rx_timer);
+
+ usb_flush_pipe(&p_infac->pipe_rx);
+ usb_flush_pipe(&p_infac->pipe_tx);
+ usb_free_pipe_resources(&p_infac->pipe_rx);
+ usb_free_pipe_resources(&p_infac->pipe_tx);
+
+ usb_set_intfdata(interface, NULL);
+ usb_put_dev(interface_to_usbdev(interface));
+ dl_fw = true;
+ offset = 0;
+}
+
+
+#ifdef CONFIG_PM
+static int eswin_usb_pm_suspend(struct usb_interface *interface, pm_message_t message)
+{
+ ECRNX_PRINT("entry %s\n", __func__);
+ usb_flush_all(g_usb);
+ return 0;
+}
+
+static int eswin_usb_pm_resume(struct usb_interface *interface)
+{
+ ECRNX_PRINT("entry %s\n", __func__);
+ usb_refill_recv_transfer(&g_usb->infac_data.pipe_rx);
+ usb_refill_recv_transfer(&g_usb->infac_msg.pipe_rx);
+ return 0;
+}
+
+#else
+#define eswin_usb_pm_suspend NULL
+#define eswin_usb_pm_resume NULL
+#endif
+
+/* table of devices that work with this driver */
+static struct usb_device_id eswin_usb_ids[] = {
+ {USB_DEVICE(0x3452, 0x6600)},
+ { /* Terminating entry */ },
+};
+
+MODULE_DEVICE_TABLE(usb, eswin_usb_ids);
+static struct usb_driver eswin_usb_driver = {
+ .name = "eswin_usb",
+ .probe = eswin_usb_probe,
+ .suspend = eswin_usb_pm_suspend,
+ .resume = eswin_usb_pm_resume,
+ .disconnect = eswin_usb_remove,
+ .id_table = eswin_usb_ids,
+ .supports_autosuspend = true,
+};
+
+static int __init eswin_usb_init(void)
+{
+ int ret;
+
+ #ifdef CONFIG_POWERKEY_GPIO
+ int pk_gpio = 4;
+ pk_gpio = CONFIG_POWERKEY_GPIO;
+ gpio_direction_output(pk_gpio,0);
+ msleep(1000);
+ gpio_direction_output(pk_gpio,1);
+ #endif
+
+ ECRNX_PRINT("%s entry !!\n", __func__);
+ ret = usb_register(&eswin_usb_driver);
+ if (ret)
+ ECRNX_PRINT("sdio driver registration failed: %d\n", ret);
+
+ ECRNX_PRINT("%s exit !!\n", __func__);
+ return ret;
+}
+
+static void __exit eswin_usb_exit(void)
+{
+ ECRNX_PRINT("%s entry !!\n", __func__);
+ usb_deregister(&eswin_usb_driver);
+ ECRNX_PRINT("%s exit !!\n", __func__);
+}
+
+void ecrnx_usb_reset_sync_fw(void)
+{
+ usb_control_msg(g_usb->infac_msg.udev,
+ usb_sndctrlpipe(g_usb->infac_msg.udev, 0),
+ 0x2,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0,
+ 0,
+ NULL, 0,10);
+}
+
+int ecrnx_usb_register_drv(void)
+{
+ return eswin_usb_init();
+}
+
+void ecrnx_usb_unregister_drv(void)
+{
+ eswin_core_unregister(g_usb->p_eswin);
+ ecrnx_usb_reset_sync_fw();
+ return eswin_usb_exit();
+}
+
+struct device *eswin_usb_get_dev(void *plat)
+{
+ struct eswin* tr = (struct eswin*)plat;
+
+ return tr->dev;
+}
+
diff --git a/drivers/net/wireless/eswin/usb/usb.h b/drivers/net/wireless/eswin/usb/usb.h
new file mode 100644
index 000000000000..27e9592bf30c
--- /dev/null
+++ b/drivers/net/wireless/eswin/usb/usb.h
@@ -0,0 +1,101 @@
+/**
+ ******************************************************************************
+ *
+ * @file usb.h
+ *
+ * @brief usb driver definitions
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+
+#ifndef __USB_H
+#define __USB_H
+
+#include "ecrnx_defs.h"
+#include <linux/usb.h>
+
+#define USB_INFAC_DATA 0
+#define USB_INFAC_MSG 1
+
+#define USB_DIR_MASK 0x80
+#define USB_NUM_MASK 0x7F
+
+#define USB_DATA_URB_NUM 64
+#define USB_MSG_URB_NUM 16
+
+#define USB_DIR_RX 0
+#define USB_DIR_TX 1
+
+#define USB_RX_MAX_BUF_SIZE 4096
+
+struct usb_infac_pipe {
+ int dir;
+ u32 urb_cnt;
+ struct usb_infac_data_t *infac;
+
+ struct usb_anchor urb_submitted;
+ unsigned int usb_pipe_handle;
+ struct list_head urb_list_head;
+ #ifdef CONFIG_ECRNX_WORKQUEUE
+ struct work_struct io_complete_work;
+ #endif
+ #ifdef CONFIG_ECRNX_TASKLET
+ struct tasklet_struct tx_tasklet;
+ struct tasklet_struct rx_tasklet;
+ #endif
+ struct sk_buff_head io_comp_queue;
+ unsigned int err_count;
+ int err_status;
+};
+
+
+struct usb_infac_data_t {
+ struct usb_interface *interface;
+ struct usb_device *udev;
+ u16 max_packet_size;
+
+ struct usb_infac_pipe pipe_rx;
+ struct usb_infac_pipe pipe_tx;
+};
+
+struct eswin_usb {
+ struct eswin * p_eswin;
+ struct device * dev;
+ struct usb_infac_data_t infac_data;
+ struct usb_infac_data_t infac_msg;
+ #ifdef CONFIG_ECRNX_KTHREAD
+ struct task_struct *kthread_tx_comp;
+ struct task_struct *kthread_rx_comp;
+
+ wait_queue_head_t wait_tx_comp;
+ wait_queue_head_t wait_rx_comp;
+ #endif
+ spinlock_t cs_lock;
+ u8 usb_enum_already;
+};
+
+struct usb_urb_context {
+ struct list_head link;
+ struct sk_buff *skb;
+ struct urb *urb;
+ struct usb_infac_pipe * pipe;
+};
+
+
+struct usb_ops {
+ int (*start)(struct eswin *tr);
+ int (*xmit)(struct eswin *tr, struct sk_buff *skb);
+ int (*suspend)(struct eswin *tr);
+ int (*resume)(struct eswin *tr);
+ int (*write)(struct eswin *tr, const void* data, const u32 len);
+ int (*wait_ack)(struct eswin *tr, void* data, const u32 len);
+};
+
+
+
+extern int ecrnx_usb_register_drv(void);
+extern void ecrnx_usb_unregister_drv(void);
+
+#endif /* __SDIO_H */
diff --git a/drivers/net/wireless/eswin/usb/usb_host_interface.h b/drivers/net/wireless/eswin/usb/usb_host_interface.h
new file mode 100644
index 000000000000..195753ea4c66
--- /dev/null
+++ b/drivers/net/wireless/eswin/usb/usb_host_interface.h
@@ -0,0 +1,29 @@
+/**
+ ******************************************************************************
+ *
+ * @file usb_host_interface.h
+ *
+ * @brief usb host interface definitions
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+
+#ifndef _USB_HOST_INTERFACE_H
+#define _USB_HOST_INTERFACE_H
+/*******************************************************************************
+ * Function: usb_host_send
+ * Description:send buff from host to slave
+ * Parameters:
+ * Input: void *buff, int len, int flag
+ *
+ * Output:
+ *
+ * Returns: 0
+ *
+ *
+ * Others:
+ ********************************************************************************/
+int usb_host_send(void *buff, int len, int flag);
+#endif
diff --git a/drivers/net/wireless/eswin/wifi_ecr6600u.cfg b/drivers/net/wireless/eswin/wifi_ecr6600u.cfg
new file mode 100644
index 000000000000..df5912e6190b
--- /dev/null
+++ b/drivers/net/wireless/eswin/wifi_ecr6600u.cfg
@@ -0,0 +1,3 @@
+DRIVER_LOG_LEVEL=3
+FW_LOG_LEVEL=2
+FW_LOG_TYPE=0
diff --git a/drivers/phy/m31/7110-m31-dphy.h b/drivers/phy/m31/7110-m31-dphy.h
index 268bbc8dad3f..e346cbdc80ea 100644
--- a/drivers/phy/m31/7110-m31-dphy.h
+++ b/drivers/phy/m31/7110-m31-dphy.h
@@ -288,6 +288,29 @@ static const struct m31_dphy_config m31_dphy_configs[] = {
{25000000, 2400000000, 0x1, 0xC0, 0x000000, 0x0, 0x0D, 0x20, 0x16, 0x0A, 0x50, 0x15, 0x00, 0x18,},
{25000000, 2500000000, 0x1, 0xC8, 0x000000, 0x0, 0x0E, 0x21, 0x16, 0x0B, 0x53, 0x16, 0x00, 0x18,},
#elif (M31_DPHY_REFCLK == M31_DPHY_REFCLK_12M)
+ {12000000, 160000000, 0x0, 0x6a, 0xaa<<16|0xaa<<8|0xaa, 0x3, 0xa, 0x17, 0x11, 0x5, 0x2b, 0xd, 0x7, 0x3d,},
+ {12000000, 170000000, 0x0, 0x71, 0x55<<16|0x55<<8|0x55, 0x3, 0xb, 0x18, 0x11, 0x5, 0x2e, 0xd, 0x7, 0x3d,},
+ {12000000, 180000000, 0x0, 0x78, 0x0<<16|0x0<<8|0x0, 0x3, 0xb, 0x19, 0x12, 0x6, 0x30, 0xe, 0x7, 0x3e,},
+ {12000000, 190000000, 0x0, 0x7e, 0xaa<<16|0xaa<<8|0xaa, 0x3, 0xc, 0x1a, 0x12, 0x6, 0x33, 0xe, 0x7, 0x3e,},
+ {12000000, 200000000, 0x0, 0x85, 0x55<<16|0x55<<8|0x55, 0x3, 0xc, 0x1b, 0x13, 0x7, 0x35, 0xf, 0x7, 0x3f,},
+ {12000000, 320000000, 0x0, 0x6a, 0xaa<<16|0xaa<<8|0xaa, 0x2, 0x8, 0x14, 0xf, 0x5, 0x2b, 0xd, 0x3, 0x23,},
+ {12000000, 330000000, 0x0, 0x6e, 0x0<<16|0x0<<8|0x0, 0x2, 0x8, 0x15, 0xf, 0x5, 0x2d, 0xd, 0x3, 0x23,},
+ {12000000, 340000000, 0x0, 0x71, 0x55<<16|0x55<<8|0x55, 0x2, 0x9, 0x15, 0xf, 0x5, 0x2e, 0xd, 0x3, 0x23,},
+ {12000000, 350000000, 0x0, 0x74, 0xaa<<16|0xaa<<8|0xaa, 0x2, 0x9, 0x15, 0x10, 0x6, 0x2f, 0xe, 0x3, 0x24,},
+ {12000000, 360000000, 0x0, 0x78, 0x0<<16|0x0<<8|0x0, 0x2, 0x9, 0x16, 0x10, 0x6, 0x30, 0xe, 0x3, 0x24,},
+ {12000000, 370000000, 0x0, 0x7b, 0x55<<16|0x55<<8|0x55, 0x2, 0x9, 0x17, 0x10, 0x6, 0x32, 0xe, 0x3, 0x24,},
+ {12000000, 380000000, 0x0, 0x7e, 0xaa<<16|0xaa<<8|0xaa, 0x2, 0xa, 0x17, 0x10, 0x6, 0x33, 0xe, 0x3, 0x24,},
+ {12000000, 390000000, 0x0, 0x82, 0x0<<16|0x0<<8|0x0, 0x2, 0xa, 0x17, 0x11, 0x6, 0x35, 0xf, 0x3, 0x25,},
+ {12000000, 400000000, 0x0, 0x85, 0x55<<16|0x55<<8|0x55, 0x2, 0xa, 0x18, 0x11, 0x7, 0x35, 0xf, 0x3, 0x25,},
+ {12000000, 410000000, 0x0, 0x88, 0xaa<<16|0xaa<<8|0xaa, 0x2, 0xa, 0x19, 0x11, 0x7, 0x37, 0xf, 0x3, 0x25,},
+ {12000000, 420000000, 0x0, 0x8c, 0x0<<16|0x0<<8|0x0, 0x2, 0xa, 0x19, 0x12, 0x7, 0x38, 0x10, 0x3, 0x26,},
+ {12000000, 430000000, 0x0, 0x8f, 0x55<<16|0x55<<8|0x55, 0x2, 0xb, 0x19, 0x12, 0x7, 0x39, 0x10, 0x3, 0x26,},
+ {12000000, 440000000, 0x0, 0x92, 0xaa<<16|0xaa<<8|0xaa, 0x2, 0xb, 0x1a, 0x12, 0x7, 0x3b, 0x10, 0x3, 0x26,},
+ {12000000, 450000000, 0x0, 0x96, 0x0<<16|0x0<<8|0x0, 0x2, 0xb, 0x1b, 0x12, 0x8, 0x3c, 0x10, 0x3, 0x26,},
+ {12000000, 460000000, 0x0, 0x99, 0x55<<16|0x55<<8|0x55, 0x2, 0xb, 0x1b, 0x13, 0x8, 0x3d, 0x11, 0x3, 0x27,},
+ {12000000, 470000000, 0x0, 0x9c, 0xaa<<16|0xaa<<8|0xaa, 0x2, 0xc, 0x1b, 0x13, 0x8, 0x3e, 0x11, 0x3, 0x27,},
+ {12000000, 480000000, 0x0, 0xa0, 0x0<<16|0x0<<8|0x0, 0x2, 0xc, 0x1c, 0x13, 0x8, 0x40, 0x11, 0x3, 0x27,},
+ {12000000, 490000000, 0x0, 0xa3, 0x55<<16|0x55<<8|0x55, 0x2, 0xc, 0x1d, 0x14, 0x8, 0x42, 0x12, 0x3, 0x28,},
{12000000, 500000000, 0x0, 0xa6, 0xaa<<16|0xaa<<8|0xaa, 0x2, 0xc, 0x1d, 0x14, 0x9, 0x42, 0x12, 0x3, 0x28,},
{12000000, 510000000, 0x0, 0xaa, 0x0<<16|0x0<<8|0x0, 0x2, 0xc, 0x1e, 0x14, 0x9, 0x44, 0x12, 0x3, 0x28,},
{12000000, 520000000, 0x0, 0xad, 0x55<<16|0x55<<8|0x55, 0x2, 0xd, 0x1e, 0x15, 0x9, 0x45, 0x13, 0x3, 0x29,},
diff --git a/drivers/phy/m31/phy-m31-dphy-tx0.c b/drivers/phy/m31/phy-m31-dphy-tx0.c
index f86fd8d83a7a..63e3271b6ef1 100644
--- a/drivers/phy/m31/phy-m31-dphy-tx0.c
+++ b/drivers/phy/m31/phy-m31-dphy-tx0.c
@@ -197,308 +197,6 @@ static int sf_dphy_clkrst_disa_assert(struct device *dev, struct sf_dphy *dphy)
return ret;
}
-/*
-*static int sf_dphy_remove(struct platform_device *pdev)
-*{
-* struct sf_dphy *dphy = dev_get_drvdata(&pdev->dev);
-* reset_control_assert(dphy->sys_rst);
-* //reset_control_assert(dphy->txbytehs_rst);
-* clk_disable_unprepare(dphy->txesc_clk);
-* return 0;
-*}
-*/
-
-#if 0
-static u32 top_sys_read32(struct sf_dphy *priv, u32 reg)
-{
- return ioread32(priv->topsys + reg);
-}
-
-
-static inline void top_sys_write32(struct sf_dphy *priv, u32 reg, u32 val)
-{
- iowrite32(val, priv->topsys + reg);
-}
-
-static void dsi_csi2tx_sel(struct sf_dphy *priv, int sel)
-{
- u32 temp = 0;
- temp = top_sys_read32(priv, SCFG_DSI_CSI_SEL);
- temp &= ~(0x1);
- temp |= (sel & 0x1);
- top_sys_write32(priv, SCFG_DSI_CSI_SEL, temp);
-}
-
-static void dphy_clane_hs_txready_sel(struct sf_dphy *priv, u32 ready_sel)
-{
- top_sys_write32(priv, SCFG_TXREADY_SRC_SEL_D, ready_sel);
- top_sys_write32(priv, SCFG_TXREADY_SRC_SEL_C, ready_sel);
- top_sys_write32(priv, SCFG_HS_PRE_ZERO_T_D, 0x30);
- top_sys_write32(priv, SCFG_HS_PRE_ZERO_T_C, 0x30);
-}
-
-static void mipi_tx_lxn_set(struct sf_dphy *priv, u32 reg, u32 n_hstx, u32 p_hstx)
-{
- u32 temp = 0;
-
- temp = n_hstx;
- temp |= p_hstx << 5;
- top_sys_write32(priv, reg, temp);
-}
-
-static void dphy_config(struct sf_dphy *priv, int bit_rate)
-{
- int pre_div, fbk_int, extd_cycle_sel;
- int dhs_pre_time, dhs_zero_time, dhs_trial_time;
- int chs_pre_time, chs_zero_time, chs_trial_time;
- int chs_clk_pre_time, chs_clk_post_time;
- u32 set_val = 0;
-
- mipi_tx_lxn_set(priv, SCFG_L0N_L0P_HSTX, 0x10, 0x10);
- mipi_tx_lxn_set(priv, SCFG_L1N_L1P_HSTX, 0x10, 0x10);
- mipi_tx_lxn_set(priv, SCFG_L2N_L2P_HSTX, 0x10, 0x10);
- mipi_tx_lxn_set(priv, SCFG_L3N_L3P_HSTX, 0x10, 0x10);
- mipi_tx_lxn_set(priv, SCFG_L4N_L4P_HSTX, 0x10, 0x10);
-
- if(bit_rate == 80) {
- pre_div=0x1, fbk_int=2*0x33, extd_cycle_sel=0x4,
- dhs_pre_time=0xe, dhs_zero_time=0x1d, dhs_trial_time=0x15,
- chs_pre_time=0x5, chs_zero_time=0x2b, chs_trial_time=0xd,
- chs_clk_pre_time=0xf,
- chs_clk_post_time=0x71;
- } else if (bit_rate == 100) {
- pre_div=0x1, fbk_int=2*0x40, extd_cycle_sel=0x4,
- dhs_pre_time=0x10, dhs_zero_time=0x21, dhs_trial_time=0x17,
- chs_pre_time=0x7, chs_zero_time=0x35, chs_trial_time=0xf,
- chs_clk_pre_time=0xf,
- chs_clk_post_time=0x73;
- } else if (bit_rate == 200) {
- pre_div=0x1, fbk_int=2*0x40, extd_cycle_sel=0x3;
- dhs_pre_time=0xc, dhs_zero_time=0x1b, dhs_trial_time=0x13;
- chs_pre_time=0x7, chs_zero_time=0x35, chs_trial_time=0xf,
- chs_clk_pre_time=0x7,
- chs_clk_post_time=0x3f;
- } else if(bit_rate == 300) {
- pre_div=0x1, fbk_int=2*0x60, extd_cycle_sel=0x3,
- dhs_pre_time=0x11, dhs_zero_time=0x25, dhs_trial_time=0x19,
- chs_pre_time=0xa, chs_zero_time=0x50, chs_trial_time=0x15,
- chs_clk_pre_time=0x7,
- chs_clk_post_time=0x45;
- } else if(bit_rate == 400) {
- pre_div=0x1, fbk_int=2*0x40, extd_cycle_sel=0x2,
- dhs_pre_time=0xa, dhs_zero_time=0x18, dhs_trial_time=0x11,
- chs_pre_time=0x7, chs_zero_time=0x35, chs_trial_time=0xf,
- chs_clk_pre_time=0x3,
- chs_clk_post_time=0x25;
- } else if(bit_rate == 500 ) {
- pre_div=0x1, fbk_int=2*0x50, extd_cycle_sel=0x2,
- dhs_pre_time=0xc, dhs_zero_time=0x1d, dhs_trial_time=0x14,
- chs_pre_time=0x9, chs_zero_time=0x42, chs_trial_time=0x12,
- chs_clk_pre_time=0x3,
- chs_clk_post_time=0x28;
- } else if(bit_rate == 600 ) {
- pre_div=0x1, fbk_int=2*0x60, extd_cycle_sel=0x2,
- dhs_pre_time=0xe, dhs_zero_time=0x23, dhs_trial_time=0x17,
- chs_pre_time=0xa, chs_zero_time=0x50, chs_trial_time=0x15,
- chs_clk_pre_time=0x3,
- chs_clk_post_time=0x2b;
- } else if(bit_rate == 700) {
- pre_div=0x1, fbk_int=2*0x38, extd_cycle_sel=0x1,
- dhs_pre_time=0x8, dhs_zero_time=0x14, dhs_trial_time=0xf,
- chs_pre_time=0x6, chs_zero_time=0x2f, chs_trial_time=0xe,
- chs_clk_pre_time=0x1,
- chs_clk_post_time=0x16;
- } else if(bit_rate == 800 ) {
- pre_div=0x1, fbk_int=2*0x40, extd_cycle_sel=0x1,
- dhs_pre_time=0x9, dhs_zero_time=0x17, dhs_trial_time=0x10,
- chs_pre_time=0x7, chs_zero_time=0x35, chs_trial_time=0xf,
- chs_clk_pre_time=0x1,
- chs_clk_post_time=0x18;
- } else if(bit_rate == 900 ) {
- pre_div=0x1, fbk_int=2*0x48, extd_cycle_sel=0x1,
- dhs_pre_time=0xa, dhs_zero_time=0x19, dhs_trial_time=0x12,
- chs_pre_time=0x8, chs_zero_time=0x3c, chs_trial_time=0x10,
- chs_clk_pre_time=0x1,
- chs_clk_post_time=0x19;
- } else if(bit_rate == 1000) {
- pre_div=0x1, fbk_int=2*0x50, extd_cycle_sel=0x1,
- dhs_pre_time=0xb, dhs_zero_time=0x1c, dhs_trial_time=0x13,
- chs_pre_time=0x9, chs_zero_time=0x42, chs_trial_time=0x12,
- chs_clk_pre_time=0x1,
- chs_clk_post_time=0x1b;
- } else if(bit_rate == 1100) {
- pre_div=0x1, fbk_int=2*0x58, extd_cycle_sel=0x1,
- dhs_pre_time=0xc, dhs_zero_time=0x1e, dhs_trial_time=0x15,
- chs_pre_time=0x9, chs_zero_time=0x4a, chs_trial_time=0x14,
- chs_clk_pre_time=0x1,
- chs_clk_post_time=0x1d;
- } else if(bit_rate == 1200) {
- pre_div=0x1, fbk_int=2*0x60, extd_cycle_sel=0x1,
- dhs_pre_time=0xe, dhs_zero_time=0x20, dhs_trial_time=0x16,
- chs_pre_time=0xa, chs_zero_time=0x50, chs_trial_time=0x15,
- chs_clk_pre_time=0x1,
- chs_clk_post_time=0x1e;
- } else if(bit_rate == 1300) {
- pre_div=0x1, fbk_int=2*0x34, extd_cycle_sel=0x0,
- dhs_pre_time=0x7, dhs_zero_time=0x12, dhs_trial_time=0xd,
- chs_pre_time=0x5, chs_zero_time=0x2c, chs_trial_time=0xd,
- chs_clk_pre_time=0x0,
- chs_clk_post_time=0xf;
- } else if(bit_rate == 1400) {
- pre_div=0x1, fbk_int=2*0x38, extd_cycle_sel=0x0,
- dhs_pre_time=0x7, dhs_zero_time=0x14, dhs_trial_time=0xe,
- chs_pre_time=0x6, chs_zero_time=0x2f, chs_trial_time=0xe,
- chs_clk_pre_time=0x0,
- chs_clk_post_time=0x10;
- } else if(bit_rate == 1500) {
- pre_div=0x1, fbk_int=2*0x3c, extd_cycle_sel=0x0,
- dhs_pre_time=0x8, dhs_zero_time=0x14, dhs_trial_time=0xf,
- chs_pre_time=0x6, chs_zero_time=0x32, chs_trial_time=0xe,
- chs_clk_pre_time=0x0,
- chs_clk_post_time=0x11;
- } else if(bit_rate == 1600) {
- pre_div=0x1, fbk_int=2*0x40, extd_cycle_sel=0x0,
- dhs_pre_time=0x9, dhs_zero_time=0x15, dhs_trial_time=0x10,
- chs_pre_time=0x7, chs_zero_time=0x35, chs_trial_time=0xf,
- chs_clk_pre_time=0x0,
- chs_clk_post_time=0x12;
- } else if(bit_rate == 1700) {
- pre_div=0x1, fbk_int=2*0x44, extd_cycle_sel=0x0,
- dhs_pre_time=0x9, dhs_zero_time=0x17, dhs_trial_time=0x10,
- chs_pre_time=0x7, chs_zero_time=0x39, chs_trial_time=0x10,
- chs_clk_pre_time=0x0,
- chs_clk_post_time=0x12;
- } else if(bit_rate == 1800) {
- pre_div=0x1, fbk_int=2*0x48, extd_cycle_sel=0x0,
- dhs_pre_time=0xa, dhs_zero_time=0x18, dhs_trial_time=0x11,
- chs_pre_time=0x8, chs_zero_time=0x3c, chs_trial_time=0x10,
- chs_clk_pre_time=0x0,
- chs_clk_post_time=0x13;
- } else if(bit_rate == 1900) {
- pre_div=0x1, fbk_int=2*0x4c, extd_cycle_sel=0x0,
- dhs_pre_time=0xa, dhs_zero_time=0x1a, dhs_trial_time=0x12,
- chs_pre_time=0x8, chs_zero_time=0x3f, chs_trial_time=0x11,
- chs_clk_pre_time=0x0,
- chs_clk_post_time=0x14;
- } else if(bit_rate == 2000) {
- pre_div=0x1, fbk_int=2*0x50, extd_cycle_sel=0x0,
- dhs_pre_time=0xb, dhs_zero_time=0x1b, dhs_trial_time=0x13,
- chs_pre_time=0x9, chs_zero_time=0x42, chs_trial_time=0x12,
- chs_clk_pre_time=0x0,
- chs_clk_post_time=0x15;
- } else if(bit_rate == 2100) {
- pre_div=0x1, fbk_int=2*0x54, extd_cycle_sel=0x0,
- dhs_pre_time=0xb, dhs_zero_time=0x1c, dhs_trial_time=0x13,
- chs_pre_time=0x9, chs_zero_time=0x46, chs_trial_time=0x13,
- chs_clk_pre_time=0x0,
- chs_clk_post_time=0x15;
- } else if(bit_rate == 2200) {
- pre_div=0x1, fbk_int=2*0x5b, extd_cycle_sel=0x0,
- dhs_pre_time=0xc, dhs_zero_time=0x1d, dhs_trial_time=0x14,
- chs_pre_time=0x9, chs_zero_time=0x4a, chs_trial_time=0x14,
- chs_clk_pre_time=0x0,
- chs_clk_post_time=0x16;
- } else if(bit_rate == 2300) {
- pre_div=0x1, fbk_int=2*0x5c, extd_cycle_sel=0x0,
- dhs_pre_time=0xc, dhs_zero_time=0x1f, dhs_trial_time=0x15,
- chs_pre_time=0xa, chs_zero_time=0x4c, chs_trial_time=0x14,
- chs_clk_pre_time=0x0,
- chs_clk_post_time=0x17;
- } else if(bit_rate == 2400) {
- pre_div=0x1, fbk_int=2*0x60, extd_cycle_sel=0x0,
- dhs_pre_time=0xd, dhs_zero_time=0x20, dhs_trial_time=0x16,
- chs_pre_time=0xa, chs_zero_time=0x50, chs_trial_time=0x15,
- chs_clk_pre_time=0x0,
- chs_clk_post_time=0x18;
- } else if(bit_rate == 2500) {
- pre_div=0x1, fbk_int=2*0x64, extd_cycle_sel=0x0,
- dhs_pre_time=0xe, dhs_zero_time=0x21, dhs_trial_time=0x16,
- chs_pre_time=0xb, chs_zero_time=0x53, chs_trial_time=0x16,
- chs_clk_pre_time=0x0,
- chs_clk_post_time=0x18;
- } else {
- //default bit_rate == 700
- pre_div=0x1, fbk_int=2*0x38, extd_cycle_sel=0x1,
- dhs_pre_time=0x8, dhs_zero_time=0x14, dhs_trial_time=0xf,
- chs_pre_time=0x6, chs_zero_time=0x2f, chs_trial_time=0xe,
- chs_clk_pre_time=0x1,
- chs_clk_post_time=0x16;
- }
- top_sys_write32(priv, SCFG_REFCLK_SEL, 0x3);
-
- set_val = 0
- | (1 << OFFSET_CFG_L1_SWAP_SEL)
- | (4 << OFFSET_CFG_L2_SWAP_SEL)
- | (2 << OFFSET_CFG_L3_SWAP_SEL)
- | (3 << OFFSET_CFG_L4_SWAP_SEL);
- top_sys_write32(priv, SCFG_LX_SWAP_SEL, set_val);
-
- set_val = 0
- | (0 << OFFSET_SCFG_PWRON_READY_N)
- | (1 << OFFSET_RG_CDTX_PLL_FM_EN)
- | (0 << OFFSET_SCFG_PLLSSC_EN)
- | (1 << OFFSET_RG_CDTX_PLL_LDO_STB_X2_EN);
- top_sys_write32(priv, SCFG_DBUS_PW_PLL_SSC_LD0, set_val);
-
- set_val = fbk_int
- | (pre_div << 9);
- top_sys_write32(priv, SCFG_RG_CDTX_PLL_FBK_PRE, set_val);
-
- top_sys_write32(priv, SCFG_RG_EXTD_CYCLE_SEL, extd_cycle_sel);
-
- set_val = chs_zero_time
- | (dhs_pre_time << OFFSET_DHS_PRE_TIME)
- | (dhs_trial_time << OFFSET_DHS_TRIAL_TIME)
- | (dhs_zero_time << OFFSET_DHS_ZERO_TIME);
- top_sys_write32(priv, SCFG_RG_CLANE_DLANE_TIME, set_val);
-
- set_val = chs_clk_post_time
- | (chs_clk_pre_time << OFFSET_CHS_PRE_TIME)
- | (chs_pre_time << OFFSET_CHS_TRIAL_TIME)
- | (chs_trial_time << OFFSET_CHS_ZERO_TIME);
- top_sys_write32(priv, SCFG_RG_CLANE_HS_TIME, set_val);
-
-}
-
-static void reset_dphy(struct sf_dphy *priv, int resetb)
-{
- u32 cfg_dsc_enable = 0x01;//bit0
-
- u32 precfg = top_sys_read32(priv, SCFG_PHY_RESETB);
- precfg &= ~(cfg_dsc_enable);
- precfg |= (resetb&cfg_dsc_enable);
- top_sys_write32(priv, SCFG_PHY_RESETB, precfg);
-}
-
-static void polling_dphy_lock(struct sf_dphy *priv)
-{
- int pll_unlock;
-
- udelay(10);
-
- do {
- pll_unlock = top_sys_read32(priv, SCFG_GRS_CDTX_PLL) >> 3;
- pll_unlock &= 0x1;
- } while(pll_unlock == 0x1);
-}
-
-static int sf_dphy_configure(struct phy *phy, union phy_configure_opts *opts)
-{ //dev_info(dphy->dev,"--->sf_dphy_configure\n");
- struct sf_dphy *dphy = phy_get_drvdata(phy);
- uint32_t bit_rate = 800000000/1000000UL;//new mipi panel clock setting
- //uint32_t bit_rate = 500000000/1000000UL;//7110 mipi panel clock setting
-
-
- dphy_config(dphy, bit_rate);
- reset_dphy(dphy, 1);
- mdelay(10);
- polling_dphy_lock(dphy);
-
- //dev_info(dphy->dev,"--->sf_dphy_configure\n");
- return 0;
-}
-#endif
-
static int is_pll_locked(struct sf_dphy *dphy)
{
int tmp = sf_dphy_get_reg(dphy->topsys + 0x8,
@@ -581,11 +279,11 @@ static int sys_m31_dphy_tx_configure(struct phy *phy, union phy_configure_opts *
CFG_L0_SWAP_SEL_SHIFT, CFG_L0_SWAP_SEL_MASK);//Lane setting
sf_dphy_set_reg(dphy->topsys, 0x1,
CFG_L1_SWAP_SEL_SHIFT, CFG_L1_SWAP_SEL_MASK);
- sf_dphy_set_reg(dphy->topsys, 0x4,
- CFG_L2_SWAP_SEL_SHIFT, CFG_L2_SWAP_SEL_MASK);
sf_dphy_set_reg(dphy->topsys, 0x2,
- CFG_L3_SWAP_SEL_SHIFT, CFG_L3_SWAP_SEL_MASK);
+ CFG_L2_SWAP_SEL_SHIFT, CFG_L2_SWAP_SEL_MASK);
sf_dphy_set_reg(dphy->topsys, 0x3,
+ CFG_L3_SWAP_SEL_SHIFT, CFG_L3_SWAP_SEL_MASK);
+ sf_dphy_set_reg(dphy->topsys, 0x4,
CFG_L4_SWAP_SEL_SHIFT, CFG_L4_SWAP_SEL_MASK);
//PLL setting
sf_dphy_set_reg(dphy->topsys + 0x1c, 0x0,
@@ -659,6 +357,35 @@ static int sf_dphy_power_off(struct phy *phy)
static int sf_dphy_init(struct phy *phy)
{
+ struct sf_dphy *dphy = phy_get_drvdata(phy);
+ uint32_t temp;
+ int ret;
+
+ temp = 0;
+ temp = sf_dphy_get_reg(dphy->aonsys, AON_GP_REG_SHIFT, AON_GP_REG_MASK);
+ dev_info(dphy->dev, "GET_AON_GP_REG\n");
+
+ if (!(temp & DPHY_TX_PSW_EN_MASK)) {
+ temp |= DPHY_TX_PSW_EN_MASK;
+ sf_dphy_set_reg(dphy->aonsys, temp, AON_GP_REG_SHIFT, AON_GP_REG_MASK);
+ }
+ dev_info(dphy->dev, "control ECO\n");
+
+ //pmic turn on
+ ret = regulator_enable(dphy->mipitx_0p9);
+ if (ret) {
+ dev_err(dphy->dev, "Cannot enable mipitx_0p9 regulator\n");
+ //goto err_reg_0p9;
+ }
+ udelay(100);
+ ret = regulator_enable(dphy->mipitx_1p8);
+ if (ret) {
+ dev_err(dphy->dev, "Cannot enable mipitx_1p8 regulator\n");
+ //goto err_reg_1p8;
+ }
+ udelay(100);
+ //mipi_pmic setting
+
return 0;
}
@@ -705,7 +432,6 @@ static int sf_dphy_probe(struct platform_device *pdev)
struct sf_dphy *dphy;
struct resource *res;
int ret;
- uint32_t temp;
dev_info(&pdev->dev, "sf_dphy_probe begin\n");
dphy = devm_kzalloc(&pdev->dev, sizeof(*dphy), GFP_KERNEL);
@@ -732,39 +458,16 @@ static int sf_dphy_probe(struct platform_device *pdev)
// this power switch control bit was added in ECO, check ECO item "aon psw_en" for detail
dev_info(dphy->dev, "control ECO\n");
dphy->aonsys = ioremap(0x17010000, 0x10000);
- temp = 0;
- temp = sf_dphy_get_reg(dphy->aonsys, AON_GP_REG_SHIFT,AON_GP_REG_MASK);
- dev_info(dphy->dev, "GET_AON_GP_REG\n");
-
- if (!(temp & DPHY_TX_PSW_EN_MASK)) {
- temp |= DPHY_TX_PSW_EN_MASK;
- sf_dphy_set_reg(dphy->aonsys, temp,AON_GP_REG_SHIFT,AON_GP_REG_MASK);
- }
- dev_info(dphy->dev, "control ECO\n");
//mipi_pmic setting
- dphy->mipitx_1p8 = devm_regulator_get(&pdev->dev, "mipitx_1p8");
+ dphy->mipitx_1p8 = devm_regulator_get(&pdev->dev, "mipi_1p8");
if (IS_ERR(dphy->mipitx_1p8))
return PTR_ERR(dphy->mipitx_1p8);
- dphy->mipitx_0p9 = devm_regulator_get(&pdev->dev, "mipitx_0p9");
+ dphy->mipitx_0p9 = devm_regulator_get(&pdev->dev, "mipi_0p9");
if (IS_ERR(dphy->mipitx_0p9))
return PTR_ERR(dphy->mipitx_0p9);
- //pmic turn on
- ret = regulator_enable(dphy->mipitx_0p9);
- if (ret) {
- dev_err(&pdev->dev, "Cannot enable mipitx_0p9 regulator\n");
- //goto err_reg_0p9;
- }
- udelay(100);
- ret = regulator_enable(dphy->mipitx_1p8);
- if (ret) {
- dev_err(&pdev->dev, "Cannot enable mipitx_1p8 regulator\n");
- //goto err_reg_1p8;
- }
- udelay(100);
- //mipi_pmic setting
ret = sf_dphy_clkrst_get(&pdev->dev, dphy);
diff --git a/drivers/regulator/axp20x-regulator.c b/drivers/regulator/axp20x-regulator.c
index c657820b0bbb..810f90f3e2a1 100644
--- a/drivers/regulator/axp20x-regulator.c
+++ b/drivers/regulator/axp20x-regulator.c
@@ -20,6 +20,7 @@
#include <linux/mfd/axp20x.h>
#include <linux/module.h>
#include <linux/of.h>
+#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/regulator/driver.h>