From 7ef9e52f3a47ccbd7ab778d39afd5fac8e31ee66 Mon Sep 17 00:00:00 2001 From: Daryl Ronningen Date: Tue, 16 May 2023 02:13:19 -0700 Subject: [PATCH] Initial driver upload --- Makefile | 129 ++++ changelog.txt | 224 +++++++ fmac.sh | 83 +++ hgic.h | 255 ++++++++ hgic_def.h | 386 ++++++++++++ hgic_fmac/Makefile | 23 + hgic_fmac/cfg.c | 1178 +++++++++++++++++++++++++++++++++++ hgic_fmac/cfg.h | 13 + hgic_fmac/core.c | 993 ++++++++++++++++++++++++++++++ hgic_fmac/ctrl.c | 70 +++ hgic_fmac/ctrl.h | 11 + hgic_fmac/event.c | 196 ++++++ hgic_fmac/event.h | 9 + hgic_fmac/hgicf.h | 156 +++++ hgic_fmac/procfs.c | 1204 ++++++++++++++++++++++++++++++++++++ hgic_fmac/procfs.h | 71 +++ hgicf.conf | 8 + hgota/fwinfo.c | 343 +++++++++++ hgota/fwinfo.h | 111 ++++ hgota/hgota.c | 349 +++++++++++ hgota/libota.c | 251 ++++++++ hgota/libota.h | 156 +++++ hgtest | 2 + iwpriv | 2 + readme.txt | 34 ++ utils/Makefile | 5 + utils/ah_freqinfo.c | 218 +++++++ utils/fwctrl.c | 1159 +++++++++++++++++++++++++++++++++++ utils/fwctrl.h | 196 ++++++ utils/fwdl.c | 438 +++++++++++++ utils/fwdl.h | 91 +++ utils/fwinfo.c | 322 ++++++++++ utils/fwinfo.h | 98 +++ utils/hgic_fmac.c | 1141 ++++++++++++++++++++++++++++++++++ utils/if_sdio.c | 899 +++++++++++++++++++++++++++ utils/if_usb.c | 378 ++++++++++++ utils/mmc_spi.c | 1428 +++++++++++++++++++++++++++++++++++++++++++ utils/ota.c | 309 ++++++++++ utils/ota.h | 29 + utils/utils.c | 269 ++++++++ utils/utils.h | 180 ++++++ version.h | 1 + 42 files changed, 13418 insertions(+) create mode 100644 Makefile create mode 100644 changelog.txt create mode 100755 fmac.sh create mode 100644 hgic.h create mode 100644 hgic_def.h create mode 100644 hgic_fmac/Makefile create mode 100644 hgic_fmac/cfg.c create mode 100644 hgic_fmac/cfg.h create mode 100644 hgic_fmac/core.c create mode 100644 hgic_fmac/ctrl.c create mode 100644 hgic_fmac/ctrl.h create mode 100644 hgic_fmac/event.c create mode 100644 hgic_fmac/event.h create mode 100644 hgic_fmac/hgicf.h create mode 100644 hgic_fmac/procfs.c create mode 100644 hgic_fmac/procfs.h create mode 100644 hgicf.conf create mode 100644 hgota/fwinfo.c create mode 100644 hgota/fwinfo.h create mode 100644 hgota/hgota.c create mode 100644 hgota/libota.c create mode 100644 hgota/libota.h create mode 100755 hgtest create mode 100755 iwpriv create mode 100644 readme.txt create mode 100644 utils/Makefile create mode 100644 utils/ah_freqinfo.c create mode 100644 utils/fwctrl.c create mode 100644 utils/fwctrl.h create mode 100644 utils/fwdl.c create mode 100644 utils/fwdl.h create mode 100644 utils/fwinfo.c create mode 100644 utils/fwinfo.h create mode 100644 utils/hgic_fmac.c create mode 100644 utils/if_sdio.c create mode 100644 utils/if_usb.c create mode 100644 utils/mmc_spi.c create mode 100644 utils/ota.c create mode 100644 utils/ota.h create mode 100644 utils/utils.c create mode 100644 utils/utils.h create mode 100644 version.h diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..39dc0b2 --- /dev/null +++ b/Makefile @@ -0,0 +1,129 @@ +CURRENT_PATH := $(shell pwd) + +#Hi3516 +#ARCH := arm +#COMPILER := arm-himix100-linux- +#LINUX_KERNEL_PATH := /home/matt/Hi3516/Hi3516EV200_SDK_V1.0.0.1/osdrv/opensource/kernel/linux-4.9.y + +#Hi3518 +#ARCH := arm +#COMPILER := arm-hisiv300-linux-uclibcgnueabi- +#LINUX_KERNEL_PATH := /home/matt/Hi3518/hi3518/linux-3.4.y + +#FH8852 +#ARCH := arm +#COMPILER := arm-fullhan-linux-uclibcgnueabi- +#LINUX_KERNEL_PATH := /home/matt/FH8852/FH8856_IPC_V1.1.0_20190125/board_support/kernel/linux-3.0.8 + +#Hi3536d +#ARCH := arm +#COMPILER := arm-hisiv510-linux-uclibcgnueabi- +#LINUX_KERNEL_PATH := /home/matt/Hi3536d/Hi3536DV100_SDK_V1.0.2.0/osdrv/opensource/kernel/linux-4.9.y + +#OpenWRT MT7628 +#ARCH := mips +#COMPILER := /home/matt/openwrt/openwrt/staging_dir/toolchain-mipsel_24kc_gcc-7.3.0_musl/bin/mipsel-openwrt-linux- +#LINUX_KERNEL_PATH := /home/matt/openwrt/openwrt/build_dir/target-mipsel_24kc_musl/linux-ramips_mt76x8/linux-4.14.151 +#export STAGING_DIR = $(COMPILER) + +#MTK SDK +ARCH := mips +COMPILER := /opt/buildroot-gcc463/usr/bin/mipsel-linux- +LINUX_KERNEL_PATH := /home/dongyun/work/disk4/AH6001/RT288x_AHv1.2/source/linux-3.10.14.x + +#Raspberry Pi +#ARCH := arm +#LINUX_KERNEL := $(shell uname -r) +#LINUX_KERNEL_PATH := /usr/src/linux-headers-$(LINUX_KERNEL) + + +CFLAGS += -DCONFIG_HGIC_AH +#CFLAGS += -DCONFIG_HGIC_2G + +export CONFIG_HGIC_AH = y +#export CONFIG_HGIC_2G = y + +help: + @echo "-------------------------------------------------------------------------------------------------------------------" + @echo "usage:" + @echo " make smac : compile SMAC driver. generate 3 ko files : hgics.ko, hgic_sdio.ko hgic_usb.ko." + @echo " make smac_usb : compile SMAC driver. generate 1 ko file : hgics_usb.ko (combined hgics.ko and hgic_usb.ko)." + @echo " make smac_sdio: compile SMAC driver. generate 1 ko file : hgics_sdio.ko (combined hgics.ko and hgic_sdio.ko)." + @echo "" + @echo " make fmac : compile FMAC driver. generate 3 ko files : hgicf.ko, hgic_sdio.ko hgic_usb.ko." + @echo " make fmac_usb : compile FMAC driver. generate 1 ko file : hgicf_usb.ko (combined hgicf.ko and hgic_usb.ko)." + @echo " make fmac_sdio: compile FMAC driver. generate 1 ko file : hgicf_sdio.ko (combined hgicf.ko and hgic_sdio.ko)." + @echo "" + @echo " make clean" + @echo "-------------------------------------------------------------------------------------------------------------------" + +smac: prepare _smac _driver_smac + +fmac: prepare _fmac _driver_fmac + +smac_usb: prepare _smac_usb + +smac_sdio: prepare _smac_sdio + +fmac_usb: prepare _fmac_usb + +fmac_sdio: prepare _fmac_sdio + +prepare: + mkdir -p ko + +_fmac: + $(MAKE) -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH)/hgic_fmac ARCH=$(ARCH) CROSS_COMPILE=$(COMPILER) CONFIG_HGICF=hgicf EXTRA_CFLAGS=$(CFLAGS) modules + cp -f hgic_fmac/hgicf.ko ko/hgicf.ko + $(COMPILER)strip -g ko/hgicf.ko + +_smac: + $(MAKE) -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH)/hgic_smac ARCH=$(ARCH) CROSS_COMPILE=$(COMPILER) CONFIG_HGICS=hgics EXTRA_CFLAGS=$(CFLAGS) modules + cp -f hgic_smac/hgics.ko ko/hgics.ko + $(COMPILER)strip -g ko/hgics.ko + +_fmac_sdio: + $(MAKE) -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH)/hgic_fmac ARCH=$(ARCH) CROSS_COMPILE=$(COMPILER) CONFIG_HGICF=hgicf_sdio EXTRA_CFLAGS="$(CFLAGS) -DCONFIG_HGIC_SDIOIN" modules + cp -f hgic_fmac/hgicf_sdio.ko ko/hgicf_sdio.ko + $(COMPILER)strip -g ko/hgicf_sdio.ko + +_fmac_usb: + $(MAKE) -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH)/hgic_fmac ARCH=$(ARCH) CROSS_COMPILE=$(COMPILER) CONFIG_HGICF=hgicf_usb EXTRA_CFLAGS="$(CFLAGS) -DCONFIG_HGIC_USBIN" modules + cp -f hgic_fmac/hgicf_usb.ko ko/hgicf_usb.ko + $(COMPILER)strip -g ko/hgicf_usb.ko + +_smac_sdio: + $(MAKE) -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH)/hgic_smac ARCH=$(ARCH) CROSS_COMPILE=$(COMPILER) CONFIG_HGICS=hgics_sdio EXTRA_CFLAGS="$(CFLAGS) -DCONFIG_HGIC_SDIOIN" modules + cp -f hgic_smac/hgics_sdio.ko ko/hgics_sdio.ko + $(COMPILER)strip -g ko/hgics_sdio.ko + +_smac_usb: + $(MAKE) -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH)/hgic_smac ARCH=$(ARCH) CROSS_COMPILE=$(COMPILER) CONFIG_HGICS=hgics_usb EXTRA_CFLAGS="$(CFLAGS) -DCONFIG_HGIC_USBIN" modules + cp -f hgic_smac/hgics_usb.ko ko/hgics_usb.ko + $(COMPILER)strip -g ko/hgics_usb.ko + +_driver_fmac: + cp -f hgic_fmac/Module.symvers utils/Module.symvers + $(MAKE) -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH)/utils ARCH=$(ARCH) CROSS_COMPILE=$(COMPILER) EXTRA_CFLAGS=$(CFLAGS) modules + cp -f utils/hgic_usb.ko ko/hgic_usb.ko + cp -f utils/hgic_sdio.ko ko/hgic_sdio.ko + $(COMPILER)strip -g ko/hgic_usb.ko + $(COMPILER)strip -g ko/hgic_sdio.ko + +_driver_smac: + cp -f hgic_smac/Module.symvers utils/Module.symvers + $(MAKE) -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH)/utils ARCH=$(ARCH) CROSS_COMPILE=$(COMPILER) EXTRA_CFLAGS=$(CFLAGS) modules + cp -f utils/hgic_usb.ko ko/hgic_usb.ko + cp -f utils/hgic_sdio.ko ko/hgic_sdio.ko + $(COMPILER)strip -g ko/hgic_usb.ko + $(COMPILER)strip -g ko/hgic_sdio.ko + +clean: + rm -fr Module.symvers ; rm -fr Module.markers ; rm -fr modules.order + cd hgic_fmac ; rm -fr *.mod.c *.mod *.o .*.cmd *.ko Module.symvers modules.order + cd hgic_smac ; rm -fr *.mod.c *.mod *.o .*.cmd *.ko Module.symvers modules.order + cd hgic_smac/umac ; rm -fr *.mod.c *.mod *.o .*.cmd *.ko + cd hgic_smac/umac/cfg80211 ; rm -fr *.mod.c *.mod *.o .*.cmd *.ko + cd hgic_smac/umac/mac80211 ; rm -fr *.mod.c *.mod *.o .*.cmd *.ko + cd utils ; rm -fr *.mod.c *.mod *.o .*.cmd *.ko Module.symvers modules.order + rm -rf ko diff --git a/changelog.txt b/changelog.txt new file mode 100644 index 0000000..c4b9248 --- /dev/null +++ b/changelog.txt @@ -0,0 +1,224 @@ +2022/01/12 + 修改bug:sdio remove过程中,执行hgic_core_remove时可能会rx data,此时会出现死机现象。 + +----------------------------------------------------- +2022/01/10 + 固件下载功能优化:在固件下载阶段,屏蔽其它数据和cmd的发送, 避免干扰固件下载过程。 + +----------------------------------------------------- +2021/12/22 + fmac: 新增event:HGIC_EVENT_UNPAIR_STA, 解除配对时,固件产生此event。 + 新增:tx maxcnt 命令,用于设置最大帧重传次数。 + +----------------------------------------------------- +2021/11/19 + fmac: 添加AP隐藏功能, iwpriv hg0 set ap_hide 1 + +----------------------------------------------------- +2021/11/08 + 解决驱动卸载死机问题。 + +----------------------------------------------------- +2021/11/01 + fmac: 添加漫游功能设置接口。 + +----------------------------------------------------- +2021/09/28 + smac: + 1. 修改bug: remove 时 先 确保 alive_timer/alive_work/delay_init 已经停止运行 + 2. hgic_frm_hdr.tx_info : 清零 + 3. ETH_P_PAE 类型数据使用最高优先级 + fmac: + 1. 修改bug: remove 时 先 确保 alive_timer/alive_work/delay_init 已经停止运行 + +----------------------------------------------------- +2021/09/27 + 添加 macfilter/atcmd 命令接口。 + +----------------------------------------------------- +2021/09/23 + 1. 优化固件下载功能,减少对 memory 的需求。 + 2. 模块监测功能修改:接口只要支持reinit,就默认开启该功能,用于监测是否需要重新下载固件。 + +----------------------------------------------------- +2021/09/13 + get module_type 修改,定义了 struct hgic_module_hwinfo 结构体。 + 使用方法参考: hgic_fmac.c + +----------------------------------------------------- +2021/08/26 + 添加 HGIC_HDR_TYPE_CMD2 和 HGIC_HDR_TYPE_EVENT2,扩展 cmd id 和 event id 到 uint16. + 添加 reset sta 接口:用于复位remote sta。 + 添加 set freqinfo接口:用于设置remote sta的频点信息。 + +----------------------------------------------------- +2021/07/05 + fmac: 添加 proc pairing_sta接口,读取当前配对sta的mac地址 + 添加 hgic_fmac.c 驱动接口封装API。 + +----------------------------------------------------- +2021/06/17 + fmac: 添加 iwpriv acktmo 命令。 + +----------------------------------------------------- +2021/06/10 + fmac: 添加 iwpriv dcdc13 命令。 + +----------------------------------------------------- +2021/05/28 + fmac: 添加 /proc/hgic/battery_soc 接口。 + +----------------------------------------------------- +2021/05/26 + fix bug: 使用驱动包iwpriv脚本执行get命令时驱动崩溃。 + +----------------------------------------------------- +2021/05/06 + fix bug: AH模块卸载时,wifi driver 死机问题。 + +----------------------------------------------------- +2021/04/16 + fix bug: fmac driver,alive_work 设置 HGICF_DEV_FLAGS_SUSPEND 后,在 delay_init 中需要清除该标识。 + +----------------------------------------------------- +2021/03/23 + fmac: 添加 /proc/hgic/evm 接口。 + +----------------------------------------------------- +2021/03/10 + fmac: 添加 iwpriv get key_mgmt 接口,获取加密方式。 + +----------------------------------------------------- +2021/02/25 + fmac: 添加 iwpriv autosleep_time 接口,设置auto sleep时间。 + +----------------------------------------------------- +2021/02/25 + if_sdio: 支持 NON_POLL 模式下载固件和模块复位检测。 + +----------------------------------------------------- +2021/02/25 + hgic_sdio: 支持 sdio spi boot + +----------------------------------------------------- +2021/02/18 + hgic_sdio: 支持 sdio spi模式。 + +----------------------------------------------------- +2021/02/18 + fmac: 添加 iwpriv sysdbg接口,设置调试信息的开关。 + +----------------------------------------------------- +2021/02/04 + fmac: 添加 iwpriv dbginfo接口,调试信息重定向。 + +----------------------------------------------------- +2021/02/04 + fmac: 添加 iwpriv wakeup_io接口,自定义唤醒IO。 + fmac: 添加 procfs event接口,用于查询固件产生的event。 + +----------------------------------------------------- +2021/02/02 + fmac: + iwpriv hg0 set loaddef=0 恢复出厂参数后不重启 + iwpriv hg0 set loaddef=1 恢复出厂参数后重启 + fmac: 添加 iwpriv reassoc_wkhost 设置断线重连后唤醒主控。 + +----------------------------------------------------- +2020/12/31 + fmac: 添加 iwpriv mcast_key 设置组播数据加密key. + + +----------------------------------------------------- +2020/12/31 + fix bug: 解析mac字符串出错,造成wakesta 地址不对。 + +----------------------------------------------------- +2020/12/21 + fmac: 添加 iwpriv unpair 命令,解除配对信息。 + + +----------------------------------------------------- +2020/12/17 + fmac: 驱动参数默认值修改。 + 添加 /proc/hgic/wkreason 接口,查询WiFi模块唤醒原因。 + +----------------------------------------------------- +2020/12/15 + fmac: 解决驱动remove时出现的死机问题。 + 添加 iwpriv aplost_time 参数,设置sta检测ap lost的超时时间,默认90秒。 + +----------------------------------------------------- +2020/12/14 + fmac: fw_ver procfs接口,获取WiFi模块固件版本信息。 + +----------------------------------------------------- +2020/12/14 + fmac: 增加ps_mode参数接口。 + +----------------------------------------------------- +2020/12/11 + fmac: iwpriv添加命令接口: + dtim_period:设置DTIM周期 + disassoc_sta:AP端主动断开指定的sta。 + loaddef:恢复出厂参数。 + +----------------------------------------------------- +2020/12/07 + 添加 wkio_mode 设置接口, 设置wakeup IO的触发模式。 + +----------------------------------------------------- +2020/12/02 + fmac: 添加 /proc/hgic/pair_state接口,用于查看配对状态:1-配对成功,0-配对未成功。 + +----------------------------------------------------- +2020/11/28 + 添加 max_idle_period 设置接口。 + +----------------------------------------------------- +2020/11/18 + fmac:wifi模块休眠后,Host端wifi驱动不再发送数据。 + +----------------------------------------------------- +2020/11/11 + 新增功能: for 电池摄像机,添加建立连接的省电模式。在AP异常时,避免频繁重连耗尽电池。 + +----------------------------------------------------- +2020/11/07 + 新增功能: 支持客户自定义低功耗模式下的心跳包和唤醒包。 + +----------------------------------------------------- +2020/10/14 + 解决驱动处理request param消息存在内存泄露的问题。 + +----------------------------------------------------- +2020/09/30 + 部分参数添加互斥机制。 + +----------------------------------------------------- +2020/09/29 + fmac: 添加 get bss_bw/freq_range/chan_list/agg_cnt 接口。 + +----------------------------------------------------- +2020/09/27 + fmac: 添加 /etc/hgicf.conf 文件,驱动自动加载参数。 + +----------------------------------------------------- +2020/09/17 + 1. 添加了驱动重新下载参数到固件;wifi模块重启后参数丢失,可以通知host重新加载参数。 + +----------------------------------------------------- +2020/09/02 + 1. smac 添加 /proc/hgic/recfg 接口,支持重新加载驱动参数。 + 2. smac 添加 /proc/hgic/radio_off 接口,用于控制 radio on/off. + 3. fmac 添加 iwpriv radio_off 命令,用于控制radio on/off + 4. fmac 添加 JOIN_GROUP/SET_ETHERTYPE 命令。 + 5. fmac 添加 proc/hgic/sta_count 接口。 + 6. fix 空指针异常bug. + 7. Update : AX218 usb bootdl max packet length is 2048,so reduce usb boot packet length + 8. fix usb 固件下载运行的bug。 + + +----------------------------------------------------- +2020/08/04 + fmac: 添加 iwpriv get ssid/wpapsk 命令. diff --git a/fmac.sh b/fmac.sh new file mode 100755 index 0000000..6f1e18e --- /dev/null +++ b/fmac.sh @@ -0,0 +1,83 @@ +#! /bin/sh + +#interface name +IFNAME="hg0" + +# ko file path +FMAC_KO_PATH="/lib/modules/3.10.14/kernel/drivers/net/wireless/hugeic/hgic_fmac/hgicf.ko" +USB_KO_PATH="/lib/modules/3.10.14/kernel/drivers/net/wireless/hugeic/utils/hgic_usb.ko" +SDIO_KO_PATH="/lib/modules/3.10.14/kernel/drivers/net/wireless/hugeic/utils/hgic_sdio.ko" + +#read paramters from system. +AH_MODE=ap +AH_SSID=ah_test_ssid +AH_PSK=00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff +AH_KEY_MGMT=NONE +AH_FREQ_START=7600 +AH_FREQ_END=7680 +AH_BSS_BW=8 +AH_TX_MCS=255 +AH_CHAN_LIST= + +#set default values +[ -z "$AH_MODE" ] && AH_MODE="ap" +[ -z "$AH_FREQ_START" ] && AH_FREQ_START="7800" +[ -z "$AH_FREQ_END" ] && AH_FREQ_END="8000" +[ -z "$AH_BSS_BW" ] && AH_BSS_BW="8" +[ -z "$AH_TX_MCS" ] && AH_TX_MCS="7" +[ -z "$AH_CHANNEL" ] && AH_CHANNEL="1" +[ -z "$AH_ACS" ] && AH_ACS="0" +[ -z "$AH_ACS_TM" ] && AH_ACS_TM="10" +[ -z "$AH_TX_POWER" ] && AH_TX_POWER="1" +[ "$AH_MODE" == "sta" ] && AH_ACS="0" +[ "$AH_ACS" == "1" ] && AH_CHANNEL="1" + +#insmod driver +ko_exist=$(lsmod|grep hgicf) +if [ -z "$ko_exist" ]; then +[ -n "$FMAC_KO_PATH" ] && insmod $FMAC_KO_PATH $ARG_IF_TEST +[ -n "$SDIO_KO_PATH" ] && insmod $SDIO_KO_PATH +[ -n "$USB_KO_PATH" ] && insmod $USB_KO_PATH +sleep 1 +fi + +#set param +CONN_STATE=$(cat /proc/hgic/conn_state|grep CONNECTED) +if [ -z "$CONN_STATE" ]; then + ifconfig $IFNAME down + iwpriv $IFNAME set freq_range=$AH_FREQ_START,$AH_FREQ_END,$AH_BSS_BW + iwpriv $IFNAME set mode=$AH_MODE + iwpriv $IFNAME set bss_bw=$AH_BSS_BW + iwpriv $IFNAME set tx_mcs=$AH_TX_MCS + iwpriv $IFNAME set tx_power=$AH_TX_POWER + iwpriv $IFNAME set channel=$AH_CHANNEL + iwpriv $IFNAME set acs="$AH_ACS,$AH_ACS_TM" + if [ x"$AH_KEY_MGMT" == "xWPA-PSK" ]; then + iwpriv $IFNAME set key_mgmt=WPA-PSK + iwpriv $IFNAME set wpa_psk=$AH_PSK + else + iwpriv $IFNAME set key_mgmt=NONE + fi + iwpriv $IFNAME set ssid=$AH_SSID + #save config + iwpriv $IFNAME save + [ "$AH_BSS_BW" == "1" ] && ifconfig $IFNAME mtu 380 +fi + +#up interface +ifconfig $IFNAME up + + +############################################################ +# 可以设置驱动参数文件,由驱动自动加载参数,需要如下3个步骤: +# 1. 生成参数文件:/etc/hgicf.conf,内容如下: +# freq_range=9000,9240,8 +# mode=ap +# ssid=ah_test_ssid +# key_mgmt=WPA-PSK +# wpa_psk=00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff +# [以上参数是最基本的参数设置,其他参数设置请参考 Linux开发指南] +# +# 2. insmod 驱动 +# 3. ifconfig up 接口 +########################################################### diff --git a/hgic.h b/hgic.h new file mode 100644 index 0000000..f963318 --- /dev/null +++ b/hgic.h @@ -0,0 +1,255 @@ +#ifndef _HUGE_IC_H_ +#define _HUGE_IC_H_ +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __RTOS__ +#define HGIC_CMD_START 100 +#else +#define HGIC_CMD_START 0 +#endif + +typedef void (*hgic_init_cb)(void *args); +typedef void (*hgic_event_cb)(char *ifname, int event, int param1, int param2); + +struct hgic_bss_info { + unsigned char bssid[6]; + unsigned char ssid[32]; + unsigned char encrypt; + char signal; + unsigned short freq; +}; + +struct hgic_fw_info { + unsigned int version; + unsigned int svn_version; + unsigned short chip_id; + unsigned short cpu_id; + unsigned char mac[6]; +}; + +struct hgic_sta_info { + unsigned char aid; + unsigned char ps:1; + unsigned char addr[6]; + char rssi; + char evm; +}; + +struct hgic_freqinfo { + unsigned char bss_bw, chan_cnt; + unsigned short freq_start, freq_end; + unsigned short chan_list[16]; +}; + +struct hgic_module_hwinfo{ + union{ + struct{ + unsigned char type; + unsigned char saw:1, rev:7; + }; + unsigned short v; + }; +}; + +enum hgic_cmd { + HGIC_CMD_DEV_OPEN = HGIC_CMD_START + 0x01, // 1 + HGIC_CMD_DEV_CLOSE, // 2 + HGIC_CMD_SET_MAC, // 3 + HGIC_CMD_SET_SSID, // 4 + HGIC_CMD_SET_BSSID, // 5 + HGIC_CMD_SET_COUNTERY, // 6 + HGIC_CMD_SET_CHANNEL, // 7 + HGIC_CMD_SET_CENTER_FREQ, // 8 + HGIC_CMD_SET_RTS_THRESHOLD, // 9 + HGIC_CMD_SET_FRG_THRESHOLD, // 10 + HGIC_CMD_SET_KEY_MGMT, // 11 + HGIC_CMD_SET_WPA_PSK, // 12 + HGIC_CMD_SET_KEY, // 13 + HGIC_CMD_SCAN, // 14 + HGIC_CMD_GET_SCAN_LIST, // 15 + HGIC_CMD_SET_BSSID_FILTER, // 16 + HGIC_CMD_DISCONNECT, // 17 + HGIC_CMD_GET_BSSID, // 18 + HGIC_CMD_SET_WBNAT, // 19 + HGIC_CMD_GET_STATUS, // 20 + HGIC_CMD_SET_LISTEN_INTERVAL, // 21 + HGIC_CMD_SET_TX_POWER, // 22 + HGIC_CMD_GET_TX_POWER, // 23 + HGIC_CMD_SET_TX_LCOUNT, // 24 + HGIC_CMD_SET_TX_SCOUNT, // 25 + HGIC_CMD_ADD_STA, // 26 + HGIC_CMD_REMOVE_STA, // 27 + HGIC_CMD_SET_TX_BW, // 28 + HGIC_CMD_SET_TX_MCS, // 29 + HGIC_CMD_SET_FREQ_RANGE, // 30 + HGIC_CMD_ACS_ENABLE, // 31 + HGIC_CMD_SET_PRIMARY_CHAN, // 32 + HGIC_CMD_SET_BG_RSSI, // 33 + HGIC_CMD_SET_BSS_BW, // 34 + HGIC_CMD_TESTMODE_CMD, // 35 + HGIC_CMD_SET_AID, // 36 + HGIC_CMD_GET_FW_STATE, // 37 + HGIC_CMD_SET_TXQ_PARAM, // 38 + HGIC_CMD_SET_CHAN_LIST, // 39 + HGIC_CMD_GET_CONN_STATE, // 40 + HGIC_CMD_SET_WORK_MODE, // 41 + HGIC_CMD_SET_PAIRED_STATIONS, // 42 + HGIC_CMD_GET_FW_INFO, // 43 + HGIC_CMD_PAIRING, // 44 + HGIC_CMD_GET_TEMPERATURE, // 45 + HGIC_CMD_ENTER_SLEEP, // 46 + HGIC_CMD_OTA, // 47 + HGIC_CMD_GET_SSID, // 48 + HGIC_CMD_GET_WPA_PSK, // 49 + HGIC_CMD_GET_SIGNAL, // 50 + HGIC_CMD_GET_TX_BITRATE, // 51 + HGIC_CMD_SET_BEACON_INT, // 52 + HGIC_CMD_GET_STA_LIST, // 53 + HGIC_CMD_SAVE_CFG, // 54 + HGIC_CMD_JOIN_GROUP, // 55 + HGIC_CMD_SET_ETHER_TYPE, // 56 + HGIC_CMD_GET_STA_COUNT, // 57 + HGIC_CMD_SET_HEARTBEAT_INT, // 58 + HGIC_CMD_SET_MCAST_KEY, // 59 + HGIC_CMD_SET_AGG_CNT, // 60 + HGIC_CMD_GET_AGG_CNT, // 61 + HGIC_CMD_GET_BSS_BW , // 62 + HGIC_CMD_GET_FREQ_RANGE, // 63 + HGIC_CMD_GET_CHAN_LIST, // 64 + HGIC_CMD_RADIO_ONOFF, // 65 + HGIC_CMD_SET_PS_HEARTBEAT, // 66 + HGIC_CMD_SET_WAKEUP_STA, // 67 + HGIC_CMD_SET_PS_HEARTBEAT_RESP, // 68 + HGIC_CMD_SET_PS_WAKEUP_DATA, // 69 + HGIC_CMD_SET_PS_CONNECT, // 70 + HGIC_CMD_SET_BSS_MAX_IDLE, // 71 + HGIC_CMD_SET_WKIO_MODE, // 72 + HGIC_CMD_SET_DTIM_PERIOD, // 73 + HGIC_CMD_SET_PS_MODE, // 74 + HGIC_CMD_LOAD_DEF, // 75 + HGIC_CMD_DISASSOC_STA, // 76 + HGIC_CMD_SET_APLOST_TIME, // 77 + HGIC_CMD_GET_WAKEUP_REASON, // 78 + HGIC_CMD_UNPAIR, // 79 + HGIC_CMD_SET_AUTO_CHAN_SWITCH, // 80 + HGIC_CMD_SET_REASSOC_WKHOST, // 81 + HGIC_CMD_SET_WAKEUP_IO, // 82 + HGIC_CMD_DBGINFO_OUTPUT, // 83 + HGIC_CMD_SET_SYSDBG, // 84 + HGIC_CMD_SET_AUTO_SLEEP_TIME, // 85 + HGIC_CMD_GET_KEY_MGMT, // 86 + HGIC_CMD_SET_PAIR_AUTOSTOP, // 87 + HGIC_CMD_SET_SUPPER_PWR, // 88 + HGIC_CMD_SET_REPEATER_SSID, // 89 + HGIC_CMD_SET_REPEATER_PSK, // 90 + HGIC_CMD_CFG_AUTO_SAVE, // 91 + HGIC_CMD_SEND_CUST_MGMT, // 92 + HGIC_CMD_GET_BATTERY_LEVEL, // 93 + HGIC_CMD_SET_DCDC13, // 94 + HGIC_CMD_SET_ACKTMO, // 95 + HGIC_CMD_GET_MODULETYPE, // 96 + HGIC_CMD_PA_PWRCTRL_DIS, // 97 + HGIC_CMD_SET_DHCPC, // 98 + HGIC_CMD_GET_DHCPC_RESULT, // 99 + HGIC_CMD_SET_WKUPDATA_MASK, // 100 + HGIC_CMD_GET_WKDATA_BUFF, // 101 + HGIC_CMD_GET_DISASSOC_REASON, // 102 + HGIC_CMD_SET_WKUPDATA_SAVEEN, // 103 + HGIC_CMD_SET_CUST_DRIVER_DATA, // 104 + HGIC_CMD_SET_MCAST_TXPARAM, // 105 + HGIC_CMD_SET_STA_FREQINFO, // 106 + HGIC_CMD_SET_RESET_STA, // 107 + HGIC_CMD_SET_UART_FIXLEN, // 108 + HGIC_CMD_GET_UART_FIXLEN, // 109 + HGIC_CMD_SET_ANT_AUTO, // 110 + HGIC_CMD_SET_ANT_SEL, // 111 + HGIC_CMD_GET_ANT_SEL, // 112 + HGIC_CMD_SET_WKUP_HOST_REASON, // 113 + HGIC_CMD_SET_MAC_FILTER_EN, // 114 + HGIC_CMD_SET_ATCMD, // 115 + HGIC_CMD_SET_ROAMING, // 116 + HGIC_CMD_SET_AP_HIDE, // 117 + HGIC_CMD_SET_DUAL_ANT, // 118 + HGIC_CMD_SET_MAX_TCNT, // 119 + HGIC_CMD_SET_ASSERT_HOLDUP, // 120 + HGIC_CMD_SET_AP_PSMODE_EN, // 121 + HGIC_CMD_SET_DUPFILTER_EN, // 122 + HGIC_CMD_SET_DIS_1V1_M2U, // 123 +}; + +enum hgic_event { + HGIC_EVENT_STATE_CHG = 0x1, // 1 + HGIC_EVENT_CH_SWICH, // 2 + HGIC_EVENT_DISCONNECT_REASON, // 3 + HGIC_EVENT_ASSOC_STATUS, // 4 + HGIC_EVENT_SCANNING, // 5 + HGIC_EVENT_SCAN_DONE, // 6 + HGIC_EVENT_TX_BITRATE, // 7 + HGIC_EVENT_PAIR_START, // 8 + HGIC_EVENT_PAIR_SUCCESS, // 9 + HGIC_EVENT_PAIR_DONE, // 10 + HGIC_EVENT_CONECT_START, // 11 + HGIC_EVENT_CONECTED, // 12 + HGIC_EVENT_DISCONECTED, // 13 + HGIC_EVENT_SIGNAL, // 14 + HGIC_EVENT_DISCONNET_LOG, // 15 + HGIC_EVENT_REQUEST_PARAM, // 16 + HGIC_EVENT_TESTMODE_STATE, // 17 + HGIC_EVENT_FWDBG_INFO, // 18 + HGIC_EVENT_CUSTOMER_MGMT, // 19 + HGIC_EVENT_SLEEP_FAIL, // 20 + HGIC_EVENT_DHCPC_DONE, // 21 + HGIC_EVENT_CONNECT_FAIL, // 22 + HGIC_EVENT_CUST_DRIVER_DATA, // 23 + HGIC_EVENT_UNPAIR_STA, // 24 +}; + +extern int hgic_sdio_init(void); +extern void hgic_sdio_exit(void); +extern int hgic_usb_init(void); +extern void hgic_usb_exit(void); + +#ifdef __RTOS__ +struct firmware { + unsigned char *data; + unsigned int size; +}; +int request_firmware(const struct firmware **fw, const char *name, void *dev); +void release_firmware(struct firmware *fw); + +extern int hgicf_init(void); +extern int hgicf_cmd(char *ifname, unsigned int cmd, unsigned int param1, unsigned int param2); +extern int hgics_init(void); +extern void hgics_exit(void); +extern int wpas_init(void); +extern int wpas_start(char *ifname); +extern int wpas_stop(char *ifname); +extern int wpas_cli(char *ifname, char *cmd, char *reply_buff, int reply_len); +extern int wpas_passphrase(char *ssid, char *passphrase, char psk[32]); +extern int hapd_init(void); +extern int hapd_start(char *ifname); +extern int hapd_stop(char *ifname); +extern int hapd_cli(char *ifname, char *cmd, char *reply_buff, int reply_len); +extern void hgic_param_iftest(int iftest); +extern const char *hgic_param_ifname(const char *name); +extern char *hgic_param_fwfile(const char *fw); +extern int hgic_param_ifcount(int count); +extern void hgic_param_initcb(hgic_init_cb cb); +extern void hgic_param_eventcb(hgic_event_cb cb); +extern int hgic_ota_start(char *ifname, char *fw_name); + +void hgic_raw_init(void); +int hgic_raw_send(char *dest, char *data, int len); +int hgic_raw_rcev(char *buf, int size, char *src); + +#ifdef HGIC_SMAC +#include "umac_config.h" +#endif +#endif + +#ifdef __cplusplus +} +#endif +#endif diff --git a/hgic_def.h b/hgic_def.h new file mode 100644 index 0000000..d432ecc --- /dev/null +++ b/hgic_def.h @@ -0,0 +1,386 @@ +#ifndef _HUGE_IC_DEF_H_ +#define _HUGE_IC_DEF_H_ +#include "hgic.h" +#include "version.h" + +#define HGIC_VERSION "v1.3.0" + +#ifndef SVN_VERSION +#error "SVN_VERSION undefined" +#endif + +#define VERSOIN_SHOW(name) do{\ + printk("** HUGE-IC WLAN Card Driver("name") "HGIC_VERSION"-"SVN_VERSION"\r\n");\ + }while(0) + +#define HGIC_WDEV_ID_AP 0 +#define HGIC_WDEV_ID_STA 1 +#define HGIC_SCAN_MAX_NUM 32 + +#define HGIC_HDR_TX_MAGIC 0x1A2B +#define HGIC_HDR_RX_MAGIC 0x2B1A + +#define HGIC_VENDOR_ID (0xA012) +#define HGIC_WLAN_AH_4001 (0x4001) +#define HGIC_WLAN_AH_4002 (0x4002) +#define HGIC_WLAN_AH_4102 (0x4102) + +#define HGIC_CTRL_TIMEOUT 100 +#define HGIC_CMD_PRIORITY 0 +#define HGIC_TX_WINDOW 20 +#define HGIC_TX_COOKIE_MASK 0x7FFF +#define HGIC_BLOCK_ACK_CNT 256 + +#define hgic_dbg(fmt, ...) printk("%s:%d::"fmt, __FUNCTION__, __LINE__, ##__VA_ARGS__) +#define hgic_err(fmt, ...) printk("%s:%d::"fmt, __FUNCTION__, __LINE__, ##__VA_ARGS__) + +#define hgic_enter() printk("enter %s\r\n", __FUNCTION__) +#define hgic_leave() printk("leave %s\r\n", __FUNCTION__) + +#define HGIC_CMD_TIMEOUT 500 +#define HGIC_TX_TIMEOUT 10 +#define HGIC_ALIVE_TIMER 5000 + +enum hgic_bus_type { + HGIC_BUS_SDIO = 0x1, + HGIC_BUS_USB, + HGIC_BUS_HWSIM, + HGIC_BUS_WLOE, + HGIC_BUS_SDSPI, +}; + + +enum hgic_hdr_type { + HGIC_HDR_TYPE_ACK = 0x1, + HGIC_HDR_TYPE_FRM, + HGIC_HDR_TYPE_CMD, + HGIC_HDR_TYPE_EVENT, + HGIC_HDR_TYPE_FIRMWARE, + HGIC_HDR_TYPE_NLMSG, + HGIC_HDR_TYPE_BOOTDL, + HGIC_HDR_TYPE_TEST, + HGIC_HDR_TYPE_FRM2, + HGIC_HDR_TYPE_TEST2, + HGIC_HDR_TYPE_SOFTFC, + HGIC_HDR_TYPE_OTA, + HGIC_HDR_TYPE_CMD2, + HGIC_HDR_TYPE_EVENT2, + HGIC_HDR_TYPE_BOOTDL_DATA, + + HGIC_HDR_TYPE_MAX, +}; + + +enum hgic_hdr_flags2 { + HGIC_HDR_FLAGS2_AFT_BEACON = BIT(0), +}; + +enum hgic_bus_flag { + HGIC_BUS_FLAGS_DISABLE_REINIT, + HGIC_BUS_FLAGS_SLEEP, + HGIC_BUS_FLAGS_INBOOT, + HGIC_BUS_FLAGS_NOPOLL, +}; + +struct hgic_txq_param { + unsigned short txop; + unsigned short cw_min; + unsigned short cw_max; + unsigned char aifs; + unsigned char acm; +}; + +struct hgic_mcast_txparam { + unsigned char dupcnt; + unsigned char tx_bw; + unsigned char tx_mcs; + unsigned char clearch; +}; + +/*data packet header*/ +struct hgic_hdr { + unsigned short magic; + unsigned char type; + unsigned char ifidx: 4, flags: 4; + unsigned short length; + unsigned short cookie; +} __packed; + +struct hgic_rx_info { + unsigned char band; + unsigned char mcs: 4, bw: 4; + char evm; + char signal; + unsigned short freq; + s16 freq_off; + unsigned char rx_flags; + unsigned char antenna; + unsigned char nss : 4, s1g_nss : 4; + unsigned char vht_flag : 3, s1g_flag : 5; +}; + +struct hgic_tx_info { + unsigned char band; + unsigned char tx_bw; + unsigned char tx_mcs; + unsigned char antenna; + unsigned int tx_flags; + unsigned int tx_flags2; +}; + +struct hgic_key_info { + unsigned int cipher; + unsigned char icv_len; + unsigned char iv_len; + unsigned char hw_key_idx; + unsigned char flags; + char keyidx; + unsigned char keylen; + unsigned char key[0]; +}; + +struct hgic_frm_hdr { + struct hgic_hdr hdr; + union { + struct hgic_rx_info rx_info; + struct hgic_tx_info tx_info; + unsigned char rev[24]; + }; +} __packed; + +struct hgic_frm_hdr2 { + struct hgic_hdr hdr; +} __packed; + +#define HDR_CMDID(ctl) ((ctl)->hdr.type==HGIC_HDR_TYPE_CMD2?(ctl)->cmd2.cmd_id:(ctl)->cmd.cmd_id) +#define HDR_EVTID(ctl) ((ctl)->hdr.type==HGIC_HDR_TYPE_EVENT2?(ctl)->event2.event_id:(ctl)->event.event_id) +#define HDR_CMDID_SET(ctl, id) if(id>255){\ + (ctl)->hdr.type =HGIC_HDR_TYPE_CMD2;\ + (ctl)->cmd2.cmd_id = id;\ + }else{\ + (ctl)->hdr.type =HGIC_HDR_TYPE_CMD;\ + (ctl)->cmd.cmd_id = id;\ + } +#define HDR_EVTID_SET(ctl, id) if(id>255){\ + (ctl)->hdr.type =HGIC_HDR_TYPE_EVENT2;\ + (ctl)->event2.event_id = id;\ + }else{\ + (ctl)->hdr.type =HGIC_HDR_TYPE_EVENT;\ + (ctl)->event.event_id = id;\ + } + +/*contro pakcet header*/ +struct hgic_ctrl_hdr { + struct hgic_hdr hdr; + union { + struct { + unsigned char cmd_id; + short status; + } cmd; + struct { + unsigned short cmd_id; + short status; + } cmd2; + struct { + unsigned char event_id; + short value; + } event; + struct { + unsigned short event_id; + short value; + } event2; + unsigned char info[4]; + }; +} __packed; + +//ack packet +struct hgic_dack_hdr { + struct hgic_hdr hdr; + uint16_t cookies[0]; +} __packed; + +struct hgic_nlmsg_hdr { + struct hgic_hdr hdr; + uint32_t group; + uint32_t portid; +}; + +struct hgic_bootdl_resp_hdr { + struct hgic_hdr hdr; + unsigned char cmd; + unsigned char rsp; + unsigned char rsp_data[4]; + unsigned char reserved; + unsigned char check; +} __attribute__((packed)); + + +struct hgic_bootdl_cmd_hdr { + struct hgic_hdr hdr; + unsigned char cmd; + unsigned char cmd_len; + unsigned char cmd_flag; + unsigned char addr[4]; + unsigned char len[4]; + unsigned char check; +} __attribute__((packed)); + +struct hgic_ota_hdr { + unsigned int version; + unsigned int off; + unsigned int tot_len; + unsigned short len; + unsigned short checksum; + unsigned short chipid; + unsigned short err_code; + unsigned char data[0]; +}; + +/////////////////////////////////////////////////////////////////////////////////////////////////// +enum hgic_rom_cmd { + HGIC_ROM_CMD_ENTER = 0, + HGIC_ROM_CMD_LDFW, + HGIC_ROM_CMD_RUN, +}; + +struct hgic_fw_ldinfo { + unsigned int run_addr; + unsigned int encrypt: 1, + resv: 31; +}; + +struct hgic_rom_hdr { + unsigned char cmd; + unsigned char subcmd; + unsigned char data[0]; +}; + +struct hgic_sta_status { + unsigned short aid; + unsigned char addr[6]; + char rssi; + char evm; + char evm_std; + unsigned char tx_mcs; + unsigned char tx_bw; +}; + +struct hgic_fw_status { + unsigned short rxq; + unsigned short txq; + unsigned short acq[4]; + unsigned short sta_count; + struct hgic_sta_status sta[0]; +}; + +enum HGIC_BUS_BOOTDL_CKSUM{ + HGIC_BUS_BOOTDL_CHECK_SUM = 0, + HGIC_BUS_BOOTDL_CHECK_CRC8, + HGIC_BUS_BOOTDL_CHECK_0XFD, + HGIC_BUS_BOOTDL_CHECK_OFF = 0xFF +} ; + +struct hgic_bus { + int type; + int dev_id; + int drv_tx_headroom; + void *bus_priv; + unsigned long flags; + int bootdl_pktlen; + int bootdl_cksum; + int blk_size; + int (*tx_packet)(void *bus, struct sk_buff *skb); + void (*tx_complete)(void *hg, struct sk_buff *skb, int success); + int (*rx_packet)(void *hg, struct sk_buff *skb, int len); + int (*reinit)(void *bus); +}; + +extern int hgic_core_probe(void *dev, struct hgic_bus *bus); +extern void hgic_core_probe_post(void *priv); +extern int hgic_core_remove(void *arg); +extern int hgic_core_suspend(void *hgobj); +extern int hgic_core_resume(void *hgobj); + +#ifdef __RTOS__ +#define ALLOC_ORDERED_WORKQUEUE alloc_ordered_workqueue +#define ALLOC_NETDEV_MQS alloc_netdev_mqs +#define netif_queue_stopped(n) (0) +#define netif_start_queue(n) +#define netif_stop_queue(n) +#else +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,36) +#define ALLOC_ORDERED_WORKQUEUE alloc_ordered_workqueue +#else +#define ALLOC_ORDERED_WORKQUEUE(n,f) create_singlethread_workqueue(n) +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,0,0) +#define ALLOC_NETDEV_MQS(size,name,setup,txqs,rxqs) alloc_netdev_mqs(size,name,0,setup,txqs,rxqs) +#elif LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,36) +#define ALLOC_NETDEV_MQS(size,name,setup,txqs,rxqs) alloc_netdev_mq(size,name,setup,txqs) +#else +#define ALLOC_NETDEV_MQS(size,name,setup,txqs,rxqs) alloc_netdev_mqs(size,name,setup,txqs,rxqs) +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,14,1) +#define _KERNEL_READ(fp, buff, size, off) kernel_read(fp, buff, size, off) +#define _KERNEL_WRITE(fp, buff, size, off) kernel_write(fp, buff, size, off) +#else +#define _KERNEL_READ(fp, buff, size, off) kernel_read(fp, 0, buff, size) +#define _KERNEL_WRITE(fp, buff, size, off) kernel_write(fp, buff, size, off) +#endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,14,0) +#define setup_timer(a, b, c) timer_setup(a, b, 0) +#define init_timer(...) +#endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,0,0) +#define ACCESS_OK(type, addr, size) access_ok(addr, size) +#define DEV_OPEN(x) dev_open(x, NULL) +#else +#define ACCESS_OK(type, addr, size) access_ok(type, addr, size) +#define DEV_OPEN(x) dev_open(x) +#endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,7,0) && defined CONFIG_HGIC_2G +#define IEEE80211_NUM_BANDS NUM_NL80211_BANDS +#define IEEE80211_BAND_2GHZ NL80211_BAND_2GHZ +#define IEEE80211_BAND_5GHZ NL80211_BAND_5GHZ +#endif +#ifndef IEEE80211_NUM_ACS +#define IEEE80211_NUM_ACS 4 +#endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,12,0) && defined CONFIG_HGIC_2G +#define vht_nss nss +#endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,14,0) && defined CONFIG_HGIC_2G +#define IEEE80211_CHAN_NO_IBSS IEEE80211_CHAN_NO_IR +#define IEEE80211_CHAN_PASSIVE_SCAN IEEE80211_CHAN_NO_IR +#endif +#if LINUX_VERSION_CODE < KERNEL_VERSION(5,5,0) +#define proc_ops file_operations +#define proc_open open +#define proc_read read +#define proc_lseek llseek +#define proc_write write +#define proc_release release +#endif +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,3,0) +#define ieee80211_free_txskb(hw, skb) dev_kfree_skb_any(skb) +#endif +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,8,0) +#define RX_FLAG_MACTIME_START RX_FLAG_MACTIME_MPDU +#define IEEE80211_ITERATE_ACTIVE_INTERFACES_ATOMIC(hw, flags, iterator, vif) ieee80211_iterate_active_interfaces_atomic(hw, iterator, vif) +#else +#define IEEE80211_ITERATE_ACTIVE_INTERFACES_ATOMIC(hw, flags, iterator, vif) ieee80211_iterate_active_interfaces_atomic(hw, flags, iterator, vif) +#endif +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0) +static inline void *__PDE_DATA(const struct inode *inode) +{ + return PDE(inode)->data; +} +static inline void *PDE_DATA(const struct inode *inode) +{ + return __PDE_DATA(inode); +} +#endif +#endif +#endif diff --git a/hgic_fmac/Makefile b/hgic_fmac/Makefile new file mode 100644 index 0000000..b2d5b13 --- /dev/null +++ b/hgic_fmac/Makefile @@ -0,0 +1,23 @@ +$(CONFIG_HGICF)-objs += cfg.o +$(CONFIG_HGICF)-objs += ctrl.o +$(CONFIG_HGICF)-objs += core.o +$(CONFIG_HGICF)-objs += event.o +$(CONFIG_HGICF)-objs += procfs.o +$(CONFIG_HGICF)-objs += ../utils/fwdl.o +$(CONFIG_HGICF)-objs += ../utils/utils.o +$(CONFIG_HGICF)-objs += ../utils/fwctrl.o +$(CONFIG_HGICF)-objs += ../utils/ota.o +$(CONFIG_HGICF)-objs += ../utils/fwinfo.o + +ifeq ($(CONFIG_HGICF), hgicf_sdio) +$(CONFIG_HGICF)-objs += ../utils/if_sdio.o +obj-m += hgicf_sdio.o +else ifeq ($(CONFIG_HGICF), hgicf_usb) +$(CONFIG_HGICF)-objs += ../utils/if_usb.o +obj-m += hgicf_usb.o +else +obj-m += hgicf.o +endif + + + diff --git a/hgic_fmac/cfg.c b/hgic_fmac/cfg.c new file mode 100644 index 0000000..09c5f1f --- /dev/null +++ b/hgic_fmac/cfg.c @@ -0,0 +1,1178 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "hgicf.h" +#include "cfg.h" + +struct hgpriv_set { + char *name; + int (*set)(struct net_device *dev, char *args); +}; +struct hgpriv_get { + char *name; + int (*get)(struct net_device *dev, struct iwreq *wrqin); +}; + +#define hgicf_pick_values(pick_type, str, array, size) do{\ + char *ptr = NULL;\ + if(str && strlen(str) > 0){\ + while ((ptr = strsep((char **)&str, ",")) != NULL) {\ + if (count >= size) break;\ + array[count++] = (pick_type)simple_strtol(ptr, 0, 10);\ + }\ + }\ + }while(0) + +static int hgicf_copyfrom_iwreq(struct iwreq *wrqin, char *buf, int len) +{ + int ret = 0; + if (len > 0) { + memset(buf, 0, len); + if (ACCESS_OK(VERIFY_READ, wrqin->u.data.pointer, wrqin->u.data.length)) { + ret = copy_from_user(buf, wrqin->u.data.pointer, wrqin->u.data.length); + } else { + memcpy(buf, wrqin->u.data.pointer, wrqin->u.data.length); + } + } + return ret; +} + +static void hgicf_copyto_iwreq(struct iwreq *wrqin, char *buf, int len) +{ + if (len > 0) { + if (ACCESS_OK(VERIFY_WRITE, wrqin->u.data.pointer, len)) { + if (!__copy_to_user(wrqin->u.data.pointer, buf, len)){ + wrqin->u.data.length = (u16)len; + } + } else { + memset(wrqin->u.data.pointer, 0, len + 1); + memcpy(wrqin->u.data.pointer, buf, len); + } + } +} + +static int hgicf_ioctl_set_countryregion(struct net_device *dev, char *args) +{ + struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev); + if (args == NULL) { + return -EINVAL; + } + return hgic_fwctrl_set_countryregion(&(vif->hg->ctrl), args); +} + +static int hgicf_ioctl_set_ssid(struct net_device *dev, char *args) +{ + int ret = -1; + struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev); + + ret = hgic_fwctrl_set_ssid(&(vif->hg->ctrl), args); + if (!ret) { + strcpy(vif->hw_ssid, args); + } + return ret; +} + +static int hgicf_ioctl_set_bssid(struct net_device *dev, char *args) +{ + int ret = -1; + u8 bssid[6]; + struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev); + + if (args == NULL || vif->fwifidx != HGIC_WDEV_ID_STA) { + return -EINVAL; + } + hgic_pick_macaddr(args, bssid); + ret = hgic_fwctrl_set_bssid(&(vif->hg->ctrl), bssid); + if (!ret) { + memcpy(vif->hw_bssid, args, ETH_ALEN); + } + return ret; +} + +static int hgicf_ioctl_set_channel(struct net_device *dev, char *args) +{ + struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev); + if (args == NULL) { + return -EINVAL; + } + return hgic_fwctrl_set_channel(&(vif->hg->ctrl), simple_strtol(args, 0, 10)); +} + +static int hgicf_ioctl_set_bssid_filter(struct net_device *dev, char *args) +{ + struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev); + return hgic_fwctrl_set_bssid_filter(&(vif->hg->ctrl), args); +} + +static int hgicf_ioctl_set_rts_threshold(struct net_device *dev, char *args) +{ + struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev); + if (args == NULL) { + return -EINVAL; + } + return hgic_fwctrl_set_rts_threshold(&(vif->hg->ctrl), simple_strtol(args, 0, 10)); +} + +static int hgicf_ioctl_set_frag_threshold(struct net_device *dev, char *args) +{ + struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev); + if (args == NULL) { + return -EINVAL; + } + return hgic_fwctrl_set_frag_threshold(&(vif->hg->ctrl), simple_strtol(args, 0, 10)); +} + +static int hgicf_ioctl_set_key_mgmt(struct net_device *dev, char *args) +{ + struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev); + return hgic_fwctrl_set_key_mgmt(&(vif->hg->ctrl), args); +} + +static int hgicf_ioctl_set_wpa_psk(struct net_device *dev, char *args) +{ + struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev); + return hgic_fwctrl_set_wpa_psk(&(vif->hg->ctrl), args); +} + +static int hgicf_ioctl_reset_counter(struct net_device *dev, char *args) +{ + //struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev); + hgic_dbg("%s\r\n", args); + return 0; +} + +static int hgicf_ioctl_set_freq_range(struct net_device *dev, char *args) +{ + struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev); + u32 vals[3] = {0, 0, 0}; + int count = 0; + + if (args == NULL || strlen(args) == 0) { + return -EINVAL; + } + hgicf_pick_values(u32, args, vals, 3); + if ((vals[0] < 7200 || vals[0] > 9300) || + (vals[1] < vals[0] || vals[1] > 9300) || + (vals[2] < 1 || vals[2] > 8)) { + return -EINVAL; + } + return hgic_fwctrl_set_freq_range(&(vif->hg->ctrl), vals[0], vals[1], vals[2]); +} + +static int hgicf_ioctl_set_bss_bw(struct net_device *dev, char *args) +{ + struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev); + u8 bss_bw = (u8)simple_strtol(args, 0, 10); + if (bss_bw != 1 && bss_bw != 2 && bss_bw != 4 && bss_bw != 8) { + bss_bw = 8; + } + return hgic_fwctrl_set_bss_bw(&(vif->hg->ctrl), bss_bw); +} + +static int hgicf_ioctl_set_tx_bw(struct net_device *dev, char *args) +{ + struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev); + u8 tx_bw = (u8)simple_strtol(args, 0, 10); + if (tx_bw != 1 && tx_bw != 2 && tx_bw != 4 && tx_bw != 8) { + tx_bw = 0; + } + return hgic_fwctrl_set_tx_bw(&(vif->hg->ctrl), tx_bw); +} + +static int hgicf_ioctl_set_tx_mcs(struct net_device *dev, char *args) +{ + struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev); + u8 tx_mcs = (u8)simple_strtol(args, 0, 10); + return hgic_fwctrl_set_tx_mcs(&(vif->hg->ctrl), tx_mcs); +} + +static int hgicf_ioctl_set_acs(struct net_device *dev, char *args) +{ + struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev); + u16 vals[2] = {0, 0}; + int count = 0; + + if (args == NULL || strlen(args) == 0) { + return -EINVAL; + } + hgicf_pick_values(u16, args, vals, 2); + return hgic_fwctrl_set_acs(&(vif->hg->ctrl), vals[0], vals[1]); +} + +static int hgicf_ioctl_set_bgrssi(struct net_device *dev, char *args) +{ + struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev); + u8 rssi = (u8)simple_strtol(args, 0, 10); + return hgic_fwctrl_set_bgrssi(&(vif->hg->ctrl), rssi); +} + +static int hgicf_ioctl_set_chan_list(struct net_device *dev, char *args) +{ + struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev); + u16 vals[32] = {0}; + int count = 0; + + if (args == NULL || strlen(args) == 0) { + return -EINVAL; + } + hgicf_pick_values(u16, args, vals, 32); + return hgic_fwctrl_set_chan_list(&(vif->hg->ctrl), vals, count); +} + +static int hgicf_ioctl_set_mode(struct net_device *dev, char *args) +{ + struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev); + if (args == NULL) { + return -EINVAL; + } + return hgic_fwctrl_set_mode(&(vif->hg->ctrl), args); +} + +static int hgicf_ioctl_set_paired_stas(struct net_device *dev, char *args) +{ + u8 *buf = NULL; + int len = 0; + int ret = 0, i = 0; + struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev); + + if (args == NULL || strlen(args) == 0) { + return -EINVAL; + } + len = ALIGN(strlen(args), 17); + buf = kzalloc(len, GFP_KERNEL); + if (buf == NULL) { + return -ENOMEM; + } + while ((i + 1) * 17 <= len) { + if (hgic_pick_macaddr(args + i * 17, buf + i * 6)) { + i++; + } else { + break; + } + } + //hgic_dbg("set paired stas:%s\r\n", args); + ret = hgic_fwctrl_set_paired_stas(&(vif->hg->ctrl), buf, i * 6); + kfree(buf); + return ret; +} + +static int hgicf_ioctl_set_pairing(struct net_device *dev, char *args) +{ + u8 enable = 0; + struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev); + + if (args == NULL) { + return -EINVAL; + } + enable = simple_strtol(args, 0, 10); + return hgic_fwctrl_set_pairing(&(vif->hg->ctrl), enable); +} + +static int hgicf_ioctl_set_beacon_int(struct net_device *dev, char *args) +{ + struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev); + u8 beacon_int = (u8)simple_strtol(args, 0, 10); + return hgic_fwctrl_set_beacon_int(&(vif->hg->ctrl), beacon_int); +} + +static int hgicf_ioctl_set_radio_off(struct net_device *dev, char *args) +{ + struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev); + vif->hg->radio_off = (u8)simple_strtol(args, 0, 10); + return hgic_fwctrl_radio_onoff(&(vif->hg->ctrl), !vif->hg->radio_off); +} + +static int hgicf_ioctl_join_group(struct net_device *dev, char *args) +{ + u8 addr[6]; + u32 aid = 0; + struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev); + + if (args == NULL) { + return -EINVAL; + } + hgic_pick_macaddr(args, addr); + aid = simple_strtol(args + 18, 0, 10); + return hgic_fwctrl_join_group(&(vif->hg->ctrl), addr, aid); +} + +static int hgicf_ioctl_set_ethertype(struct net_device *dev, char *args) +{ + u16 type = 0; + struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev); + + if (args == NULL) { + return -EINVAL; + } + type = simple_strtol(args, 0, 16); + return hgic_fwctrl_set_ethertype(&(vif->hg->ctrl), type); +} + +static int hgicf_ioctl_set_txpower(struct net_device *dev, char *args) +{ + u16 type = 0; + struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev); + + if (args == NULL) { + return -EINVAL; + } + type = simple_strtol(args, 0, 10); + return hgic_fwctrl_set_txpower(&(vif->hg->ctrl), type); +} + +static int hgicf_ioctl_set_aggcnt(struct net_device *dev, char *args) +{ + u8 aggcnt = 0; + struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev); + + if (args == NULL) { + return -EINVAL; + } + aggcnt = simple_strtol(args, 0, 10); + return hgic_fwctrl_set_agg_cnt(&(vif->hg->ctrl), aggcnt); +} +static int hgicf_ioctl_set_ps_connect(struct net_device *dev, char *args) +{ + int count = 0; + u8 vals[2]; + struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev); + + if (args == NULL) { + return -EINVAL; + } + hgicf_pick_values(u8, args, vals, 2); + return hgic_fwctrl_set_ps_connect(&(vif->hg->ctrl), vals[0], vals[1]); +} + +static int hgicf_ioctl_set_bss_max_idle(struct net_device *dev, char *args) +{ + u32 max_idle = 0; + struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev); + + if (args == NULL) { + return -EINVAL; + } + max_idle = simple_strtol(args, 0, 10); + return hgic_fwctrl_set_bss_max_idle(&(vif->hg->ctrl), max_idle); +} + +static int hgicf_ioctl_set_dtim_period(struct net_device *dev, char *args) +{ + u32 period = 0; + struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev); + + if (args == NULL) { + return -EINVAL; + } + period = simple_strtol(args, 0, 10); + return hgic_fwctrl_set_dtim_period(&(vif->hg->ctrl), period); +} + +static int hgicf_ioctl_set_wkio_mode(struct net_device *dev, char *args) +{ + u32 mode = 0; + struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev); + + if (args == NULL) { + return -EINVAL; + } + mode = simple_strtol(args, 0, 10); + return hgic_fwctrl_set_wkio_mode(&(vif->hg->ctrl), mode); +} + +static int hgicf_ioctl_set_load_def(struct net_device *dev, char *args) +{ + u32 rst = 0; + struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev); + if (args == NULL) { + return -EINVAL; + } + rst = simple_strtol(args, 0, 10); + return hgic_fwctrl_set_load_def(&(vif->hg->ctrl), rst); +} + +static int hgicf_ioctl_set_disassoc_sta(struct net_device *dev, char *args) +{ + u8 addr[6]; + struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev); + + if (args == NULL) { + return -EINVAL; + } + hgic_pick_macaddr(args, addr); + return hgic_fwctrl_disassoc_sta(&(vif->hg->ctrl), addr); +} + +static int hgicf_ioctl_set_ps_mode(struct net_device *dev, char *args) +{ + u32 mode = 0; + struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev); + + if (args == NULL) { + return -EINVAL; + } + mode = simple_strtol(args, 0, 10); + return hgic_fwctrl_set_ps_mode(&(vif->hg->ctrl), mode); +} + +static int hgicf_ioctl_set_aplost_time(struct net_device *dev, char *args) +{ + u32 time = 0; + struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev); + + if (args == NULL) { + return -EINVAL; + } + time = simple_strtol(args, 0, 10); + return hgic_fwctrl_set_aplost_time(&(vif->hg->ctrl), time); +} + +static int hgicf_ioctl_set_unpair(struct net_device *dev, char *args) +{ + u8 addr[6]; + struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev); + hgic_pick_macaddr(args, addr); + return hgic_fwctrl_unpair(&(vif->hg->ctrl), addr); +} + +static int hgicf_ioctl_set_auto_chswitch(struct net_device *dev, char *args) +{ + u32 enable = 0; + struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev); + + if (args == NULL) { + return -EINVAL; + } + enable = simple_strtol(args, 0, 10); + return hgic_fwctrl_set_auto_chswitch(&(vif->hg->ctrl), enable == 1); +} + +static int hgicf_ioctl_set_mcast_key(struct net_device *dev, char *args) +{ + struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev); + if (args == NULL) { + return -EINVAL; + } + return hgic_fwctrl_set_mcast_key(&(vif->hg->ctrl), args); +} + +static int hgicf_ioctl_set_reassoc_wkhost(struct net_device *dev, char *args) +{ + u32 enable = 0; + struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev); + + if (args == NULL) { + return -EINVAL; + } + enable = simple_strtol(args, 0, 10); + return hgic_fwctrl_set_reassoc_wkhost(&(vif->hg->ctrl), enable == 1); +} + +static int hgicf_ioctl_set_wakeup_io(struct net_device *dev, char *args) +{ + struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev); + u8 vals[2] = {0, 0}; + int count = 0; + + hgicf_pick_values(u8, args, vals, 2); + return hgic_fwctrl_set_wakeup_io(&(vif->hg->ctrl), vals[0], vals[1]); +} + +static int hgicf_ioctl_set_dbginfo_output(struct net_device *dev, char *args) +{ + u32 enable = 0; + struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev); + + if (args) { + enable = simple_strtol(args, 0, 10); + } + return hgic_fwctrl_set_dbginfo_output(&(vif->hg->ctrl), enable == 1); +} + +static int hgicf_ioctl_set_sysdbg(struct net_device *dev, char *args) +{ + struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev); + if (args == NULL) { + return -EINVAL; + } + return hgic_fwctrl_set_sysdbg(&(vif->hg->ctrl), args); +} + +static int hgicf_ioctl_set_primary_chan(struct net_device *dev, char *args) +{ + u32 chan = 0; + struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev); + + if (args == NULL) { + return -EINVAL; + } + chan = simple_strtol(args, 0, 10); + return hgic_fwctrl_set_primary_chan(&(vif->hg->ctrl), chan); +} + +static int hgicf_ioctl_set_autosleep_time(struct net_device *dev, char *args) +{ + u32 time = 0; + struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev); + + if (args == NULL) { + return -EINVAL; + } + time = simple_strtol(args, 0, 10); + return hgic_fwctrl_set_autosleep_time(&(vif->hg->ctrl), time); +} + +static int hgicf_ioctl_set_supper_pwr(struct net_device *dev, char *args) +{ + u32 enable = 0; + struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev); + + if (args == NULL) { + return -EINVAL; + } + enable = simple_strtol(args, 0, 10); + return hgic_fwctrl_set_supper_pwr(&(vif->hg->ctrl), enable); +} + +static int hgicf_ioctl_set_repeater_ssid(struct net_device *dev, char *args) +{ + struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev); + return hgic_fwctrl_set_repeater_ssid(&(vif->hg->ctrl), args); +} + +static int hgicf_ioctl_set_repeater_psk(struct net_device *dev, char *args) +{ + struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev); + return hgic_fwctrl_set_repeater_psk(&(vif->hg->ctrl), args); +} + +static int hgicf_ioctl_set_auto_save(struct net_device *dev, char *args) +{ + u32 enable = 0; + struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev); + + if (args == NULL) { + return -EINVAL; + } + enable = simple_strtol(args, 0, 10); + return hgic_fwctrl_set_auto_save(&(vif->hg->ctrl), enable); +} +static int hgicf_ioctl_set_pair_autostop(struct net_device *dev, char *args) +{ + u32 enable = 0; + struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev); + + if (args == NULL) { + return -EINVAL; + } + enable = simple_strtol(args, 0, 10); + return hgic_fwctrl_set_pair_autostop(&(vif->hg->ctrl), enable); +} +static int hgicf_ioctl_set_dcdc13v(struct net_device *dev, char *args) +{ + u32 enable = 0; + struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev); + + if (args == NULL) { + return -EINVAL; + } + enable = simple_strtol(args, 0, 10); + return hgic_fwctrl_set_dcdc13v(&(vif->hg->ctrl), enable); +} +static int hgicf_ioctl_set_acktmo(struct net_device *dev, char *args) +{ + u32 tmo = 0; + struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev); + + if (args == NULL) { + return -EINVAL; + } + tmo = simple_strtol(args, 0, 10); + return hgic_fwctrl_set_acktmo(&(vif->hg->ctrl), tmo); +} +static int hgicf_ioctl_set_pa_pwrctl_dis(struct net_device *dev, char *args) +{ + u32 dis = 0; + struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev); + + if (args == NULL) { + return -EINVAL; + } + dis = simple_strtol(args, 0, 10); + return hgic_fwctrl_set_pa_pwrctl_dis(&(vif->hg->ctrl), dis); +} +static int hgicf_ioctl_set_dhcpc(struct net_device *dev, char *args) +{ + u32 en = 0; + struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev); + + if (args == NULL) { + return -EINVAL; + } + en = simple_strtol(args, 0, 10); + return hgic_fwctrl_set_dhcpc(&(vif->hg->ctrl), en); +} +static int hgicf_ioctl_set_wkdata_save(struct net_device *dev, char *args) +{ + u32 en = 0; + struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev); + + if (args == NULL) { + return -EINVAL; + } + en = simple_strtol(args, 0, 10); + return hgic_fwctrl_set_wkdata_save(&(vif->hg->ctrl), en); +} +static int hgicf_ioctl_set_mcast_txparam(struct net_device *dev, char *args) +{ + struct hgic_mcast_txparam txparam; + struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev); + u8 *vals = (u8 *)&txparam; + int count = 0; + + if (args == NULL) { + return -EINVAL; + } + hgicf_pick_values(u8, args, vals, 4); + return hgic_fwctrl_set_mcast_txparam(&(vif->hg->ctrl), &txparam); +} + +static int hgicf_ioctl_set_resetsta(struct net_device *dev, char *args) +{ + u8 addr[6]; + struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev); + + if (args == NULL || vif->fwifidx != HGIC_WDEV_ID_STA) { + return -EINVAL; + } + hgic_pick_macaddr(args, addr); + return hgic_fwctrl_reset_sta(&(vif->hg->ctrl), addr); +} + +static int hgicf_ioctl_set_ant_auto(struct net_device *dev, char *args) +{ + u32 en = 0; + struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev); + + if (args == NULL) { + return -EINVAL; + } + en = simple_strtol(args, 0, 10); + return hgic_fwctrl_set_ant_auto(&(vif->hg->ctrl), en); +} +static int hgicf_ioctl_select_ant(struct net_device *dev, char *args) +{ + u32 ant = 0; + struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev); + + if (args == NULL) { + return -EINVAL; + } + ant = simple_strtol(args, 0, 10); + return hgic_fwctrl_select_ant(&(vif->hg->ctrl), ant); +} +static int hgicf_ioctl_set_wkhost_reasons(struct net_device *dev, char *args) +{ + int count = 0; + u8 reasons[33]; + struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev); + + if (args == NULL) { + return -EINVAL; + } + memset(reasons, 0, sizeof(reasons)); + hgicf_pick_values(u8, args, reasons, 32); + return hgic_fwctrl_set_wkhost_reasons(&(vif->hg->ctrl), reasons, count + 1); +} + +static int hgicf_ioctl_set_mac_filter(struct net_device *dev, char *args) +{ + u32 en = 0; + struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev); + + if (args == NULL) { + return -EINVAL; + } + en = simple_strtol(args, 0, 10); + return hgic_fwctrl_set_mac_filter(&(vif->hg->ctrl), en); +} + +static int hgicf_ioctl_set_atcmd(struct net_device *dev, char *args) +{ + struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev); + return hgic_fwctrl_set_atcmd(&(vif->hg->ctrl), args); +} + +static int hgicf_ioctl_set_roaming(struct net_device *dev, char *args) +{ + int count = 0; + u8 vals[2]; + struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev); + + if (args == NULL) { + return -EINVAL; + } + hgicf_pick_values(u8, args, vals, 2); + return hgic_fwctrl_set_roaming(&(vif->hg->ctrl), vals[0], vals[2]); +} + +static int hgicf_ioctl_set_ap_hide(struct net_device *dev, char *args) +{ + u32 hide = 0; + struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev); + + if (args == NULL) { + return -EINVAL; + } + hide = simple_strtol(args, 0, 10); + return hgic_fwctrl_set_ap_hide(&(vif->hg->ctrl), hide); +} + +static int hgicf_ioctl_set_max_txcnt(struct net_device *dev, char *args) +{ + u32 txcnt = 0; + struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev); + + if (args == NULL) { + return -EINVAL; + } + txcnt = simple_strtol(args, 0, 10); + return hgic_fwctrl_set_frm_tx_maxcnt(&(vif->hg->ctrl), txcnt); +} +static int hgicf_ioctl_set_assert_holdup(struct net_device *dev, char *args) +{ + u32 holdup = 0; + struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev); + + if (args == NULL) { + return -EINVAL; + } + holdup = simple_strtol(args, 0, 10); + return hgic_fwctrl_set_assert_holdup(&(vif->hg->ctrl), holdup); +} + +static struct hgpriv_set hgpriv_sets[] = { + {"country_region", hgicf_ioctl_set_countryregion}, + {"ssid", hgicf_ioctl_set_ssid}, + {"bssid", hgicf_ioctl_set_bssid}, + {"channel", hgicf_ioctl_set_channel}, + {"rts_threshold", hgicf_ioctl_set_rts_threshold}, + {"frag_threshold", hgicf_ioctl_set_frag_threshold}, + {"key_mgmt", hgicf_ioctl_set_key_mgmt}, + {"wpa_psk", hgicf_ioctl_set_wpa_psk}, + {"reset_counter", hgicf_ioctl_reset_counter}, + {"bssid_filter", hgicf_ioctl_set_bssid_filter}, + {"freq_range", hgicf_ioctl_set_freq_range}, + {"bss_bw", hgicf_ioctl_set_bss_bw}, + {"tx_bw", hgicf_ioctl_set_tx_bw}, + {"tx_mcs", hgicf_ioctl_set_tx_mcs}, + {"acs", hgicf_ioctl_set_acs}, + {"bgrssi", hgicf_ioctl_set_bgrssi}, + {"chan_list", hgicf_ioctl_set_chan_list}, + {"mode", hgicf_ioctl_set_mode}, + {"paired_stas", hgicf_ioctl_set_paired_stas}, + {"pairing", hgicf_ioctl_set_pairing}, + {"beacon_int", hgicf_ioctl_set_beacon_int}, + {"radio_off", hgicf_ioctl_set_radio_off}, + {"join_group", hgicf_ioctl_join_group}, + {"ether_type", hgicf_ioctl_set_ethertype}, + {"txpower", hgicf_ioctl_set_txpower}, + {"agg_cnt", hgicf_ioctl_set_aggcnt}, + {"ps_connect", hgicf_ioctl_set_ps_connect}, + {"bss_max_idle", hgicf_ioctl_set_bss_max_idle}, + {"wkio_mode", hgicf_ioctl_set_wkio_mode}, + {"loaddef", hgicf_ioctl_set_load_def}, + {"disassoc_sta", hgicf_ioctl_set_disassoc_sta}, + {"dtim_period", hgicf_ioctl_set_dtim_period}, + {"ps_mode", hgicf_ioctl_set_ps_mode}, + {"aplost_time", hgicf_ioctl_set_aplost_time}, + {"unpair", hgicf_ioctl_set_unpair}, + {"auto_chswitch", hgicf_ioctl_set_auto_chswitch}, + {"mcast_key", hgicf_ioctl_set_mcast_key}, + {"reassoc_wkhost", hgicf_ioctl_set_reassoc_wkhost}, + {"wakeup_io", hgicf_ioctl_set_wakeup_io}, + {"dbginfo", hgicf_ioctl_set_dbginfo_output}, + {"sysdbg", hgicf_ioctl_set_sysdbg}, + {"primary_chan", hgicf_ioctl_set_primary_chan}, + {"autosleep_time", hgicf_ioctl_set_autosleep_time}, + {"supper_pwr", hgicf_ioctl_set_supper_pwr}, + {"r_ssid", hgicf_ioctl_set_repeater_ssid}, + {"r_psk", hgicf_ioctl_set_repeater_psk}, + {"auto_save", hgicf_ioctl_set_auto_save}, + {"pair_autostop", hgicf_ioctl_set_pair_autostop}, + {"dcdc13", hgicf_ioctl_set_dcdc13v}, + {"acktmo", hgicf_ioctl_set_acktmo}, + {"pa_pwrctl_dis", hgicf_ioctl_set_pa_pwrctl_dis}, + {"dhcpc", hgicf_ioctl_set_dhcpc}, + {"wkdata_save", hgicf_ioctl_set_wkdata_save}, + {"mcast_txparam", hgicf_ioctl_set_mcast_txparam}, + {"reset_sta", hgicf_ioctl_set_resetsta}, + {"ant_auto", hgicf_ioctl_set_ant_auto}, + {"ant_sel", hgicf_ioctl_select_ant}, + {"wkhost_reason", hgicf_ioctl_set_wkhost_reasons}, + {"macfilter", hgicf_ioctl_set_mac_filter}, + {"atcmd", hgicf_ioctl_set_atcmd}, + {"roaming", hgicf_ioctl_set_roaming}, + {"ap_hide", hgicf_ioctl_set_ap_hide}, + {"max_txcnt", hgicf_ioctl_set_max_txcnt}, + {"assert_holdup", hgicf_ioctl_set_assert_holdup}, + {NULL,} +}; + +int hgicf_ioctl_set_proc(struct net_device *dev, struct iwreq *wrqin) +{ + int ret = 0; + char *field, *val; + struct hgpriv_set *set = NULL; + u8 *cmd = kmalloc(wrqin->u.data.length + 1, GFP_KERNEL); + + if (cmd == NULL){ + return -ENOMEM; + } + + ret = hgicf_copyfrom_iwreq(wrqin, cmd, wrqin->u.data.length + 1); + //hgic_dbg("%s\r\n", cmd); + while (!ret && (field = strsep((char **)&cmd, ";")) != NULL) { + if (!*field) { + continue; + } + + if ((val = strchr(field, '=')) != NULL) { + *val++ = 0; + } + + for (set = hgpriv_sets; val && set->name; set++) { + if (strcmp(field, set->name) == 0) { + //printk("set [%s=%s]\r\n", set->name, val); + ret = set->set(dev, val); + break; + } + } + + if (set->name == NULL) { + hgic_dbg("not support:%s=%s\r\n", field, val); + ret = -1; + break; + } + } + + kfree(cmd); + return ret; +} + +static int hgicf_ioctl_get_scan_list(struct net_device *dev, struct iwreq *wrqin) +{ + int ret = 0; + int i = 0; + int count = 0; + int len = 0; + struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev); + struct hgic_bss_info *bss = NULL; + char *buf = kzalloc(1024, GFP_ATOMIC); + char *print_buf = kzalloc(4096, GFP_ATOMIC); + + if (buf == NULL || print_buf == NULL) { + if (buf) { kfree(buf); } + if (print_buf) { kfree(print_buf); } + return -ENOMEM; + } + + ret = hgic_fwctrl_get_scan_list(&(vif->hg->ctrl), buf, 1024); + if (ret > 0) { + bss = (struct hgic_bss_info *)buf; + count = ret / sizeof(struct hgic_bss_info); + len += sprintf(print_buf + len, "\r\nBSSID \tSSID \tEncryption\tFrequence\tSignal\r\n"); + for (i = 0; i < count; i++) { + len += sprintf(print_buf + len, "%pM\t%s\t%10s\t%10d\t%d\r\n", + bss[i].bssid, bss[i].ssid, + bss[i].encrypt ? (bss[i].encrypt == 1 ? "WPA" : "WPA2") : "NONE", + bss[i].freq, bss[i].signal); + } + wrqin->u.data.length = (u16)len; + } else { + len = 2; + } + hgicf_copyto_iwreq(wrqin, print_buf, len); + kfree(buf); + kfree(print_buf); + return (0); +} + +static int hgicf_ioctl_get_sta_list(struct net_device *dev, struct iwreq *wrqin) +{ + //int i = 0; + int count = 0; + int len = 0; + struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev); + char *buf = kzalloc(1024, GFP_ATOMIC); + char *print_buf = kzalloc(4096, GFP_ATOMIC); + //char *ptr = buf; + struct hgic_sta_info *sta = (struct hgic_sta_info *)buf; + + if (buf == NULL || print_buf == NULL) { + if (buf) { kfree(buf); } + if (print_buf) { kfree(print_buf); } + return -ENOMEM; + } + + count = hgic_fwctrl_get_sta_list(&(vif->hg->ctrl), (struct hgic_sta_info *)buf, (1024 / sizeof(struct hgic_sta_info))); + if (count > 0) { + len += sprintf(print_buf, "%d sta:\r\n", count); + while (count-- > 0) { + len += sprintf(print_buf + len, "aid:%d, %pM, ps:%d, rssi:%d, evm:%d\r\n", sta->aid, sta->addr, sta->ps, (s8)sta->rssi, (s8)sta->evm); + sta++; + } + wrqin->u.data.length = (u16)len; + } else { + len = 2; + } + hgicf_copyto_iwreq(wrqin, print_buf, len); + kfree(buf); + kfree(print_buf); + return (0); +} + +static int hgicf_ioctl_get_ssid(struct net_device *dev, struct iwreq *wrqin) +{ + int len = 0; + struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev); + char *buf = kzalloc(40, GFP_ATOMIC); + + if (buf == NULL) { + return -ENOMEM; + } + len = hgic_fwctrl_get_ssid(&(vif->hg->ctrl), buf, 32); + hgicf_copyto_iwreq(wrqin, buf, len); + kfree(buf); + return (0); +} + +static int hgicf_ioctl_get_bssid(struct net_device *dev, struct iwreq *wrqin) +{ + int len = 0; + char buf[24] = {0}; + char bssid[6]; + struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev); + + len = hgic_fwctrl_get_bssid(&(vif->hg->ctrl), bssid); + if (len == 6) { sprintf(buf, "%pM", bssid); } + len = strlen(buf); + hgicf_copyto_iwreq(wrqin, buf, len); + return (0); +} + +static int hgicf_ioctl_get_wpa_psk(struct net_device *dev, struct iwreq *wrqin) +{ + int len = 0; + struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev); + char *buf = kzalloc(80, GFP_ATOMIC); + + if (buf == NULL) { + return -ENOMEM; + } + len = hgic_fwctrl_get_wpapsk(&(vif->hg->ctrl), buf, 64); + hgicf_copyto_iwreq(wrqin, buf, len); + kfree(buf); + return (0); +} + +static int hgicf_ioctl_get_txpower(struct net_device *dev, struct iwreq *wrqin) +{ + char str[4]; + int txpower = 0; + struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev); + txpower = hgic_fwctrl_get_txpower(&(vif->hg->ctrl)); + sprintf(str, "%d", txpower); + hgicf_copyto_iwreq(wrqin, str, strlen(str)); + return (0); +} + +static int hgicf_ioctl_get_bss_bw(struct net_device *dev, struct iwreq *wrqin) +{ + char str[4]; + int bss_bw = 0; + struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev); + bss_bw = hgic_fwctrl_get_bss_bw(&(vif->hg->ctrl)); + sprintf(str, "%d", bss_bw); + hgicf_copyto_iwreq(wrqin, str, strlen(str)); + return (0); +} + +static int hgicf_ioctl_get_aggcnt(struct net_device *dev, struct iwreq *wrqin) +{ + char str[4]; + int aggcnt = 0; + struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev); + aggcnt = hgic_fwctrl_get_agg_cnt(&(vif->hg->ctrl)); + sprintf(str, "%d", aggcnt); + hgicf_copyto_iwreq(wrqin, str, strlen(str)); + return (0); +} + +static int hgicf_ioctl_get_chan_list(struct net_device *dev, struct iwreq *wrqin) +{ + int cnt = 0, i = 0; + int len = 0; + u16 chan_list[16]; + char *buf = kzalloc(128, GFP_KERNEL); + struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev); + + if (buf) { + cnt = hgic_fwctrl_get_chan_list(&(vif->hg->ctrl), chan_list, 16); + for (i = 0; i < cnt; i++) { + len += sprintf(buf + len, "%d,", chan_list[i]); + } + if (len > 0) { + len--; + buf[len] = 0; + hgicf_copyto_iwreq(wrqin, buf, len); + } + kfree(buf); + } + return (0); +} + +static int hgicf_ioctl_get_freq_range(struct net_device *dev, struct iwreq *wrqin) +{ + char str[32] = {0}; + int ret; + u32 vals[3]; + struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev); + + ret = hgic_fwctrl_get_freq_range(&(vif->hg->ctrl), &vals[0], &vals[1], &vals[2]); + if (ret) { + sprintf(str, "%d,%d,%d", vals[0], vals[1], vals[2]); + } + hgicf_copyto_iwreq(wrqin, str, strlen(str)); + return (0); +} + +static int hgicf_ioctl_get_key_mgmt(struct net_device *dev, struct iwreq *wrqin) +{ + int len = 0; + struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev); + char *buf = kzalloc(40, GFP_ATOMIC); + + if (buf == NULL) { + return -ENOMEM; + } + len = hgic_fwctrl_get_key_mgmt(&(vif->hg->ctrl), buf, 32); + hgicf_copyto_iwreq(wrqin, buf, len); + kfree(buf); + return (0); +} + +static int hgicf_ioctl_get_battery_level(struct net_device *dev, struct iwreq *wrqin) +{ + char str[4]; + int val = 0; + struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev); + + val = hgic_fwctrl_get_battery_level(&(vif->hg->ctrl)); + sprintf(str, "%d", val); + hgicf_copyto_iwreq(wrqin, str, strlen(str)); + return (0); +} + +static int hgicf_ioctl_get_module_type(struct net_device *dev, struct iwreq *wrqin) +{ + u16 type = 0; + char str[12]; + struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev); + + type = (u16)hgic_fwctrl_get_module_type(&(vif->hg->ctrl)); + if (type) { + sprintf(str, "%d", type); + hgicf_copyto_iwreq(wrqin, str, strlen(str)); + } + return (0); +} + +static int hgicf_ioctl_get_disassoc_reason(struct net_device *dev, struct iwreq *wrqin) +{ + char str[12]; + int reason = 0; + struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev); + + reason = hgic_fwctrl_get_disassoc_reason(&(vif->hg->ctrl)); + sprintf(str, "%d", reason); + hgicf_copyto_iwreq(wrqin, str, strlen(str)); + return (0); +} + +static int hgicf_ioctl_get_ant_sel(struct net_device *dev, struct iwreq *wrqin) +{ + char str[12]; + int ant = 0; + struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev); + + ant = hgic_fwctrl_get_ant_sel(&(vif->hg->ctrl)); + sprintf(str, "%d", ant); + hgicf_copyto_iwreq(wrqin, str, strlen(str)); + return (0); +} + +static struct hgpriv_get hgpriv_gets[] = { + {"sta_list", hgicf_ioctl_get_sta_list}, + {"scan_list", hgicf_ioctl_get_scan_list}, + {"ssid", hgicf_ioctl_get_ssid}, + {"bssid", hgicf_ioctl_get_bssid}, + {"wpa_psk", hgicf_ioctl_get_wpa_psk}, + {"txpower", hgicf_ioctl_get_txpower}, + {"agg_cnt", hgicf_ioctl_get_aggcnt}, + {"bss_bw", hgicf_ioctl_get_bss_bw}, + {"chan_list", hgicf_ioctl_get_chan_list}, + {"freq_range", hgicf_ioctl_get_freq_range}, + {"key_mgmt", hgicf_ioctl_get_key_mgmt}, + {"battery_level", hgicf_ioctl_get_battery_level}, + {"module_type", hgicf_ioctl_get_module_type}, + {"disassoc_reason", hgicf_ioctl_get_disassoc_reason}, + {"ant_sel", hgicf_ioctl_get_ant_sel}, + {NULL,} +}; + +int hgicf_ioctl_get_proc(struct net_device *dev, struct iwreq *wrqin) +{ + int ret = 0; + char field[64]; + struct hgpriv_get *get = NULL; + + ret = hgicf_copyfrom_iwreq(wrqin, field, 64); + for (get = hgpriv_gets; !ret && get->name; get++) { + if (strcmp(field, get->name) == 0) { + return get->get(dev, wrqin); + } + } + hgic_dbg("not support:%s\r\n", field); + return -ENOTSUPP; +} + +int hgicf_ioctl_stat(struct net_device *dev, struct iwreq *wrqin) +{ + //struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev); + return 0; +} + +int hgicf_ioctl_e2p(struct net_device *dev, struct iwreq *wrqin) +{ + //struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev); + return 0; +} + +int hgicf_ioctl_savecfg(struct net_device *dev, struct iwreq *wrqin) +{ + struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev); + return hgic_fwctrl_save_cfg(&(vif->hg->ctrl)); +} + +int hgicf_ioctl_scan(struct net_device *dev, struct iwreq *wrqin) +{ + struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev); + return hgic_fwctrl_scan(&(vif->hg->ctrl)); +} + diff --git a/hgic_fmac/cfg.h b/hgic_fmac/cfg.h new file mode 100644 index 0000000..091e002 --- /dev/null +++ b/hgic_fmac/cfg.h @@ -0,0 +1,13 @@ + +#ifndef _HGICF_CFG_H_ +#define _HGICF_CFG_H_ +#ifndef __RTOS__ +int hgicf_ioctl_set_proc(struct net_device *dev, struct iwreq *wrqin); +int hgicf_ioctl_get_proc(struct net_device *dev, struct iwreq *wrqin); +int hgicf_ioctl_stat(struct net_device *dev, struct iwreq *wrqin); +int hgicf_ioctl_e2p(struct net_device *dev, struct iwreq *wrqin); +int hgicf_ioctl_scan(struct net_device *dev, struct iwreq *wrqin); +int hgicf_ioctl_savecfg(struct net_device *dev, struct iwreq *wrqin); +#endif +#endif + diff --git a/hgic_fmac/core.c b/hgic_fmac/core.c new file mode 100644 index 0000000..ed013be --- /dev/null +++ b/hgic_fmac/core.c @@ -0,0 +1,993 @@ + +#ifdef __RTOS__ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#else +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif + +#include "hgicf.h" +#include "ctrl.h" +#include "cfg.h" +#include "event.h" + +static int txq_size = 1024; +static char *conf_file = "/etc/hgicf.conf"; +static char *fw_file = "hgicf.bin"; +static int if_test = 0; +static int soft_fc = 1; +static int no_bootdl = 0; +static int qc_mode = 0; +static char *ifname = "hg%d"; +static hgic_init_cb init_cb = NULL; +#ifdef __RTOS__ +static hgic_event_cb event_cb = NULL; +#endif + +#define TXWND_INIT_VAL (2) +#define MATCH_NDEV(fwidx,hg) ((fwidx) == HGIC_WDEV_ID_STA ? (hg->sta ? hg->sta->ndev : NULL) : \ + (hg->ap ? hg->ap->ndev : NULL)) + +#ifdef __RTOS__ +static int iface_cnt = 1; +void dev_queue_xmit(struct sk_buff *skb) +{ + struct net_device *ndev = skb->dev; + if (ndev) { + ndev->netdev_ops->ndo_start_xmit(skb, ndev); + } else { + kfree_skb(skb); + } +} +#endif + +u16 hgicf_data_cookie(struct hgicf_wdev *hg) +{ + unsigned long flags; + uint16_t cookie = 0; + + spin_lock_irqsave(&hg->lock, flags); + cookie = hg->data_cookie++; + spin_unlock_irqrestore(&hg->lock, flags); + return cookie; +} + +void hgicf_load_config(struct hgicf_wdev *hg) +{ +#ifndef __RTOS__ + struct file *fp = NULL; + struct iwreq wrqin; + struct net_device *ndev = NULL; + ssize_t ret = 0; + unsigned long long __maybe_unused offset = 0 ; + char *conf = kzalloc(2048, GFP_KERNEL); + char *ptr, *str; + + if (hg->sta) {ndev = hg->sta->ndev; } + else if (hg->ap) {ndev = hg->ap->ndev; } + else { + hgic_err("No netdev!! \r\n"); + goto __out; + } + + fp = filp_open(conf_file, O_RDONLY, 0); + if (!IS_ERR(fp) && conf) { + ret = _KERNEL_READ(fp, conf, 2048, &offset); + str = conf; + + hgic_fwctrl_close_dev(&(hg->ctrl)); + while (str) { + ptr = strstr(str, "\r\n"); + if (ptr) { + *ptr = 0; + ptr += 2; + } else { + ptr = strstr(str, "\n"); + if (ptr) { *ptr++ = 0;} + } + wrqin.u.data.length = strlen(str); + if (wrqin.u.data.length > 0) { + hgic_dbg("param: [%s]\r\n", str); + wrqin.u.data.pointer = str; + ret = hgicf_ioctl_set_proc(ndev, &wrqin); + if (ret < 0) { + hgic_err("invalid param:[%s]\r\n", str); + } + } + str = ptr; + } + hgic_fwctrl_open_dev(&(hg->ctrl)); + } + +__out: + if (!IS_ERR(fp)) { filp_close(fp, NULL); } + if (conf) { kfree(conf); } +#endif +} + +static int hgicf_netif_open(struct net_device *dev) +{ + struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev); + if (!test_bit(HGICF_DEV_FLAGS_RUNNING, &vif->hg->flags)) { + return 0; + } + vif->hg->radio_off = 0; + return hgic_fwctrl_open_dev(&(vif->hg->ctrl)); +} + +static int hgicf_netif_stop(struct net_device *dev) +{ + struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev); + if (!test_bit(HGICF_DEV_FLAGS_RUNNING, &vif->hg->flags)) { + return 0; + } + vif->hg->radio_off = 1; + return hgic_fwctrl_close_dev(&(vif->hg->ctrl)); +} + +static void hgicf_netif_uninit(struct net_device *dev) +{ + struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev); + if (!test_bit(HGICF_DEV_FLAGS_RUNNING, &vif->hg->flags)) { + return; + } +} + +static netdev_tx_t hgicf_netif_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev); + struct hgic_frm_hdr2 *hdr = NULL; + u8 pad = ((ulong)skb->data & 0x3) ? (4 - ((ulong)skb->data & 0x3)) : 0; + + if (!test_bit(HGICF_DEV_FLAGS_RUNNING, &vif->hg->flags) || + (vif->hg->tx_dataq.qlen > txq_size) || + test_bit(HGIC_BUS_FLAGS_SLEEP, &vif->hg->bus->flags)) { + dev_kfree_skb(skb); + return NETDEV_TX_OK; + } + +#ifndef __RTOS__ + if (skb_headroom(skb) < sizeof(struct hgic_frm_hdr2)) { + struct sk_buff *nskb = skb_copy_expand(skb, skb->dev->needed_headroom, + skb->dev->needed_tailroom, GFP_KERNEL); + dev_kfree_skb(skb); + if (nskb == NULL) { + return NETDEV_TX_OK; + } + skb = nskb; + pad = ((ulong)skb->data & 0x3) ? (4 - ((ulong)skb->data & 0x3)) : 0; + } +#endif + + hdr = (struct hgic_frm_hdr2 *)skb_push(skb, sizeof(struct hgic_frm_hdr2) + pad); + hdr->hdr.ifidx = vif->fwifidx; + hdr->hdr.length = skb->len; + hdr->hdr.magic = HGIC_HDR_TX_MAGIC; + hdr->hdr.type = HGIC_HDR_TYPE_FRM2; + hdr->hdr.flags = pad; + hdr->hdr.cookie = hgicf_data_cookie(vif->hg); + skb_queue_tail(&vif->hg->tx_dataq, skb); + queue_work(vif->hg->tx_wq, &vif->hg->tx_work); + return NETDEV_TX_OK; +} + +static int hgicf_netif_change_mac(struct net_device *dev, void *addr) +{ + int ret = 0; +#ifdef __RTOS__ + ret = hgicf_ioctl(dev, HGIC_CMD_SET_MAC, addr, 0); +#else + struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev); + struct sockaddr *sa = (struct sockaddr *)addr; + + if (!test_bit(HGICF_DEV_FLAGS_RUNNING, &vif->hg->flags)) { + return 0; + } + ret = hgic_fwctrl_set_mac(&(vif->hg->ctrl), sa->sa_data); + if (!ret) { + ret = eth_mac_addr(dev, addr); + } +#endif + return ret; +} + +static void hgicf_netif_set_multicast_list(struct net_device *dev) +{ + struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev); + if (!test_bit(HGICF_DEV_FLAGS_RUNNING, &vif->hg->flags)) { + return; + } +} + +#ifdef __RTOS__ +static int hgicf_netif_ioctl(struct net_device *dev, u32 cmd, u32 param1, u32 param2) +{ + return hgicf_ioctl(dev, cmd, param1, param2); +} +int hgicf_cmd(char *ifname, unsigned int cmd, unsigned int param1, unsigned int param2) +{ + struct net_device *ndev = net_device_get_by_name(ifname); + if (ndev == NULL) { + return -ENODEV; + } + return hgicf_netif_ioctl(ndev, cmd, param1, param2); +} +#else +static int hgicf_netif_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +{ + struct hgicf_vif *vif = (struct hgicf_vif *)netdev_priv(dev); + if (!test_bit(HGICF_DEV_FLAGS_RUNNING, &vif->hg->flags)) { + return 0; + } + return hgicf_ioctl(dev, ifr, cmd); +} +#endif + +static const struct net_device_ops hgicf_netif_ops = { + .ndo_open = hgicf_netif_open, + .ndo_stop = hgicf_netif_stop, + .ndo_uninit = hgicf_netif_uninit, + .ndo_start_xmit = hgicf_netif_xmit, + .ndo_set_rx_mode = hgicf_netif_set_multicast_list, + .ndo_set_mac_address = hgicf_netif_change_mac, + .ndo_do_ioctl = hgicf_netif_ioctl, +}; + +static void hgicf_netif_setup(struct net_device *dev) +{ +#ifdef __RTOS__ + dev->netdev_ops = &hgicf_netif_ops; +#else + ether_setup(dev); + dev->netdev_ops = &hgicf_netif_ops; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,15,0) + dev->priv_destructor = free_netdev; +#else + dev->destructor = free_netdev; +#endif +#endif +} + +static int hgicf_request_txwnd(struct hgic_bus *bus) +{ + struct hgic_hdr *hdr = NULL; + struct sk_buff *skb = dev_alloc_skb(sizeof(struct hgic_hdr) + 2); + + if (skb == NULL) { return -ENOMEM; } + hdr = (struct hgic_hdr *)skb->data; + hdr->magic = HGIC_HDR_TX_MAGIC; + hdr->type = HGIC_HDR_TYPE_SOFTFC; + hdr->length = sizeof(struct hgic_hdr) + 2; + hdr->ifidx = 0; + hdr->flags = 0; + hdr->cookie = 0; + skb_put(skb, sizeof(struct hgic_hdr) + 2); + return bus->tx_packet(bus, skb); +} + +static int hgicf_check_txwnd(struct hgicf_wdev *hg) +{ + int err = 0; + + if (!hg->soft_fc || test_bit(HGIC_BUS_FLAGS_INBOOT, &hg->bus->flags)) { + return 0; + } + + while (hg->soft_fc && atomic_read(&hg->txwnd) < TXWND_INIT_VAL && + test_bit(HGICF_DEV_FLAGS_RUNNING, &hg->flags) && + !test_bit(HGIC_BUS_FLAGS_SLEEP, &hg->bus->flags) && + !test_bit(HGICF_DEV_FLAGS_SUSPEND, &hg->flags) && + !test_bit(HGIC_BUS_FLAGS_INBOOT, &hg->bus->flags)) { + + err = hgicf_request_txwnd(hg->bus); + if (!err) { + wait_for_completion_timeout(&hg->txwnd_cp, msecs_to_jiffies(100)); + } + + if(atomic_read(&hg->txwnd) < TXWND_INIT_VAL) + msleep(10); + } + + if (!hg->soft_fc || test_bit(HGIC_BUS_FLAGS_INBOOT, &hg->bus->flags)) { + return 0; + } + + if (atomic_read(&hg->txwnd) < TXWND_INIT_VAL) { + return -1; + } else { + atomic_dec(&hg->txwnd); + } + + return 0; +} + +static void hgicf_test_work(struct work_struct *work) +{ + int ret = 0; + u32 diff_jiff = 0; + struct hgicf_wdev *hg = NULL; + struct sk_buff *skb = NULL; + struct hgic_frm_hdr *frmhdr = NULL; + + printk("start if test ...\r\n"); + hg = container_of(work, struct hgicf_wdev, test_work); + hg->test_jiff = jiffies; + + //set_bit(HGIC_BUS_FLAGS_DISABLE_REINIT, &hg->bus->flags); + while (test_bit(HGICF_DEV_FLAGS_RUNNING, &hg->flags)) { + if (time_after(jiffies, hg->test_jiff + msecs_to_jiffies(5000))) { + diff_jiff = jiffies_to_msecs(jiffies - hg->test_jiff); + diff_jiff /= 1000; + if (diff_jiff == 0) { diff_jiff = 0xffff; } + printk("HGIC IF TEST: tx:%d KB/s, rx:%d KB/s (%d %d %d)\r\n", + (hg->test_tx_len / 1024) / diff_jiff, + (hg->test_rx_len / 1024) / diff_jiff, + hg->test_tx_len, hg->test_rx_len, diff_jiff); + hg->test_rx_len = 0; + hg->test_tx_len = 0; + hg->test_jiff = jiffies; + } + + skb = dev_alloc_skb(1500 + hg->bus->drv_tx_headroom); + if (skb) { + skb_reserve(skb, hg->bus->drv_tx_headroom); + frmhdr = (struct hgic_frm_hdr *)skb->data; + frmhdr->hdr.magic = HGIC_HDR_TX_MAGIC; + frmhdr->hdr.type = (hg->if_test == 1 ? HGIC_HDR_TYPE_TEST : HGIC_HDR_TYPE_TEST2); + frmhdr->hdr.length = 1500; + frmhdr->hdr.ifidx = 0; + frmhdr->hdr.cookie = hgicf_data_cookie(hg); + if(hg->if_test == 3) memset(skb->data+8, 0xAA, 1500-8); + skb_put(skb, 1500); + while (test_bit(HGICF_DEV_FLAGS_RUNNING, &hg->flags) && hgicf_check_txwnd(hg)) { + msleep(10); + } + ret = hg->bus->tx_packet(hg->bus, skb); + if (ret) { + msleep(10); + } + } else { + msleep(10); + } + } + printk("if test stop!\r\n"); +} + +static void hgicf_tx_work(struct work_struct *work) +{ + struct sk_buff *skb = NULL; + struct hgicf_wdev *hg = container_of(work, struct hgicf_wdev, tx_work); + struct hgic_hdr *hdr = NULL; + + //hgic_dbg("Enter\n"); +_CTRLQ_TX: + while (!skb_queue_empty(&hg->ctrl.txq)) { + if (test_bit(HGIC_BUS_FLAGS_SLEEP, &hg->bus->flags) || + !test_bit(HGICF_DEV_FLAGS_RUNNING, &hg->flags)) { + hgic_clear_queue(&hg->ctrl.txq); + goto _CTRLQ_TX; + } + + if (hgicf_check_txwnd(hg)) { + msleep(10); + continue; + } + + skb = skb_dequeue(&hg->ctrl.txq); + if(skb){ + hdr = (struct hgic_hdr *)skb->data; + switch(hdr->type){ + case HGIC_HDR_TYPE_BOOTDL: + break; + case HGIC_HDR_TYPE_BOOTDL_DATA: + skb_pull(skb, sizeof(struct hgic_bootdl_cmd_hdr)); + break; + default: + if(test_bit(HGIC_BUS_FLAGS_INBOOT, &hg->bus->flags)){ + kfree_skb(skb); + skb = NULL; + } + break; + } + } + + if(skb){ + hg->bus->tx_packet(hg->bus, skb); + } + } + + while (!skb_queue_empty(&hg->tx_dataq) && + test_bit(HGICF_DEV_FLAGS_RUNNING, &hg->flags) && + !test_bit(HGICF_DEV_FLAGS_SUSPEND, &hg->flags)) { + + if (test_bit(HGIC_BUS_FLAGS_SLEEP, &hg->bus->flags) || + test_bit(HGIC_BUS_FLAGS_INBOOT, &hg->bus->flags)) { + hgic_clear_queue(&hg->tx_dataq); + goto _CTRLQ_TX; + } + + if (hgicf_check_txwnd(hg)) { + msleep(10); + goto _CTRLQ_TX; + } + + skb = skb_dequeue(&hg->tx_dataq); + hg->bus->tx_packet(hg->bus, skb); + if (!skb_queue_empty(&hg->ctrl.txq)) { + goto _CTRLQ_TX; + } + } + //hgic_dbg("Leave\n"); +} + +static struct hgicf_vif *hgicf_create_iface(struct hgicf_wdev *hg) +{ + int ret = 0; + struct net_device *ndev = NULL; + struct hgicf_vif *vif = NULL; + + hgic_dbg("Enter\n"); + ndev = ALLOC_NETDEV_MQS(sizeof(struct hgicf_vif), ifname, hgicf_netif_setup, 1, 1); + if (!ndev) { + hgic_err("%s: alloc_netdev_mqs failed\n", __func__); + return NULL; + } + vif = (struct hgicf_vif *)netdev_priv(ndev); + vif->ndev = ndev; + vif->hg = hg; + vif->state = 0; + ndev->needed_headroom += (hg->bus->drv_tx_headroom + sizeof(struct hgic_frm_hdr2)); + memcpy(ndev->dev_addr, hg->fwinfo.mac, ETH_ALEN); + ret = register_netdev(ndev); + if (ret) { + free_netdev(ndev); + return NULL; + } +#ifndef __RTOS__ + rtnl_lock(); + DEV_OPEN(ndev); + rtnl_unlock(); +#endif + return vif; +} + +static int hgicf_rx_data(void *hgobj, struct sk_buff *skb, int len) +{ + int i = 0; + struct hgic_frm_hdr2 *hdr = NULL; + struct hgicf_wdev *hg = hgobj; + + hgic_skip_padding(skb); + hdr = (struct hgic_frm_hdr2 *) skb->data; + skb->dev = MATCH_NDEV(hdr->hdr.ifidx, hg); + if (hdr->hdr.magic != HGIC_HDR_RX_MAGIC) { + hgic_err("invalid magic unmber:%x\r\n", hdr->hdr.magic); + hgic_print_hex(skb->data, 16); + dev_kfree_skb_any(skb); + return -1; + } + + if (hdr->hdr.type != HGIC_HDR_TYPE_BOOTDL) { + if (len < hdr->hdr.length) { + hgic_err("invalid data length: %x/%x,cookie:%d\r\n", len, hdr->hdr.length, hdr->hdr.cookie); + dev_kfree_skb_any(skb); + return -1; + } + } + + skb_put(skb, len > hdr->hdr.length ? hdr->hdr.length : len); + switch (hdr->hdr.type) { + case HGIC_HDR_TYPE_FRM2: + hg->last_rx = jiffies; + if (skb->dev == NULL || hg->if_test) { + dev_kfree_skb_any(skb); + break; + } + skb_pull(skb, sizeof(struct hgic_frm_hdr2)); + skb->protocol = eth_type_trans(skb, skb->dev); + if (in_interrupt()) { + netif_rx(skb); + } else { + netif_rx_ni(skb); + } + break; + case HGIC_HDR_TYPE_CMD: + case HGIC_HDR_TYPE_CMD2: + case HGIC_HDR_TYPE_EVENT: + case HGIC_HDR_TYPE_EVENT2: + case HGIC_HDR_TYPE_OTA: + case HGIC_HDR_TYPE_BOOTDL: + if (hg->if_test) { + dev_kfree_skb_any(skb); + break; + } + hgic_fwctrl_rx(&hg->ctrl, skb); + break; + case HGIC_HDR_TYPE_TEST2: + hg->last_rx = jiffies; + hg->test_rx_len += len; + if (hg->if_test == 3) { + for (i = 8; i < 1500; i++) { + if (skb->data[i] != 0xAA) { + printk("data verify fail\r\n"); + break; + } + } + } + dev_kfree_skb(skb); + break; + case HGIC_HDR_TYPE_SOFTFC: + hg->last_rx = jiffies; + atomic_set(&hg->txwnd, hdr->hdr.cookie); + complete(&hg->txwnd_cp); + dev_kfree_skb_any(skb); + break; + default: + hgic_err("invalid data:%d\r\n", hdr->hdr.type); + dev_kfree_skb_any(skb); + break; + } + return 0; +} + +static void hgicf_tx_complete(void *hgobj, struct sk_buff *skb, int success) +{ + struct hgicf_wdev *hg = hgobj; + + if (hg->if_test) { + hg->test_tx_len += skb->len; + } + dev_kfree_skb_any(skb); + if (!success) { + hgic_err("tx failed\r\n"); + } +} + +static int hgicf_download_fw(struct hgicf_wdev *hg) +{ + int err = -1; + int retry = 10; + int status = -1; + + hgic_dbg("Enter\n"); + if (no_bootdl || qc_mode) { status = STATE_FW; } + + set_bit(HGIC_BUS_FLAGS_INBOOT, &hg->bus->flags); + while (status != STATE_FW && retry-- > 0 && err) { + status = hgic_bootdl_cmd_enter(&hg->bootdl); + if (status == STATE_BOOT) { + err = hgic_bootdl_download(&hg->bootdl, fw_file); + } + } + clear_bit(HGIC_BUS_FLAGS_INBOOT, &hg->bus->flags); + + if (status == STATE_BOOT && !err) { + set_bit(HGICF_DEV_FLAGS_BOOTDL, &hg->flags); + if (hg->bus->reinit && test_bit(HGIC_BUS_FLAGS_NOPOLL, &hg->bus->flags)) { + msleep(100); + retry = 4; + if (test_bit(HGICF_DEV_FLAGS_RUNNING, &hg->flags) && !hg->bus->reinit(hg->bus)) { + while (retry-- > 0 && (STATE_FW != status)) { + status = hgic_bootdl_cmd_enter(&hg->bootdl); + } + } + } + } + + if(!no_bootdl && hg->bus->reinit){ + mod_timer(&hg->alive_tmr, jiffies + msecs_to_jiffies(HGIC_ALIVE_TIMER*4)); + } + return (status == STATE_FW); +} + +static void hgicf_delay_init(struct work_struct *work) +{ + int ret = 0; + u8 retry = 0; + int conn_stat = 0; + struct hgicf_wdev *hg = container_of(work, struct hgicf_wdev, delay_init); + + hgic_dbg("Enter\n"); + while(!test_bit(HGIC_BUS_FLAGS_SLEEP, &hg->bus->flags) && test_bit(HGICF_DEV_FLAGS_RUNNING, &hg->flags)){ + if((retry & 0xf) == 0){ + ret = hgicf_download_fw(hg); + if(ret) break; + } + msleep(10); + retry++; + } + + if (ret) { + clear_bit(HGICF_DEV_FLAGS_SUSPEND, &hg->flags); + hg->last_rx = jiffies; + hg->soft_fc = soft_fc; + conn_stat = hgic_fwctrl_get_conn_state(&hg->ctrl); +#ifndef __RTOS__ + hgic_fwctrl_get_dhcpc_result(&hg->ctrl, (u8 *)&hg->dhcpc, sizeof(hg->dhcpc)); +#endif + hgic_fwctrl_get_fwinfo(&hg->ctrl, &hg->fwinfo); + printk("hgic fw info:%d.%d.%d.%d, svn version:%d, %pM, conn_state:%d\r\n", + (hg->fwinfo.version >> 24) & 0xff, (hg->fwinfo.version >> 16) & 0xff, + (hg->fwinfo.version >> 8) & 0xff, (hg->fwinfo.version & 0xff), + hg->fwinfo.svn_version, hg->fwinfo.mac, conn_stat); + if (!test_bit(HGICF_DEV_FLAGS_INITED, &hg->flags)) { + hg->sta = hgicf_create_iface(hg); + if (!hg->sta) { + hgic_err("create iface fail, ret:%d\r\n", ret); + return; + } + hg->sta->fwifidx = HGIC_WDEV_ID_STA; + hg->sta->hw_state = conn_stat; + hgicf_create_procfs(hg); + if (init_cb) { init_cb(0); } + if (if_test) { + hg->if_test = if_test; + queue_work(hg->tx_wq, &hg->test_work); + } + set_bit(HGICF_DEV_FLAGS_INITED, &hg->flags); + } + if ((test_bit(HGICF_DEV_FLAGS_BOOTDL, &hg->flags) || conn_stat != HGICF_HW_CONNECTED) && !qc_mode) { + hgicf_load_config(hg); + } + } + hgic_dbg("Leave, ret=%d\n", ret); +} + +static void hgicf_alive_work(struct work_struct *work) +{ + int retry = 4; + int status = -1; + struct hgicf_wdev *hg = container_of(work, struct hgicf_wdev, alive_work); + + //hgic_dbg("SLEEP:%d\r\n", test_bit(HGIC_BUS_FLAGS_SLEEP, &hg->bus->flags)); + if(!test_bit(HGIC_BUS_FLAGS_SLEEP, &hg->bus->flags)){ + if(time_after(jiffies, hg->last_rx + msecs_to_jiffies(HGIC_ALIVE_TIMER))){ + //hgic_dbg("detect device status ...\r\n"); + while(retry-- > 0 && status != STATE_FW){ + status = hgic_bootdl_cmd_enter(&hg->bootdl); + } + if (status != STATE_FW) { + hgic_dbg("need reload firmware ...\r\n"); + hg->soft_fc = 0; + set_bit(HGICF_DEV_FLAGS_SUSPEND, &hg->flags); + hgic_clear_queue(&hg->ctrl.txq); + hgic_clear_queue(&hg->tx_dataq); + hg->bus->reinit(hg->bus); + schedule_work(&hg->delay_init); + } + } + } + //hgic_dbg("next detect after %d sec.\r\n", HGIC_ALIVE_TIMER); + mod_timer(&hg->alive_tmr, jiffies + msecs_to_jiffies(HGIC_ALIVE_TIMER)); +} + +#ifdef __RTOS__ +static void hgicf_alive_timer(unsigned long arg) +{ + struct hgicf_wdev *hg = (struct hgicf_wdev *) arg; + schedule_work(&hg->alive_work); +} +#else +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,14,0) +static void hgicf_alive_timer(struct timer_list *t) +{ + struct hgicf_wdev *hg = from_timer(hg, t, alive_tmr); + schedule_work(&hg->alive_work); +} +#else +static void hgicf_alive_timer(unsigned long arg) +{ + struct hgicf_wdev *hg = (struct hgicf_wdev *) arg; + schedule_work(&hg->alive_work); +} +#endif +#endif + +void hgic_schedule(struct hgic_fwctrl *ctrl) +{ + struct hgicf_wdev *hg = container_of(ctrl, struct hgicf_wdev, ctrl); + queue_work(hg->tx_wq, &hg->tx_work); +} + +void hgic_rx_fw_event(struct hgic_fwctrl *ctrl, struct sk_buff *skb) +{ + struct hgicf_wdev *hg = container_of(ctrl, struct hgicf_wdev, ctrl); + hgicf_rx_fw_event(hg, skb); +} + +int hgic_core_remove(void *hgobj) +{ + struct hgicf_wdev *hg = hgobj; + + if (hg) { + hgic_dbg("Enter\n"); + clear_bit(HGICF_DEV_FLAGS_RUNNING, &hg->flags); + + del_timer_sync(&hg->alive_tmr); + cancel_work_sync(&hg->alive_work); + cancel_work_sync(&hg->delay_init); + hgic_dbg(" trace ...\r\n"); + if (hg->ap) { + netif_stop_queue(hg->ap->ndev); + } + if (hg->sta) { + netif_stop_queue(hg->sta->ndev); + } + hgicf_delete_procfs(hg); + hgic_fwctrl_release(&hg->ctrl); + hgic_dbg(" trace ...\r\n"); + hgic_ota_release(&hg->ota); + hgic_dbg(" trace ...\r\n"); + hgic_bootdl_release(&hg->bootdl, 0); + hgic_dbg(" trace ...\r\n"); + //while (!test_bit(HGICF_DEV_FLAGS_STOP, &hg->flags)); + cancel_work_sync(&hg->tx_work); + hgic_dbg(" trace ...\r\n"); + cancel_work_sync(&hg->test_work); + hgic_dbg(" trace ... %p\r\n", hg->ap); + if (hg->ap) { + unregister_netdev(hg->ap->ndev); + } + hgic_dbg(" trace ... %p\r\n", hg->sta); + if (hg->sta) { + unregister_netdev(hg->sta->ndev); + } + hgic_dbg(" trace ...\r\n"); + hgic_clear_queue(&hg->tx_dataq); + hgic_dbg(" trace ...\r\n"); +#ifdef __RTOS__ + skb_queue_head_deinit(&hg->tx_dataq); +#else + hgic_clear_queue(&hg->custmgmt_q); + hgic_clear_queue(&hg->dbginfo_q); + hgic_clear_queue(&hg->cust_driverdata_q); +#endif + hgic_dbg(" trace ...\r\n"); + if (hg->tx_wq) { + flush_workqueue(hg->tx_wq); + destroy_workqueue(hg->tx_wq); + } + hgic_dbg(" trace ...\r\n"); +#ifdef __RTOS__ + deinit_completion(&hg->txwnd_cp); + spin_lock_deinit(&hg->lock); +#endif + if(hg->paired_stas) + kfree(hg->paired_stas); + kfree(hg); + hgic_dbg("Leave\n"); + } + return 0; +} +EXPORT_SYMBOL(hgic_core_remove); + +int hgic_core_probe(void *dev, struct hgic_bus *bus) +{ + struct hgicf_wdev *hg = NULL; + + hgic_dbg("Enter,qc_mode=%d, no_bootdl=%d\n", qc_mode, no_bootdl); + hg = kzalloc(sizeof(struct hgicf_wdev), GFP_KERNEL); + if (hg) { + memset(hg, 0, sizeof(struct hgicf_wdev)); + hg->ctrl.qc_mode = qc_mode; + hg->bus = bus; + hg->dev = dev; + hg->dev_id = bus->dev_id; + hgic_fwctrl_init(&hg->ctrl, dev); + hgic_ota_init(&hg->ota, &hg->ctrl, &hg->fwinfo); + spin_lock_init(&hg->lock); + INIT_WORK(&hg->tx_work, hgicf_tx_work); + INIT_WORK(&hg->delay_init, hgicf_delay_init); + INIT_WORK(&hg->alive_work, hgicf_alive_work); + INIT_WORK(&hg->test_work, hgicf_test_work); + init_timer(&hg->alive_tmr); + setup_timer(&hg->alive_tmr, hgicf_alive_timer, (unsigned long)hg); + skb_queue_head_init(&hg->tx_dataq); + init_completion(&hg->txwnd_cp); + hgic_bootdl_init(&hg->bootdl, hg->bus, &hg->ctrl); +#ifndef __RTOS__ + skb_queue_head_init(&hg->custmgmt_q); + skb_queue_head_init(&hg->dbginfo_q); + skb_queue_head_init(&hg->cust_driverdata_q); + sema_init(&hg->evtq.sema, 0); +#endif + atomic_set(&hg->txwnd, TXWND_INIT_VAL); + bus->rx_packet = hgicf_rx_data; + bus->tx_complete = hgicf_tx_complete; + bus->bus_priv = hg; + hg->tx_wq = ALLOC_ORDERED_WORKQUEUE("hgicf_tx", 4096); + if (!hg->tx_wq) { + goto __failed; + } + set_bit(HGICF_DEV_FLAGS_RUNNING, &hg->flags); + hgic_dbg("ok\n"); + return 0; + } + +__failed: + bus->rx_packet = NULL; + bus->tx_complete = NULL; + bus->bus_priv = NULL; + hgic_core_remove(hg); + hgic_dbg("fail\n"); + return -1; +} +EXPORT_SYMBOL(hgic_core_probe); + +#ifdef CONFIG_PM +int hgic_core_suspend(void *hgobj) +{ + int err = 0; + struct hgicf_wdev *hg = (struct hgicf_wdev *)hgobj; + err = hgic_fwctrl_enter_sleep(&hg->ctrl, 1); + set_bit(HGIC_BUS_FLAGS_SLEEP, &hg->bus->flags); + return err; +} +EXPORT_SYMBOL(hgic_core_suspend); + +int hgic_core_resume(void *hgobj) +{ + int err = 0; + struct hgicf_wdev *hg = (struct hgicf_wdev *)hgobj; + clear_bit(HGIC_BUS_FLAGS_SLEEP, &hg->bus->flags); + if (hg->bus->reinit) { + hg->bus->reinit(hg->bus); + } + err = hgic_fwctrl_enter_sleep(&hg->ctrl, 0); + if(err) + hgic_err("exit sleep fail, ret=%d\r\n", err); + schedule_work(&hg->alive_work); + return err; +} +EXPORT_SYMBOL(hgic_core_resume); +#endif + +void hgic_core_probe_post(void *priv) +{ + struct hgicf_wdev *hg = (struct hgicf_wdev *)priv; + schedule_work(&hg->delay_init); +} +EXPORT_SYMBOL(hgic_core_probe_post); + +#ifdef __RTOS__ +int hgic_ifbus_reinit(const char *ifname) +{ + struct hgicf_vif *vif = NULL; + struct net_device *ndev = net_device_get_by_name(ifname); + if (ndev) { + vif = (struct hgicf_vif *)netdev_priv(ndev); + if (vif->hg->bus->reinit) { + return vif->hg->bus->reinit(vif->hg->bus); + } + } + return 0; +} +struct hgic_ota *hgic_devota(struct net_device *dev) +{ + struct hgicf_vif *vif = NULL; + + if (dev == NULL) { + return NULL; + } + vif = (struct hgicf_vif *)netdev_priv(dev); + return &(vif->hg->ota); +} +#endif + +int __init hgicf_init(void) +{ + VERSOIN_SHOW("fmac"); +#ifdef __RTOS__ + rcu_init(); + tasklet_core_init(); + net_device_init(); +#else +#ifdef CONFIG_HGIC_USBIN + hgic_usb_init(); +#endif +#ifdef CONFIG_HGIC_SDIOIN + hgic_sdio_init(); +#endif +#endif + hgic_leave(); + return 0; +} + +void __exit hgicf_exit(void) +{ +#ifndef __RTOS__ +#ifdef CONFIG_HGIC_USBIN + hgic_usb_exit(); +#endif +#ifdef CONFIG_HGIC_SDIOIN + hgic_sdio_exit(); +#endif +#endif + +#ifdef __RTOS__ + net_device_exit(); + tasklet_core_exit(); +#endif +} + +const char *hgic_param_ifname(const char *name) +{ + if (name) ifname = (char *)name; + return ifname; +} +#ifdef __RTOS__ +void hgic_param_iftest(int iftest) +{ + if_test = iftest; +} +char *hgic_param_fwfile(const char *fw) +{ + if (fw) fw_file = fw; + return fw_file; +} +int hgic_param_ifcount(int count) +{ + if (count) iface_cnt = count; + return iface_cnt; +} +void hgic_param_initcb(hgic_init_cb cb) +{ + init_cb = cb; +} +void hgic_param_eventcb(hgic_event_cb cb) +{ + event_cb = cb; +} +void hgic_param_bootdl(int enable) +{ + no_bootdl = !enable; +} +#endif + +void hgicf_event(struct hgicf_wdev *hg, char *name, int event, int param1, int param2) +{ +#ifdef __RTOS__ + if(event_cb) + event_cb(name, event, param1, param2); +#else + FWEVTQ_SET(&hg->evtq, event); + up(&hg->evtq.sema); +#endif +} + +module_init(hgicf_init); +module_exit(hgicf_exit); +module_param(txq_size, int, 0); +module_param(fw_file, charp, 0644); +module_param(if_test, int, 0); +module_param(soft_fc, int, 0); +module_param(ifname, charp, 0644); +module_param(conf_file, charp, 0644); +module_param(no_bootdl, int, 0); +module_param(qc_mode, int, 0); +MODULE_DESCRIPTION("HUGE-IC FullMAC Wireless Card Driver"); +MODULE_AUTHOR("Dongyun"); +MODULE_LICENSE("GPL"); + diff --git a/hgic_fmac/ctrl.c b/hgic_fmac/ctrl.c new file mode 100644 index 0000000..5fcd57a --- /dev/null +++ b/hgic_fmac/ctrl.c @@ -0,0 +1,70 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "hgicf.h" +#include "ctrl.h" +#include "cfg.h" +#include "event.h" + +/* cmd id, Odd : get (world access), even : set (root access) */ +#define HG_PRIV_IOCTL_GET (SIOCIWFIRSTPRIV + 0x01) +#define HG_PRIV_IOCTL_SET (SIOCIWFIRSTPRIV + 0x02) +#define HG_PRIV_IOCTL_STAT (SIOCIWFIRSTPRIV + 0x03) +#define HG_PRIV_IOCTL_SCAN (SIOCIWFIRSTPRIV + 0x04) +#define HG_PRIV_IOCTL_E2P (SIOCIWFIRSTPRIV + 0x05) +#define HG_PRIV_IOCTL_SAVE (SIOCIWFIRSTPRIV + 0x06) + +struct iw_priv_args hgicf_privtab[] = { + {HG_PRIV_IOCTL_SET , IW_PRIV_TYPE_CHAR | 1536, 0, "set"}, + {HG_PRIV_IOCTL_GET , IW_PRIV_TYPE_CHAR | 1024, IW_PRIV_TYPE_CHAR | 1024, "get"}, + {HG_PRIV_IOCTL_SCAN, IW_PRIV_TYPE_CHAR | 1536, 0, "scan"}, + {HG_PRIV_IOCTL_E2P , IW_PRIV_TYPE_CHAR | 1024, IW_PRIV_TYPE_CHAR | 1024, "e2p"}, + {HG_PRIV_IOCTL_STAT, 0, IW_PRIV_TYPE_CHAR | 1024, "stat"}, + {HG_PRIV_IOCTL_SAVE, IW_PRIV_TYPE_CHAR | 1024, 0, "save"}, +}; + +int hgicf_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +{ + int ret = 0; + struct iwreq *wrqin = (struct iwreq *) ifr; + + switch (cmd) { + case SIOCGIWPRIV: + if (wrqin->u.data.pointer) { + if (!ACCESS_OK(VERIFY_WRITE, wrqin->u.data.pointer, sizeof(hgicf_privtab))) { + break; + } + if ((sizeof(hgicf_privtab) / sizeof(hgicf_privtab[0])) <= wrqin->u.data.length) { + wrqin->u.data.length = sizeof(hgicf_privtab) / sizeof(hgicf_privtab[0]); + ret = copy_to_user(wrqin->u.data.pointer, hgicf_privtab, sizeof(hgicf_privtab)); + } + } + break; + case HG_PRIV_IOCTL_SET: + ret = hgicf_ioctl_set_proc(dev, wrqin); + break; + case HG_PRIV_IOCTL_GET: + ret = hgicf_ioctl_get_proc(dev, wrqin); + break; + case HG_PRIV_IOCTL_SCAN: + ret = hgicf_ioctl_scan(dev, wrqin); + break; + case HG_PRIV_IOCTL_E2P: + ret = hgicf_ioctl_e2p(dev, wrqin); + break; + case HG_PRIV_IOCTL_STAT: + ret = hgicf_ioctl_stat(dev, wrqin); + break; + case HG_PRIV_IOCTL_SAVE: + ret = hgicf_ioctl_savecfg(dev, wrqin); + break; + } + + return ret; +} + diff --git a/hgic_fmac/ctrl.h b/hgic_fmac/ctrl.h new file mode 100644 index 0000000..593cfe5 --- /dev/null +++ b/hgic_fmac/ctrl.h @@ -0,0 +1,11 @@ + +#ifndef _HGICF_CTRL_H_ +#define _HGICF_CTRL_H_ + +#ifdef __RTOS__ +int hgicf_ioctl(struct net_device *dev, u32 cmd, u32 param1, u32 param2); +#else +int hgicf_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd); +#endif +#endif + diff --git a/hgic_fmac/event.c b/hgic_fmac/event.c new file mode 100644 index 0000000..63ba73d --- /dev/null +++ b/hgic_fmac/event.c @@ -0,0 +1,196 @@ + +#ifdef __RTOS__ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#else +#include +#include +#include +#include +#include +#include +#include +#endif + +#include "hgicf.h" +#include "../utils/fwctrl.h" +#include "cfg.h" +#include "event.h" + +char *hgicf_hw_state(int state) +{ + switch (state) { + case HGICF_HW_DISCONNECTED: + return "Disconnect"; + case HGICF_HW_DISABLED: + return "DISABLED"; + case HGICF_HW_INACTIVE: + return "INACTIVE"; + case HGICF_HW_SCANNING: + return "SCANNING"; + case HGICF_HW_AUTHENTICATING: + return "AUTHENTICATING"; + case HGICF_HW_ASSOCIATING: + return "ASSOCIATING"; + case HGICF_HW_ASSOCIATED: + return "ASSOCIATED"; + case HGICF_HW_4WAY_HANDSHAKE: + return "4WAY_HANDSHAKE"; + case HGICF_HW_GROUP_HANDSHAKE: + return "GROUP_HANDSHAKE"; + case HGICF_HW_CONNECTED: + return "CONNECTED"; + default: + return "Unknown"; + } +} + +int hgicf_rx_fw_event(struct hgicf_wdev *hg, struct sk_buff *skb) +{ + char drop = 1; + char *data = NULL; + u32 evt_id = 0; + struct hgic_ctrl_hdr *evt = NULL; + struct hgicf_vif *vif = NULL; + + if (skb == NULL || skb->dev == NULL || skb->len < sizeof(struct hgic_ctrl_hdr)) { + dev_kfree_skb(skb); + return -1; + } + + vif = (struct hgicf_vif *)netdev_priv(skb->dev); + evt = (struct hgic_ctrl_hdr *)skb->data; + skb_pull(skb, sizeof(struct hgic_ctrl_hdr)); + data = skb->data; + evt_id = HDR_EVTID(evt); + + //hgic_dbg("event id:%d\r\n", evt->event.event_id); + switch (evt_id) { + case HGIC_EVENT_STATE_CHG: + vif->hw_state = data[0]; + switch (vif->hw_state) { + case HGICF_HW_CONNECTED: + memcpy(vif->hw_bssid, data + 1, ETH_ALEN); + break; + default: + break; + } + break; + case HGIC_EVENT_DISCONNECT_REASON: + memcpy(&vif->disconnect_reason, data, sizeof(int)); + break; + case HGIC_EVENT_ASSOC_STATUS: + memcpy(&vif->assoc_status_code, data, sizeof(int)); + break; + case HGIC_EVENT_SCANNING: + hgicf_event(hg, skb->dev->name, HGIC_EVENT_SCANNING, 0, 0); + break; + case HGIC_EVENT_SCAN_DONE: + hgicf_event(hg, skb->dev->name, HGIC_EVENT_SCAN_DONE, 0, 0); + break; + case HGIC_EVENT_TX_BITRATE: + hg->status.tx_bitrate = get_unaligned_le32((u8 *)(evt + 1)); + hgicf_event(hg, skb->dev->name, HGIC_EVENT_TX_BITRATE, hg->status.tx_bitrate, 0); + break; + case HGIC_EVENT_PAIR_START: + hg->status.pair_state = 0; + memset(hg->pairing_sta, 0, 6); + hgicf_event(hg, skb->dev->name, HGIC_EVENT_PAIR_START, 0, 0); + break; + case HGIC_EVENT_PAIR_SUCCESS: + hg->status.pair_state = 1; + if(skb->len >= 6) memcpy(hg->pairing_sta, data, 6); + hgicf_event(hg, skb->dev->name, HGIC_EVENT_PAIR_SUCCESS, (long)hg->pairing_sta, 0); + break; + case HGIC_EVENT_PAIR_DONE: + hg->paired_stas_cnt = (evt->hdr.length - sizeof(struct hgic_ctrl_hdr)) / 6; + if (hg->paired_stas) { + kfree(hg->paired_stas); + } + hg->paired_stas = kzalloc(evt->hdr.length, GFP_KERNEL); + if (hg->paired_stas) { + memcpy(hg->paired_stas, data, hg->paired_stas_cnt * 6); + } + hgicf_event(hg, skb->dev->name, HGIC_EVENT_PAIR_DONE, (long)hg->paired_stas, (long)hg->paired_stas_cnt); + break; + case HGIC_EVENT_CONECT_START: + vif->hw_state = HGICF_HW_ASSOCIATING; + hgicf_event(hg, skb->dev->name, HGIC_EVENT_CONECT_START, 0, 0); + break; + case HGIC_EVENT_CONECTED: + vif->hw_state = HGICF_HW_CONNECTED; + hgicf_event(hg, skb->dev->name, HGIC_EVENT_CONECTED, (long)skb->data, skb->len); + break; + case HGIC_EVENT_DISCONECTED: + vif->hw_state = HGICF_HW_DISCONNECTED; + hgicf_event(hg, skb->dev->name, HGIC_EVENT_DISCONECTED, (long)skb->data, skb->len); + break; + case HGIC_EVENT_SIGNAL: + hg->status.signal = (s8)data[0]; + hg->status.evm = (s8)data[1]; + hgicf_event(hg, skb->dev->name, HGIC_EVENT_SIGNAL, hg->status.signal, hg->status.evm); + break; + case HGIC_EVENT_FWDBG_INFO: + hgicf_event(hg, skb->dev->name, HGIC_EVENT_FWDBG_INFO, (long)skb->data, skb->len); +#ifndef __RTOS__ + printk("%s", skb->data); + if (skb_queue_len(&hg->dbginfo_q) > 4) { + kfree_skb(skb_dequeue(&hg->dbginfo_q)); + } + skb_queue_tail(&hg->dbginfo_q, skb); + drop = 0; +#endif + break; + case HGIC_EVENT_REQUEST_PARAM: + schedule_work(&hg->ctrl.flush_work); + break; + case HGIC_EVENT_CUSTOMER_MGMT: + hgic_dbg("rx mgmt from %pM, %d bytes\r\n", data, skb->len - 6); + hgicf_event(hg, skb->dev->name, HGIC_EVENT_CUSTOMER_MGMT, (long)skb->data, skb->len); +#ifndef __RTOS__ + if (skb_queue_len(&hg->custmgmt_q) > 16) { + kfree_skb(skb_dequeue(&hg->custmgmt_q)); + } + skb_queue_tail(&hg->custmgmt_q, skb); + drop = 0; +#endif + break; + case HGIC_EVENT_CUST_DRIVER_DATA: + hgicf_event(hg, skb->dev->name, HGIC_EVENT_CUST_DRIVER_DATA, (long)skb->data, skb->len); +#ifndef __RTOS__ + if (skb_queue_len(&hg->cust_driverdata_q) > 16) { + kfree_skb(skb_dequeue(&hg->cust_driverdata_q)); + } + skb_queue_tail(&hg->cust_driverdata_q, skb); + drop = 0; +#endif + break; + case HGIC_EVENT_DHCPC_DONE: +#ifndef __RTOS__ + memcpy(&hg->dhcpc, skb->data, sizeof(hg->dhcpc)); +#endif + hgicf_event(hg, skb->dev->name, HGIC_EVENT_DHCPC_DONE, (long)skb->data, skb->len); + break; + case HGIC_EVENT_CONNECT_FAIL: + hg->status.conntect_fail = data[0]; + hgicf_event(hg, skb->dev->name, HGIC_EVENT_CONNECT_FAIL, data[0], 0); + break; + case HGIC_EVENT_UNPAIR_STA: + hgic_dbg("unpair sta: %pM\r\n", data); + hgicf_event(hg, skb->dev->name, HGIC_EVENT_UNPAIR_STA, data, 0); + break; + default: + break; + } + if (drop) { dev_kfree_skb(skb); } + return 0; +} + diff --git a/hgic_fmac/event.h b/hgic_fmac/event.h new file mode 100644 index 0000000..48e6f9c --- /dev/null +++ b/hgic_fmac/event.h @@ -0,0 +1,9 @@ + +#ifndef _HGICF_EVENT_H_ +#define _HGICF_EVENT_H_ + +int hgicf_rx_fw_event(struct hgicf_wdev *hg, struct sk_buff *skb); +void hgicf_event(struct hgicf_wdev *hg, char *name, int event, int param1, int param2); + +#endif + diff --git a/hgic_fmac/hgicf.h b/hgic_fmac/hgicf.h new file mode 100644 index 0000000..8e23784 --- /dev/null +++ b/hgic_fmac/hgicf.h @@ -0,0 +1,156 @@ + +#ifndef _HGIC_FMAC_H_ +#define _HGIC_FMAC_H_ + +#ifndef __RTOS__ +#include +#endif +#include +#include +#include +#include "../hgic_def.h" +#include "../utils/utils.h" +#include "../utils/fwdl.h" +#include "../utils/fwctrl.h" +#include "procfs.h" +#include "../utils/ota.h" + +#define FWEVNTQ_SIZE (128) +enum hgicf_dev_flags { + HGICF_DEV_FLAGS_INITED, + HGICF_DEV_FLAGS_RUNNING, + HGICF_DEV_FLAGS_SUSPEND, + HGICF_DEV_FLAGS_BOOTDL, +}; + +enum hgicf_state { + HGICF_STATE_NONE = 0, + HGICF_STATE_START, + HGICF_STATE_FW_LD, + HGICF_STATE_STOP, + HGICF_STATE_PAUSE, + HGICF_STATE_QUEUE_STOPPED, +}; + +enum hgicf_hw_state { + HGICF_HW_DISCONNECTED, + HGICF_HW_DISABLED, + HGICF_HW_INACTIVE, + HGICF_HW_SCANNING, + HGICF_HW_AUTHENTICATING, + HGICF_HW_ASSOCIATING, + HGICF_HW_ASSOCIATED, + HGICF_HW_4WAY_HANDSHAKE, + HGICF_HW_GROUP_HANDSHAKE, + HGICF_HW_CONNECTED, +}; + +struct hgicf_wdev; + +struct hgicf_vif { + u8 fwifidx; + unsigned long state; + struct net_device *ndev; + struct hgicf_wdev *hg; + int hw_state; + char hw_ssid[32]; + char hw_bssid[ETH_ALEN]; + int disconnect_reason; + int assoc_status_code; +}; + +struct hgicf_status { + u32 tx_bitrate; + s8 signal; + s8 evm; + s8 pair_state; + u8 conntect_fail; +}; + +struct hgicf_wdev { + void *dev; + u32 dev_id; + unsigned long flags; + u16 data_cookie; + u16 ctrl_cookie; + u16 rx_cookie; + spinlock_t lock; + struct hgic_fw_info fwinfo; + struct hgic_bus *bus; + struct hgicf_vif *ap; + struct hgicf_vif *sta; + struct sk_buff_head tx_dataq; + struct work_struct tx_work; + struct work_struct delay_init; /*delay init work*/ + struct work_struct alive_work; + struct workqueue_struct *tx_wq; + struct delayed_work status_work; /*status print work*/ + struct hgic_fwctrl ctrl; + struct hgic_bootdl bootdl; + struct hgicf_procfs proc; + struct hgicf_status status; + struct timer_list alive_tmr; + unsigned long last_rx; + /*if test*/ + struct work_struct test_work; + u32 test_rx_len, test_tx_len; + u32 test_jiff, if_test; + /*soft fc*/ + int soft_fc; + atomic_t txwnd; + struct completion txwnd_cp; +#ifndef __RTOS__ + struct sk_buff_head custmgmt_q; + struct sk_buff_head dbginfo_q; + struct sk_buff_head cust_driverdata_q; +#endif + struct hgic_ota ota; + u8 pairing_sta[6]; + u8 *paired_stas; + u32 paired_stas_cnt; + u8 radio_off; +#ifndef __RTOS__ + struct { + u8 buf[FWEVNTQ_SIZE]; + u8 wpos, rpos; + struct semaphore sema; + }evtq; + struct { + u32 ipaddr, netmask, svrip, router, dns1, dns2; + }dhcpc; +#endif +}; + +struct hgicf_hw_status { + uint32_t rssi; + uint32_t per; +} __packed; + +extern void hgic_print_hex(char *buf, int len); +extern char *hgicf_hw_state(int state); +u16 hgicf_data_cookie(struct hgicf_wdev *hg); + +#ifndef __RTOS__ +#define FWEVTQ_NPOS(evtq,pos) ((((evtq)->pos)+1 >= FWEVNTQ_SIZE) ? (0) : (((evtq)->pos)+1)) +#define FWEVTQ_EMPTY(evtq) ((evtq)->wpos == (evtq)->rpos) +#define FWEVTQ_FULL(evtq) (FWEVTQ_NPOS(evtq, wpos) == (evtq)->rpos) +#define FWEVTQ_COUNT(evtq) (((evtq)->rpos <= (evtq)->wpos)?\ + ((evtq)->wpos - (evtq)->rpos):\ + (FWEVNTQ_SIZE-(evtq)->rpos + (evtq)->wpos)) + +#define FWEVTQ_GET(evtq, val) do{\ + if(!FWEVTQ_EMPTY(evtq)){\ + val = (evtq)->buf[(evtq)->rpos];\ + (evtq)->rpos = FWEVTQ_NPOS((evtq), rpos);\ + }\ + } while(0) + +#define FWEVTQ_SET(evtq, val) do{\ + if(FWEVTQ_FULL(evtq)){\ + (evtq)->rpos = FWEVTQ_NPOS((evtq), rpos);\ + }\ + (evtq)->buf[(evtq)->wpos] = val;\ + (evtq)->wpos = FWEVTQ_NPOS((evtq), wpos);\ + } while (0) +#endif +#endif diff --git a/hgic_fmac/procfs.c b/hgic_fmac/procfs.c new file mode 100644 index 0000000..b724e31 --- /dev/null +++ b/hgic_fmac/procfs.c @@ -0,0 +1,1204 @@ +#include +#include +#include +#include +#include + +#include "hgicf.h" +#include "cfg.h" + +static int hgicf_signal_show(struct seq_file *seq, void *v) +{ + struct hgicf_wdev *hg = (struct hgicf_wdev *)seq->private; + seq_printf(seq, "%d\r\n", hg->status.signal); + return 0; +} +static int hgicf_signal_open(struct inode *inode, struct file *file) +{ + return single_open(file, hgicf_signal_show, PDE_DATA(inode)); +} +static const struct proc_ops hgicf_pops_signal = { + .proc_open = hgicf_signal_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = single_release, +}; +///////////////////////////////////////////////////////////////////////////////////////////////// + +static int hgicf_evm_show(struct seq_file *seq, void *v) +{ + struct hgicf_wdev *hg = (struct hgicf_wdev *)seq->private; + seq_printf(seq, "%d\r\n", hg->status.evm); + return 0; +} +static int hgicf_evm_open(struct inode *inode, struct file *file) +{ + return single_open(file, hgicf_evm_show, PDE_DATA(inode)); +} +static const struct proc_ops hgicf_pops_evm = { + .proc_open = hgicf_evm_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = single_release, +}; +///////////////////////////////////////////////////////////////////////////////////////////////// +static int hgicf_conn_state_show(struct seq_file *seq, void *v) +{ + struct hgicf_wdev *hg = (struct hgicf_wdev *)seq->private; + if (hg->sta) { + hg->sta->hw_state = hgic_fwctrl_get_conn_state(&hg->ctrl); + seq_printf(seq, "%s\r\n", hgicf_hw_state(hg->sta->hw_state)); + } else { + seq_printf(seq, "CONNECTED\r\n"); + } + return 0; +} +static int hgicf_conn_state_open(struct inode *inode, struct file *file) +{ + return single_open(file, hgicf_conn_state_show, PDE_DATA(inode)); +} +static const struct proc_ops hgicf_pops_conn_state = { + .proc_open = hgicf_conn_state_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = single_release, +}; +///////////////////////////////////////////////////////////////////////////////////////////////// + + +static int hgicf_txrate_show(struct seq_file *seq, void *v) +{ + struct hgicf_wdev *hg = (struct hgicf_wdev *)seq->private; + seq_printf(seq, "%d\r\n", hg->status.tx_bitrate); + return 0; +} +static int hgicf_txrate_open(struct inode *inode, struct file *file) +{ + return single_open(file, hgicf_txrate_show, PDE_DATA(inode)); +} +static const struct proc_ops hgicf_pops_txrate = { + .proc_open = hgicf_txrate_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = single_release, +}; +///////////////////////////////////////////////////////////////////////////////////////////////// +static int hgicf_testmode_show(struct seq_file *seq, void *v) +{ + struct hgicf_wdev *hg = (struct hgicf_wdev *)seq->private; + seq_printf(seq, "%s\r\n", hg->proc.testmode_buf); + return 0; +} +static int hgicf_testmode_open(struct inode *inode, struct file *file) +{ + return single_open(file, hgicf_testmode_show, PDE_DATA(inode)); +} +static ssize_t hgicf_testmode_send_cmd(struct file *file, const char __user *buffer, + size_t count, loff_t *data) +{ + int ret = 0; + struct seq_file *seq = (struct seq_file *)file->private_data; + struct hgicf_wdev *hg = (struct hgicf_wdev *)seq->private; + + memset(hg->proc.testmode_buf, 0, HGIC_TESTMODE_BUFF_SIZE); + ret = copy_from_user(hg->proc.testmode_buf, buffer, count); + hgic_fwctrl_testmode_cmd(&hg->ctrl, hg->proc.testmode_buf, HGIC_TESTMODE_BUFF_SIZE); + return ret ? -EINVAL : count; +} +static const struct proc_ops hgicf_pops_testmode = { + .proc_open = hgicf_testmode_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_write = hgicf_testmode_send_cmd, + .proc_release = single_release, +}; +//////////////////////////////////////////////////////////////////////////////////////////// +static int hgicf_status_show(struct seq_file *seq, void *v) +{ + struct hgicf_wdev *hg = (struct hgicf_wdev *)seq->private; + seq_printf(seq, "fw info:%d.%d.%d.%d, svn version:%d, bootdl:%d\r\n", + (hg->fwinfo.version >> 24) & 0xff, (hg->fwinfo.version >> 16) & 0xff, + (hg->fwinfo.version >> 8) & 0xff, (hg->fwinfo.version & 0xff), + hg->fwinfo.svn_version, test_bit(HGICF_DEV_FLAGS_BOOTDL, &hg->flags)); + return 0; +} +static int hgicf_status_open(struct inode *inode, struct file *file) +{ + return single_open(file, hgicf_status_show, PDE_DATA(inode)); +} +static const struct proc_ops hgicf_pops_status = { + .proc_open = hgicf_status_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = single_release, +}; +/////////////////////////////////////////////////////////////////////////////////////////// +static int hgicf_ota_show(struct seq_file *seq, void *v) +{ + seq_printf(seq, "Enter OTA,SDK Version:20200402\r\n"); + return 0; +} + +static int hgicf_ota_open(struct inode *inode, struct file *file) +{ + return single_open(file, hgicf_ota_show, PDE_DATA(inode)); +} + +static ssize_t hgicf_ota_send_data(struct file *file, const char __user *buffer, + size_t count, loff_t *data) +{ + ssize_t ret = -1; + char *fw_path = NULL; + char *ptr = NULL; + struct seq_file *seq = (struct seq_file *)file->private_data; + struct hgicf_wdev *hg = (struct hgicf_wdev *)seq->private; + + if (file == NULL || buffer == NULL || seq == NULL || hg == NULL) { + hgic_dbg("%s: Input para error!\r\n", __FUNCTION__); + return -EINVAL; + } + + fw_path = kzalloc(count + 1, GFP_KERNEL); + if (!fw_path) { + return -ENOMEM; + } + + ret = copy_from_user(fw_path, buffer, count); + if (ret) { + hgic_dbg("%s: Error:copy_from_user:fw_path failed!\r\n", __FUNCTION__); + kfree(fw_path); + return -EFAULT; + } + + ptr = &fw_path[count - 1]; + while (*ptr == '\r' || *ptr == '\n') { *ptr-- = 0; } + + hgic_dbg("ota firmware: %s\r\n", fw_path); + ret = hgic_ota_send_fw(&hg->ota, fw_path, HG_OTA_WRITE_MEM_TMO); + kfree(fw_path); + return (ret ? -EIO : count); +} +static const struct proc_ops hgicf_pops_ota = { + .proc_open = hgicf_ota_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_write = hgicf_ota_send_data, + .proc_release = single_release, +}; + +/////////////////////////////////////////////////////////////////////////////////////////// +static int hgicf_temperature_show(struct seq_file *seq, void *v) +{ + struct hgicf_wdev *hg = (struct hgicf_wdev *)seq->private; + int temp = hgic_fwctrl_get_temperature(&hg->ctrl); + if (temp < 0) { temp = 0; } + seq_printf(seq, "%d\r\n", temp); + return 0; +} +static int hgicf_temperature_open(struct inode *inode, struct file *file) +{ + return single_open(file, hgicf_temperature_show, PDE_DATA(inode)); +} +static const struct proc_ops hgicf_pops_temperature = { + .proc_open = hgicf_temperature_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = single_release, +}; +///////////////////////////////////////////////////////////////////////////////////////////////// +static int hgicf_sleep_show(struct seq_file *seq, void *v) +{ + struct hgicf_wdev *hg = (struct hgicf_wdev *)seq->private; + seq_printf(seq, "%d\r\n", test_bit(HGIC_BUS_FLAGS_SLEEP, &hg->bus->flags)); + return 0; +} +static int hgicf_sleep_open(struct inode *inode, struct file *file) +{ + return single_open(file, hgicf_sleep_show, PDE_DATA(inode)); +} + +static ssize_t hgicf_enter_sleep(struct file *file, const char __user *buffer, + size_t count, loff_t *data) +{ + int err = 0; + struct seq_file *seq = (struct seq_file *)file->private_data; + struct hgicf_wdev *hg = (struct hgicf_wdev *)seq->private; + u8 str[2] = {0, 0}; + int sleep = 0; + + err = copy_from_user(str, buffer, 1); + sleep = simple_strtol(str, 0, 10); + hgic_dbg("enter sleep :%d ...\r\n", sleep); + if (sleep) { + if(!test_bit(HGIC_BUS_FLAGS_SLEEP, &hg->bus->flags)){ + err = hgic_fwctrl_enter_sleep(&hg->ctrl, 1); + if (!err) { + set_bit(HGIC_BUS_FLAGS_SLEEP, &hg->bus->flags); + }else{ + hgic_err("sleep fail, ret=%d\r\n", err); + } + } + } else { + if(test_bit(HGIC_BUS_FLAGS_SLEEP, &hg->bus->flags)){ + clear_bit(HGIC_BUS_FLAGS_SLEEP, &hg->bus->flags); + if (hg->bus->reinit) { + hg->bus->reinit(hg->bus); + } + err = hgic_fwctrl_enter_sleep(&hg->ctrl, 0); + if(err) hgic_err("exit sleep fail, ret=%d\r\n", err); + } + } + return count; +} +static const struct proc_ops hgicf_pops_sleep = { + .proc_open = hgicf_sleep_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_write = hgicf_enter_sleep, + .proc_release = single_release, +}; +/////////////////////////////////////////////////////////////////////////////////////////// +static int hgicf_iwpriv_show(struct seq_file *seq, void *v) +{ + struct hgicf_wdev *hg = (struct hgicf_wdev *)seq->private; + seq_printf(seq, "%s\r\n", hg->proc.iwpriv_buf); + memset(hg->proc.iwpriv_buf, 0, sizeof(hg->proc.iwpriv_buf)); + return 0; +} + +static int hgicf_iwpriv_open(struct inode *inode, struct file *file) +{ + return single_open(file, hgicf_iwpriv_show, PDE_DATA(inode)); +} + +static ssize_t hgicf_iwpriv_write(struct file *file, const char __user *buffer, + size_t count, loff_t *data) +{ + int ret = 0; + int i = 0; + struct seq_file *seq = (struct seq_file *)file->private_data; + struct hgicf_wdev *hg = (struct hgicf_wdev *)seq->private; + char *cmd_buf = kzalloc(count + 1, GFP_KERNEL); + char *ifname, *cmd, *args; + struct iwreq wrqin; + struct net_device *ndev = NULL; + + if (count <= 0) { return -1; } + memset(hg->proc.iwpriv_buf, 0, sizeof(hg->proc.iwpriv_buf)); + if (cmd_buf) { + i = count - 1; + ret = copy_from_user(cmd_buf, buffer, count); + while (i >= 0 && (cmd_buf[i] == '\r' || cmd_buf[i] == '\n')) { + cmd_buf[i--] = 0; + } + + ifname = cmd_buf; + cmd = strchr(ifname, ' '); + if (cmd == NULL) { + sprintf(hg->proc.iwpriv_buf, "invalid paramters:%s", cmd_buf); + goto __out; + } + *cmd++ = 0; + args = strchr(cmd, ' '); + if (args) { *args++ = 0; } + + //hgic_dbg("ifname:%s, cmd:%s, args:%s\r\n", ifname, cmd, args); + if (hg->sta && strcmp(hg->sta->ndev->name, ifname) == 0) { + ndev = hg->sta->ndev; + } else if (hg->ap && strcmp(hg->ap->ndev->name, ifname) == 0) { + ndev = hg->ap->ndev; + } else { + strcpy(hg->proc.iwpriv_buf, "no such device"); + goto __out; + } + memset(&wrqin, 0, sizeof(wrqin)); + if (strcasecmp(cmd, "set") == 0) { + wrqin.u.data.pointer = args; + wrqin.u.data.length = args ? strlen(args) : 0; + ret = hgicf_ioctl_set_proc(ndev, &wrqin); + if (ret) { + sprintf(hg->proc.iwpriv_buf, "%s error:%d", args, ret); + } else { + sprintf(hg->proc.iwpriv_buf, "%s OK", args); + } + } else if (strcasecmp(cmd, "get") == 0 && args) { + wrqin.u.data.pointer = hg->proc.iwpriv_buf; + wrqin.u.data.length = strlen(args); + strcpy(hg->proc.iwpriv_buf, args); + ret = hgicf_ioctl_get_proc(ndev, &wrqin); + } else if (strcasecmp(cmd, "scan") == 0) { + wrqin.u.data.pointer = hg->proc.iwpriv_buf; + ret = hgicf_ioctl_scan(ndev, &wrqin); + strcpy(hg->proc.iwpriv_buf, ret ? "Fail" : "OK"); + } else if (strcasecmp(cmd, "save") == 0) { + ret = hgicf_ioctl_savecfg(ndev, &wrqin); + strcpy(hg->proc.iwpriv_buf, ret ? "Fail" : "OK"); + } else if (strcasecmp(cmd, "stat") == 0) { + wrqin.u.data.pointer = hg->proc.iwpriv_buf; + ret = hgicf_ioctl_stat(ndev, &wrqin); + } else if (strcasecmp(cmd, "e2p") == 0) { + strcpy(hg->proc.iwpriv_buf, "not support now"); + } else { + strcpy(hg->proc.iwpriv_buf, "invalid parameters"); + } + } else { + strcpy(hg->proc.iwpriv_buf, "no memory"); + } + +__out: + if (cmd_buf) { + kfree(cmd_buf); + } + return ret ? -EINVAL : count; +} +static const struct proc_ops hgicf_pops_iwpriv = { + .proc_open = hgicf_iwpriv_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_write = hgicf_iwpriv_write, + .proc_release = single_release, +}; +/////////////////////////////////////////////////////////////////////////////////////////// +static int hgicf_paired_stas_show(struct seq_file *seq, void *v) +{ + int i = 0; + struct hgicf_wdev *hg = (struct hgicf_wdev *)seq->private; + if (hg->paired_stas && hg->paired_stas_cnt) { + for (i = 0; i < hg->paired_stas_cnt; i++) { + seq_printf(seq, "%pM", hg->paired_stas + i * 6); + } + } + return 0; +} +static int hgicf_paired_stas_open(struct inode *inode, struct file *file) +{ + return single_open(file, hgicf_paired_stas_show, PDE_DATA(inode)); +} +static const struct proc_ops hgicf_pops_paired_stas = { + .proc_open = hgicf_paired_stas_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = single_release, +}; +///////////////////////////////////////////////////////////////////////////////////////////////// +static int hgicf_radio_off_show(struct seq_file *seq, void *v) +{ + struct hgicf_wdev *hg = (struct hgicf_wdev *)seq->private; + seq_printf(seq, "Radio %s\r\n", hg->radio_off ? "OFF" : "ON"); + return 0; +} +static int hgicf_radio_off_open(struct inode *inode, struct file *file) +{ + return single_open(file, hgicf_radio_off_show, PDE_DATA(inode)); +} +static ssize_t hgicf_radio_off_config(struct file *file, const char __user *buffer, + size_t count, loff_t *data) +{ + int ret = 0; + struct seq_file *seq = (struct seq_file *)file->private_data; + struct hgicf_wdev *hg = (struct hgicf_wdev *)seq->private; + u8 str[2] = {0, 0}; + + ret = copy_from_user(str, buffer, 1); + if(ret) + return -EINVAL; + + hg->radio_off = simple_strtol(str, 0, 10); + hgic_dbg("radio %s \r\n", hg->radio_off ? "OFF" : "ON"); + if (hg->radio_off) { + hgic_fwctrl_close_dev(&hg->ctrl); + } else { + hgic_fwctrl_open_dev(&hg->ctrl); + } + return count; +} +static const struct proc_ops hgicf_pops_radio_off = { + .proc_open = hgicf_radio_off_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_write = hgicf_radio_off_config, + .proc_release = single_release, +}; +/////////////////////////////////////////////////////////////////////////////////////////// +static int hgicf_sta_count_show(struct seq_file *seq, void *v) +{ + struct hgicf_wdev *hg = (struct hgicf_wdev *)seq->private; + seq_printf(seq, "%d\r\n", hgic_fwctrl_get_sta_count(&hg->ctrl)); + return 0; +} +static int hgicf_sta_count_open(struct inode *inode, struct file *file) +{ + return single_open(file, hgicf_sta_count_show, PDE_DATA(inode)); +} +static const struct proc_ops hgicf_pops_sta_count = { + .proc_open = hgicf_sta_count_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = single_release, +}; +///////////////////////////////////////////////////////////////////////////////////////////////// +static int hgicf_ps_heartbeat_open(struct inode *inode, struct file *file) +{ + return single_open(file, NULL, PDE_DATA(inode)); +} +static ssize_t hgicf_ps_heartbeat_set(struct file *file, const char __user *buffer, + size_t count, loff_t *data) +{ + int ret = 0; + struct seq_file *seq = (struct seq_file *)file->private_data; + struct hgicf_wdev *hg = (struct hgicf_wdev *)seq->private; + u8 str[32]; + u32 ip[4] = {0}; + u32 port = 0; + u32 ipaddr = 0; + u32 period = 0; + u32 hb_tmo = 300; + + memset(str, 0, sizeof(str)); + ret = copy_from_user(str, buffer, count > 32 ? 32 : count); + if(ret) + return -EINVAL; + + sscanf(str, "%d.%d.%d.%d,%d,%d,%d", &ip[0], &ip[1], &ip[2], &ip[3], &port, &period, &hb_tmo); + ipaddr = ((ip[0] & 0xff) << 24) | ((ip[1] & 0xff) << 16) | (((ip[2] & 0xff) << 8)) | (ip[3] & 0xff); + hgic_fwctrl_set_ps_heartbeat(&hg->ctrl, ipaddr, port, period, hb_tmo); + return count; +} +static const struct proc_ops hgicf_pops_ps_heartbeat = { + .proc_open = hgicf_ps_heartbeat_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_write = hgicf_ps_heartbeat_set, + .proc_release = single_release, +}; +///////////////////////////////////////////////////////////////////////////////////////////////// +static int hgicf_ps_heartbeat_resp_open(struct inode *inode, struct file *file) +{ + return single_open(file, NULL, PDE_DATA(inode)); +} +static ssize_t hgicf_ps_heartbeat_resp_set(struct file *file, const char __user *buffer, + size_t count, loff_t *data) +{ + int ret = 0; + struct seq_file *seq = (struct seq_file *)file->private_data; + struct hgicf_wdev *hg = (struct hgicf_wdev *)seq->private; + u8 *ptr = kzalloc(count, GFP_KERNEL); + if (ptr) { + ret = copy_from_user(ptr, buffer, count); + if(!ret) + ret = hgic_fwctrl_set_ps_heartbeat_resp(&hg->ctrl, ptr, count); + kfree(ptr); + } + return ret ? -EINVAL: count; +} +static const struct proc_ops hgicf_pops_ps_heartbeat_resp = { + .proc_open = hgicf_ps_heartbeat_resp_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_write = hgicf_ps_heartbeat_resp_set, + .proc_release = single_release, +}; +///////////////////////////////////////////////////////////////////////////////////////////////// +static int hgicf_ps_wakeup_data_open(struct inode *inode, struct file *file) +{ + return single_open(file, NULL, PDE_DATA(inode)); +} +static ssize_t hgicf_ps_wakeup_data_set(struct file *file, const char __user *buffer, + size_t count, loff_t *data) +{ + int ret = 0; + struct seq_file *seq = (struct seq_file *)file->private_data; + struct hgicf_wdev *hg = (struct hgicf_wdev *)seq->private; + u8 *ptr = kzalloc(count, GFP_KERNEL); + if (ptr) { + ret = copy_from_user(ptr, buffer, count); + if(!ret){ + ret = hgic_fwctrl_set_ps_wakeup_data(&hg->ctrl, ptr, count); + } + kfree(ptr); + } + return ret ? -EINVAL: count; +} +static const struct proc_ops hgicf_pops_ps_wakeup_data = { + .proc_open = hgicf_ps_wakeup_data_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_write = hgicf_ps_wakeup_data_set, + .proc_release = single_release, +}; +///////////////////////////////////////////////////////////////////////////////////////////////// +static int hgicf_wakeup_open(struct inode *inode, struct file *file) +{ + return single_open(file, NULL, PDE_DATA(inode)); +} +static ssize_t hgicf_wakeup_sta(struct file *file, const char __user *buffer, + size_t count, loff_t *data) +{ + int ret = 0; + u8 addr[6]; + u8 addr_str[32]; + struct seq_file *seq = (struct seq_file *)file->private_data; + struct hgicf_wdev *hg = (struct hgicf_wdev *)seq->private; + + ret = copy_from_user(addr_str, buffer, count > 32 ? 32 : count); + if(ret) return -EINVAL; + if (!hgic_pick_macaddr(addr_str, addr)) { + memset(addr, 0, 6); + } + ret = hgic_fwctrl_wakeup_sta(&hg->ctrl, addr); + return ret ? -EINVAL : count; +} +static const struct proc_ops hgicf_pops_wakeup = { + .proc_open = hgicf_wakeup_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_write = hgicf_wakeup_sta, + .proc_release = single_release, +}; +/////////////////////////////////////////////////////////////////////////////////////////// + +static int hgicf_pair_state_show(struct seq_file *seq, void *v) +{ + struct hgicf_wdev *hg = (struct hgicf_wdev *)seq->private; + seq_printf(seq, "%d\r\n", hg->status.pair_state); + return 0; +} +static int hgicf_pair_state_open(struct inode *inode, struct file *file) +{ + return single_open(file, hgicf_pair_state_show, PDE_DATA(inode)); +} +static const struct proc_ops hgicf_pops_pair_state = { + .proc_open = hgicf_pair_state_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = single_release, +}; +/////////////////////////////////////////////////////////////////////////////////////////// + +static int hgicf_fw_ver_show(struct seq_file *seq, void *v) +{ + struct hgicf_wdev *hg = (struct hgicf_wdev *)seq->private; + seq_printf(seq, "%d.%d.%d.%d-%d\r\n", + (hg->fwinfo.version >> 24) & 0xff, (hg->fwinfo.version >> 16) & 0xff, + (hg->fwinfo.version >> 8) & 0xff, (hg->fwinfo.version & 0xff), + hg->fwinfo.svn_version); + return 0; +} +static int hgicf_fw_ver_open(struct inode *inode, struct file *file) +{ + return single_open(file, hgicf_fw_ver_show, PDE_DATA(inode)); +} +static const struct proc_ops hgicf_pops_fw_ver = { + .proc_open = hgicf_fw_ver_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = single_release, +}; +/////////////////////////////////////////////////////////////////////////////////////////// + +static int hgicf_wkreason_show(struct seq_file *seq, void *v) +{ + struct hgicf_wdev *hg = (struct hgicf_wdev *)seq->private; + int reason = hgic_fwctrl_get_wkreason(&hg->ctrl); + seq_printf(seq, "%d\r\n", reason); + return 0; +} +static int hgicf_wkreason_open(struct inode *inode, struct file *file) +{ + return single_open(file, hgicf_wkreason_show, PDE_DATA(inode)); +} +static const struct proc_ops hgicf_pops_wkreason = { + .proc_open = hgicf_wkreason_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = single_release, +}; +/////////////////////////////////////////////////////////////////////////////////////////// + +static int hgicf_fwevent_show(struct seq_file *seq, void *v) +{ + u8 event = 0; + struct hgicf_wdev *hg = (struct hgicf_wdev *)seq->private; + + if(down_timeout(&hg->evtq.sema, msecs_to_jiffies(100))) + return 0; + FWEVTQ_GET(&hg->evtq, event); + seq_printf(seq, "%d\r\n", event); + return 0; +} +static int hgicf_fwevent_open(struct inode *inode, struct file *file) +{ + return single_open(file, hgicf_fwevent_show, PDE_DATA(inode)); +} +static const struct proc_ops hgicf_pops_fwevent = { + .proc_open = hgicf_fwevent_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = single_release, +}; +///////////////////////////////////////////////////////////////////////////////////////////////// +static int hgicf_mgmt_open(struct inode *inode, struct file *file) +{ + return single_open(file, NULL, PDE_DATA(inode)); +} +static ssize_t hgicf_send_mgmt(struct file *file, const char __user *buffer, + size_t count, loff_t *data) +{ + int ret = 0; + u8 *buff; + struct seq_file *seq = (struct seq_file *)file->private_data; + struct hgicf_wdev *hg = (struct hgicf_wdev *)seq->private; + + buff = kzalloc(count+1, GFP_KERNEL); + if(buff == NULL) + return -ENOMEM; + ret = copy_from_user(buff, buffer, count); + if(!ret) + ret = hgic_fwctrl_send_cust_mgmt(&hg->ctrl, buff, count); + kfree(buff); + return ret ? -EINVAL : count; +} +static ssize_t hgicf_mgmt_read(struct file *file, char __user *buffer, + size_t count, loff_t *data) +{ + int ret = 0; + struct sk_buff *skb = NULL; + struct seq_file *seq = (struct seq_file *)file->private_data; + struct hgicf_wdev *hg = (struct hgicf_wdev *)seq->private; + + if(!skb_queue_empty(&hg->custmgmt_q)){ + skb = skb_dequeue(&hg->custmgmt_q); + if(skb){ + ret = copy_to_user(buffer, skb->data, skb->len); + kfree(skb); + } + } + return ret; +} + +static const struct proc_ops hgicf_pops_mgmt = { + .proc_open = hgicf_mgmt_open, + .proc_read = hgicf_mgmt_read, + .proc_lseek = seq_lseek, + .proc_write = hgicf_send_mgmt, + .proc_release = single_release, +}; +/////////////////////////////////////////////////////////////////////////////////////////// + +static int hgicf_battery_level_show(struct seq_file *seq, void *v) +{ + struct hgicf_wdev *hg = (struct hgicf_wdev *)seq->private; + int soc = hgic_fwctrl_get_battery_level(&hg->ctrl); + seq_printf(seq, "%d\r\n", soc); + return 0; +} +static int hgicf_battery_level_open(struct inode *inode, struct file *file) +{ + return single_open(file, hgicf_battery_level_show, PDE_DATA(inode)); +} +static const struct proc_ops hgicf_pops_battery_level = { + .proc_open = hgicf_battery_level_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = single_release, +}; +/////////////////////////////////////////////////////////////////////////////////////////// + +static int hgicf_fwdbginfo_show(struct seq_file *seq, void *v) +{ + struct hgicf_wdev *hg = (struct hgicf_wdev *)seq->private; + struct sk_buff *skb = skb_dequeue(&hg->dbginfo_q); + if(skb){ + seq_printf(seq, "%s", skb->data); + } + return 0; +} +static int hgicf_fwdbginfo_open(struct inode *inode, struct file *file) +{ + return single_open(file, hgicf_fwdbginfo_show, PDE_DATA(inode)); +} +static const struct proc_ops hgicf_pops_fwdbginfo = { + .proc_open = hgicf_fwdbginfo_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = single_release, +}; +///////////////////////////////////////////////////////////////////////////////////////////////// + +static int hgicf_pairing_sta_show(struct seq_file *seq, void *v) +{ + struct hgicf_wdev *hg = (struct hgicf_wdev *)seq->private; + seq_printf(seq, "%pM", hg->pairing_sta); + return 0; +} +static int hgicf_pairing_sta_open(struct inode *inode, struct file *file) +{ + return single_open(file, hgicf_pairing_sta_show, PDE_DATA(inode)); +} +static const struct proc_ops hgicf_pops_pairing_sta = { + .proc_open = hgicf_pairing_sta_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = single_release, +}; + +///////////////////////////////////////////////////////////////////////////////////////////////// +static int hgicf_dhcpc_open(struct inode *inode, struct file *file) +{ + return single_open(file, NULL, PDE_DATA(inode)); +} +static ssize_t hgicf_dhcpc_read(struct file *file, char __user *buffer, + size_t count, loff_t *data) +{ + struct seq_file *seq = (struct seq_file *)file->private_data; + struct hgicf_wdev *hg = (struct hgicf_wdev *)seq->private; + return copy_to_user(buffer, &hg->dhcpc, sizeof(hg->dhcpc)); +} +static const struct proc_ops hgicf_pops_dhcpc = { + .proc_open = hgicf_dhcpc_open, + .proc_read = hgicf_dhcpc_read, + .proc_lseek = seq_lseek, + .proc_release = single_release, +}; + +///////////////////////////////////////////////////////////////////////////////////////////////// +static int hgicf_wkdata_buff_open(struct inode *inode, struct file *file) +{ + return single_open(file, NULL, PDE_DATA(inode)); +} +static ssize_t hgicf_wkdata_buff_read(struct file *file, char __user *buffer, + size_t count, loff_t *data) +{ + int ret = 0; + struct seq_file *seq = (struct seq_file *)file->private_data; + struct hgicf_wdev *hg = (struct hgicf_wdev *)seq->private; + u8 *buff = (u8 *)kzalloc(1600, GFP_KERNEL); + + if(buff){ + ret = hgic_fwctrl_get_wkdata_buff(&hg->ctrl, buff, 1600); + if(ret > 0){ + ret = copy_to_user(buffer, buff, ret); + //printk("wkdata buff:%d\r\n", ret); + } + kfree(buff); + return ret; + } + return -ENOMEM; +} +static const struct proc_ops hgicf_pops_wkdata_buff = { + .proc_open = hgicf_wkdata_buff_open, + .proc_read = hgicf_wkdata_buff_read, + .proc_lseek = seq_lseek, + .proc_release = single_release, +}; + +///////////////////////////////////////////////////////////////////////////////////////////////// +static int hgicf_ps_wkdata_mask_open(struct inode *inode, struct file *file) +{ + return single_open(file, NULL, PDE_DATA(inode)); +} +static ssize_t hgicf_ps_wkdata_mask_write(struct file *file, const char __user *buffer, + size_t count, loff_t *data) +{ + int ret = 0; + struct seq_file *seq = (struct seq_file *)file->private_data; + struct hgicf_wdev *hg = (struct hgicf_wdev *)seq->private; + u16 buff[64]; + u8 len = count>128 ? 128 : count; + ret = copy_from_user((u8 *)buff, buffer, len); + if(!ret){ + ret = hgic_fwctrl_set_wkdata_mask(&hg->ctrl, buff[0], (u8 *)(buff+1), len - 2); + } + return ret ? -EINVAL : count; +} +static const struct proc_ops hgicf_pops_wkdata_mask = { + .proc_open = hgicf_ps_wkdata_mask_open, + .proc_write = hgicf_ps_wkdata_mask_write, + .proc_lseek = seq_lseek, + .proc_release = single_release, +}; + +///////////////////////////////////////////////////////////////////////////////////////////////// +static int hgicf_cust_driverdata_open(struct inode *inode, struct file *file) +{ + return single_open(file, NULL, PDE_DATA(inode)); +} +static ssize_t hgicf_cust_driverdata_write(struct file *file, const char __user *buffer, + size_t count, loff_t *data) +{ + int ret = 0; + u8 *buff; + struct seq_file *seq = (struct seq_file *)file->private_data; + struct hgicf_wdev *hg = (struct hgicf_wdev *)seq->private; + + if(count > 1500) count = 1500; + buff = kzalloc(count+1, GFP_KERNEL); + if(buff == NULL) + return -ENOMEM; + ret = copy_from_user(buff, buffer, count); + if(!ret) + ret = hgic_fwctrl_set_cust_driver_data(&hg->ctrl, buff, count); + kfree(buff); + return ret ? -EINVAL : count; +} +static ssize_t hgicf_cust_driverdata_read(struct file *file, char __user *buffer, + size_t count, loff_t *data) +{ + int ret = 0; + struct sk_buff *skb = NULL; + struct seq_file *seq = (struct seq_file *)file->private_data; + struct hgicf_wdev *hg = (struct hgicf_wdev *)seq->private; + + if(!skb_queue_empty(&hg->cust_driverdata_q)){ + skb = skb_dequeue(&hg->cust_driverdata_q); + if(skb){ + ret = copy_to_user(buffer, skb->data, skb->len); + kfree(skb); + } + } + return ret; +} + +static const struct proc_ops hgicf_pops_cust_driverdata = { + .proc_open = hgicf_cust_driverdata_open, + .proc_read = hgicf_cust_driverdata_read, + .proc_lseek = seq_lseek, + .proc_write = hgicf_cust_driverdata_write, + .proc_release = single_release, +}; +/////////////////////////////////////////////////////////////////////////////////////////// + +///////////////////////////////////////////////////////////////////////////////////////////////// +static int hgicf_freqinfo_open(struct inode *inode, struct file *file) +{ + return single_open(file, NULL, PDE_DATA(inode)); +} +static ssize_t hgicf_freqinfo_write(struct file *file, const char __user *buffer, + size_t count, loff_t *data) +{ + int ret = 0; + u8 *buff; + struct seq_file *seq = (struct seq_file *)file->private_data; + struct hgicf_wdev *hg = (struct hgicf_wdev *)seq->private; + + buff = kzalloc(count+1, GFP_KERNEL); + if(buff == NULL) + return -ENOMEM; + ret = copy_from_user(buff, buffer, count); + if(!ret) + ret = hgic_fwctrl_set_freqinfo(&hg->ctrl, buff, count); + kfree(buff); + return ret ? -EINVAL : count; +} + +static const struct proc_ops hgicf_pops_freqinfo = { + .proc_open = hgicf_freqinfo_open, + .proc_lseek = seq_lseek, + .proc_write = hgicf_freqinfo_write, + .proc_release = single_release, +}; +/////////////////////////////////////////////////////////////////////////////////////////// + +///////////////////////////////////////////////////////////////////////////////////////////////// +void hgicf_create_procfs(struct hgicf_wdev *hg) +{ + hgic_dbg("enter\r\n"); + hg->proc.rootdir = proc_mkdir("hgic", NULL); + if (hg->proc.rootdir == NULL) { + hgic_err("create proc dir: hgic failed\r\n"); + return; + } + + hg->proc.tx_bitrate = proc_create_data("tx_bitrate", 0x0444, + hg->proc.rootdir, &hgicf_pops_txrate, hg); + if (hg->proc.tx_bitrate == NULL) { + hgic_err("create proc file: tx_bitrate failed\r\n"); + } + + hg->proc.testmode = proc_create_data("testmode", 0x0666, + hg->proc.rootdir, &hgicf_pops_testmode, hg); + if (hg->proc.testmode == NULL) { + hgic_err("create proc file: testmode failed\r\n"); + } + + hg->proc.status = proc_create_data("status", 0x0444, + hg->proc.rootdir, &hgicf_pops_status, hg); + if (hg->proc.status == NULL) { + hgic_err("create proc file: status failed\r\n"); + } + + hg->proc.signal = proc_create_data("signal", 0x0444, + hg->proc.rootdir, &hgicf_pops_signal, hg); + if (hg->proc.signal == NULL) { + hgic_err("create proc file: signal failed\r\n"); + } + + hg->proc.evm = proc_create_data("evm", 0x0444, + hg->proc.rootdir, &hgicf_pops_evm, hg); + if (hg->proc.evm == NULL) { + hgic_err("create proc file: evm failed\r\n"); + } + + hg->proc.conn_state = proc_create_data("conn_state", 0x0444, + hg->proc.rootdir, &hgicf_pops_conn_state, hg); + if (hg->proc.conn_state == NULL) { + hgic_err("create proc file: conn_state failed\r\n"); + } + + hg->proc.ota = proc_create_data("ota", 0x0666, + hg->proc.rootdir, &hgicf_pops_ota, hg); + if (hg->proc.ota == NULL) { + hgic_err("create proc file: testmode failed\r\n"); + } + + hg->proc.temp = proc_create_data("temperature", 0x0444, + hg->proc.rootdir, &hgicf_pops_temperature, hg); + if (hg->proc.temp == NULL) { + hgic_err("create proc file: temperature failed\r\n"); + } + + hg->proc.sleep = proc_create_data("sleep", 0x0666, + hg->proc.rootdir, &hgicf_pops_sleep, hg); + if (hg->proc.sleep == NULL) { + hgic_err("create proc file: sleep failed\r\n"); + } + + hg->proc.iwpriv = proc_create_data("iwpriv", 0x0666, + hg->proc.rootdir, &hgicf_pops_iwpriv, hg); + if (hg->proc.iwpriv == NULL) { + hgic_err("create proc file: iwpriv failed\r\n"); + } + + hg->proc.paired_stas = proc_create_data("paired_stas", 0x0444, + hg->proc.rootdir, &hgicf_pops_paired_stas, hg); + if (hg->proc.paired_stas == NULL) { + hgic_err("create proc file: paired_stas failed\r\n"); + } + hg->proc.radio_off = proc_create_data("radio_off", 0x0666, + hg->proc.rootdir, &hgicf_pops_radio_off, hg); + if (hg->proc.radio_off == NULL) { + hgic_err("create proc file: radio_off failed\r\n"); + } + hg->proc.sta_count = proc_create_data("sta_count", 0x0444, + hg->proc.rootdir, &hgicf_pops_sta_count, hg); + if (hg->proc.sta_count == NULL) { + hgic_err("create proc file: sta_count failed\r\n"); + } + hg->proc.heartbeat = proc_create_data("heartbeat", 0x0666, + hg->proc.rootdir, &hgicf_pops_ps_heartbeat, hg); + if (hg->proc.heartbeat == NULL) { + hgic_err("create proc file: wakeup failed\r\n"); + } + hg->proc.heartbeat_resp = proc_create_data("heartbeat_resp", 0x0666, + hg->proc.rootdir, &hgicf_pops_ps_heartbeat_resp, hg); + if (hg->proc.heartbeat_resp == NULL) { + hgic_err("create proc file: wakeup failed\r\n"); + } + hg->proc.wakeup_data = proc_create_data("wakeup_data", 0x0666, + hg->proc.rootdir, &hgicf_pops_ps_wakeup_data, hg); + if (hg->proc.wakeup_data == NULL) { + hgic_err("create proc file: wakeup failed\r\n"); + } + hg->proc.wakeup = proc_create_data("wakeup", 0x0666, + hg->proc.rootdir, &hgicf_pops_wakeup, hg); + if (hg->proc.wakeup == NULL) { + hgic_err("create proc file: wakeup failed\r\n"); + } + hg->proc.pair_state = proc_create_data("pair_state", 0x0444, + hg->proc.rootdir, &hgicf_pops_pair_state, hg); + if (hg->proc.pair_state == NULL) { + hgic_err("create proc file: pair_state failed\r\n"); + } + hg->proc.fw_ver = proc_create_data("fw_ver", 0x0444, + hg->proc.rootdir, &hgicf_pops_fw_ver, hg); + if (hg->proc.fw_ver == NULL) { + hgic_err("create proc file: fw_ver failed\r\n"); + } + hg->proc.wkreason = proc_create_data("wkreason", 0x0444, + hg->proc.rootdir, &hgicf_pops_wkreason, hg); + if (hg->proc.wkreason == NULL) { + hgic_err("create proc file: wkreason failed\r\n"); + } + hg->proc.fwevent = proc_create_data("event", 0x0444, + hg->proc.rootdir, &hgicf_pops_fwevent, hg); + if (hg->proc.fwevent == NULL) { + hgic_err("create proc file: event failed\r\n"); + } + hg->proc.mgmt = proc_create_data("mgmt", 0x0666, + hg->proc.rootdir, &hgicf_pops_mgmt, hg); + if (hg->proc.mgmt == NULL) { + hgic_err("create proc file: mgmt failed\r\n"); + } + hg->proc.battery_level = proc_create_data("battery_level", 0x0444, + hg->proc.rootdir, &hgicf_pops_battery_level, hg); + if (hg->proc.battery_level == NULL) { + hgic_err("create proc file: battery_soc failed\r\n"); + } + hg->proc.fwdbginfo = proc_create_data("fwdbginfo", 0x0444, + hg->proc.rootdir, &hgicf_pops_fwdbginfo, hg); + if (hg->proc.fwdbginfo == NULL) { + hgic_err("create proc file: fwdbginfo failed\r\n"); + } + hg->proc.pairing_sta = proc_create_data("pairing_sta", 0x0444, + hg->proc.rootdir, &hgicf_pops_pairing_sta, hg); + if (hg->proc.pairing_sta == NULL) { + hgic_err("create proc file: pairing_sta failed\r\n"); + } + hg->proc.dhcpc = proc_create_data("dhcpc", 0x0444, + hg->proc.rootdir, &hgicf_pops_dhcpc, hg); + if (hg->proc.dhcpc == NULL) { + hgic_err("create proc file: dhcpc failed\r\n"); + } + hg->proc.wkdata_buff = proc_create_data("wkdata_buff", 0x0444, + hg->proc.rootdir, &hgicf_pops_wkdata_buff, hg); + if (hg->proc.wkdata_buff == NULL) { + hgic_err("create proc file: wkdata_buff failed\r\n"); + } + hg->proc.wkdata_mask = proc_create_data("wkdata_mask", 0x0666, + hg->proc.rootdir, &hgicf_pops_wkdata_mask, hg); + if (hg->proc.wkdata_mask == NULL) { + hgic_err("create proc file: wkdata_mask failed\r\n"); + } + hg->proc.cust_driverdata = proc_create_data("cust_driverdata", 0x0666, + hg->proc.rootdir, &hgicf_pops_cust_driverdata, hg); + if (hg->proc.cust_driverdata == NULL) { + hgic_err("create proc file: cust_driverdata failed\r\n"); + } + hg->proc.freqinfo = proc_create_data("freqinfo", 0x0444, + hg->proc.rootdir, &hgicf_pops_freqinfo, hg); + if (hg->proc.freqinfo == NULL) { + hgic_err("create proc file: freqinfo failed\r\n"); + } + hgic_dbg("leave\r\n"); +} + +void hgicf_delete_procfs(struct hgicf_wdev *hg) +{ + hgic_dbg("enter\r\n"); + if (hg->proc.rootdir) { + if (hg->proc.tx_bitrate) { + remove_proc_entry("tx_bitrate", hg->proc.rootdir); + hg->proc.tx_bitrate = NULL; + } + if (hg->proc.testmode) { + remove_proc_entry("testmode", hg->proc.rootdir); + hg->proc.testmode = NULL; + } + if (hg->proc.status) { + remove_proc_entry("status", hg->proc.rootdir); + hg->proc.status = NULL; + } + if (hg->proc.signal) { + remove_proc_entry("signal", hg->proc.rootdir); + hg->proc.signal = NULL; + } + if (hg->proc.evm) { + remove_proc_entry("evm", hg->proc.rootdir); + hg->proc.evm = NULL; + } + if (hg->proc.conn_state) { + remove_proc_entry("conn_state", hg->proc.rootdir); + hg->proc.conn_state = NULL; + } + if (hg->proc.ota) { + remove_proc_entry("ota", hg->proc.rootdir); + hg->proc.ota = NULL; + } + if (hg->proc.temp) { + remove_proc_entry("temperature", hg->proc.rootdir); + hg->proc.temp = NULL; + } + if (hg->proc.sleep) { + remove_proc_entry("sleep", hg->proc.rootdir); + hg->proc.sleep = NULL; + } + if (hg->proc.iwpriv) { + remove_proc_entry("iwpriv", hg->proc.rootdir); + hg->proc.iwpriv = NULL; + } + if (hg->proc.paired_stas) { + remove_proc_entry("paired_stas", hg->proc.rootdir); + hg->proc.paired_stas = NULL; + } + if (hg->proc.radio_off) { + remove_proc_entry("radio_off", hg->proc.rootdir); + hg->proc.radio_off = NULL; + } + if (hg->proc.sta_count) { + remove_proc_entry("sta_count", hg->proc.rootdir); + hg->proc.sta_count = NULL; + } + if (hg->proc.heartbeat) { + remove_proc_entry("heartbeat", hg->proc.rootdir); + hg->proc.heartbeat = NULL; + } + if (hg->proc.heartbeat_resp) { + remove_proc_entry("heartbeat_resp", hg->proc.rootdir); + hg->proc.heartbeat_resp = NULL; + } + if (hg->proc.wakeup_data) { + remove_proc_entry("wakeup_data", hg->proc.rootdir); + hg->proc.wakeup_data = NULL; + } + if (hg->proc.wakeup) { + remove_proc_entry("wakeup", hg->proc.rootdir); + hg->proc.wakeup = NULL; + } + if (hg->proc.pair_state) { + remove_proc_entry("pair_state", hg->proc.rootdir); + hg->proc.pair_state = NULL; + } + if (hg->proc.fw_ver) { + remove_proc_entry("fw_ver", hg->proc.rootdir); + hg->proc.fw_ver = NULL; + } + if (hg->proc.wkreason) { + remove_proc_entry("wkreason", hg->proc.rootdir); + hg->proc.wkreason = NULL; + } + if (hg->proc.fwevent) { + remove_proc_entry("event", hg->proc.rootdir); + hg->proc.fwevent = NULL; + } + if (hg->proc.mgmt) { + remove_proc_entry("mgmt", hg->proc.rootdir); + hg->proc.mgmt = NULL; + } + if (hg->proc.battery_level) { + remove_proc_entry("battery_level", hg->proc.rootdir); + hg->proc.battery_level = NULL; + } + if (hg->proc.fwdbginfo) { + remove_proc_entry("fwdbginfo", hg->proc.rootdir); + hg->proc.fwdbginfo = NULL; + } + if (hg->proc.pairing_sta) { + remove_proc_entry("pairing_sta", hg->proc.rootdir); + hg->proc.pairing_sta = NULL; + } + if (hg->proc.dhcpc) { + remove_proc_entry("dhcpc", hg->proc.rootdir); + hg->proc.dhcpc = NULL; + } + if (hg->proc.wkdata_buff) { + remove_proc_entry("wkdata_buff", hg->proc.rootdir); + hg->proc.wkdata_buff = NULL; + } + if (hg->proc.wkdata_mask) { + remove_proc_entry("wkdata_mask", hg->proc.rootdir); + hg->proc.wkdata_mask = NULL; + } + if (hg->proc.cust_driverdata) { + remove_proc_entry("cust_driverdata", hg->proc.rootdir); + hg->proc.cust_driverdata = NULL; + } + if (hg->proc.freqinfo) { + remove_proc_entry("freqinfo", hg->proc.rootdir); + hg->proc.freqinfo = NULL; + } + remove_proc_entry("hgic", NULL); + hg->proc.rootdir = NULL; + } + hgic_dbg("leave\r\n"); +} + diff --git a/hgic_fmac/procfs.h b/hgic_fmac/procfs.h new file mode 100644 index 0000000..544f060 --- /dev/null +++ b/hgic_fmac/procfs.h @@ -0,0 +1,71 @@ + +#ifndef _HGICF_PROCFS_H_ +#define _HGICF_PROCFS_H_ + +#ifdef __RTOS__ +struct hgicf_procfs {}; +#define hgicf_create_procfs(hg) +#define hgicf_delete_procfs(hg) + +#else + +#include +#include + +struct hgicf_wdev; + +#define HGIC_TESTMODE_BUFF_SIZE (4096) +struct hgicf_procfs { + struct proc_dir_entry *rootdir; + struct proc_dir_entry *tx_bitrate; + struct proc_dir_entry *testmode; + struct proc_dir_entry *status; + struct proc_dir_entry *signal; + struct proc_dir_entry *evm; + struct proc_dir_entry *conn_state; + u8 testmode_buf[HGIC_TESTMODE_BUFF_SIZE]; + struct proc_dir_entry *ota; + struct proc_dir_entry *temp; + struct proc_dir_entry *sleep; + struct proc_dir_entry *iwpriv; + u8 iwpriv_buf[4096]; + struct proc_dir_entry *paired_stas; + struct proc_dir_entry *radio_off; + struct proc_dir_entry *sta_count; + struct proc_dir_entry *wakeup; + struct proc_dir_entry *wakeup_data; + struct proc_dir_entry *heartbeat; + struct proc_dir_entry *heartbeat_resp; + struct proc_dir_entry *pair_state; + struct proc_dir_entry *fw_ver; + struct proc_dir_entry *wkreason; + struct proc_dir_entry *fwevent; + struct proc_dir_entry *mgmt; + struct proc_dir_entry *battery_level; + struct proc_dir_entry *fwdbginfo; + struct proc_dir_entry *pairing_sta; + struct proc_dir_entry *dhcpc; + struct proc_dir_entry *wkdata_buff; + struct proc_dir_entry *wkdata_mask; + struct proc_dir_entry *cust_driverdata; + struct proc_dir_entry *freqinfo; +}; + +void hgicf_create_procfs(struct hgicf_wdev *hg); +void hgicf_delete_procfs(struct hgicf_wdev *hg); + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,36) +static inline struct proc_dir_entry *PDE(const struct inode *inode) +{ + return PROC_I(inode)->pde; +} +static inline void *PDE_DATA(const struct inode *inode) +{ + return PDE(inode)->data; +} +#endif + +#endif + +#endif + diff --git a/hgicf.conf b/hgicf.conf new file mode 100644 index 0000000..cbb7bbd --- /dev/null +++ b/hgicf.conf @@ -0,0 +1,8 @@ +freq_range=9080,9240,8 +bss_bw=8 +tx_mcs=255 +chan_list= +key_mgmt=NONE +wpa_psk= +ssid=hgic_ah_test +mode=ap diff --git a/hgota/fwinfo.c b/hgota/fwinfo.c new file mode 100644 index 0000000..83a7656 --- /dev/null +++ b/hgota/fwinfo.c @@ -0,0 +1,343 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "libota.h" +#include "fwinfo.h" + +static void print_hex(char *data, int len) +{ + int i = 0; + + printf("\r\n"); + for(i=0;i> 1) ^ 0xA001; } // 0xA001 = reverse 0x8005 + else + { crc = (crc >> 1); } + } + } + return crc; +} + + +/****************************************************************************** +* Name: CRC-32 x32+x26+x23+x22+x16+x12+x11+x10+x8+x7+x5+x4+x2+x+1 +* Poly: 0x4C11DB7 +* Init: 0xFFFFFFF +* Refin: True +* Refout: True +* Xorout: 0xFFFFFFF +* Alias: CRC_32/ADCCP +* Use: WinRAR,ect. +*****************************************************************************/ +uint32 fwinfo_crc32(const uint8 *data, uint32 length) +{ + uint8 i; + uint32 crc = 0xffffffff; // Initial value + while (length--) { + crc ^= *data++; // crc ^= *data; data++; + for (i = 0; i < 8; ++i) { + if (crc & 1) { + crc = (crc >> 1) ^ 0xEDB88320;// 0xEDB88320= reverse 0x04C11DB7 + } else { + crc = (crc >> 1); + } + } + } + return ~crc; +} + + +int32 fwinfo_check_fwinfo_hdr(const uint8 *data) +{ + struct fwinfo_hdr *fw_hdr = NULL; + + if (data == NULL) { + fwinfo_err("%s:Input para error!\n", __FUNCTION__); + return -EINVAL; + } + + //print_hex(data, 64); + + fw_hdr = (struct fwinfo_hdr *)data; + if (fw_hdr->boot.boot_flag != FWINFO_BOOT_HDR) { + fwinfo_err("%s:Can not find boot header!\n", __FUNCTION__); + return -EINVAL;; + } + if (fw_hdr->fw_infor.func_code != FWINFO_CODE_HDR) { + fwinfo_err("%s:Can not find fw infor header %x!\n", __FUNCTION__, fw_hdr->fw_infor.func_code); + return -EINVAL;; + } + if (fw_hdr->spi_infor.func_code != FWINFO_SPI_HDR) { + fwinfo_err("%s:Can not find spi info header!\n", __FUNCTION__); + return -EINVAL; + } + return 0; +} + +uint16 fwinfo_get_fw_aes_en(const uint8 *data, int32 *err_code) +{ + struct fwinfo_hdr *fw_hdr = NULL; + int32 ret = 0; + + ret = fwinfo_check_fwinfo_hdr(data); + if (ret) { + fwinfo_err("%s:fwinfo_check_fwinfo_hdr error!\n", __FUNCTION__); + *err_code = ret; + return -1; + } + + fw_hdr = (struct fwinfo_hdr *)data; + fwinfo_dbg("%s:Check Para:aes_en:%d\n", __FUNCTION__, fw_hdr->boot.mode.aes_en); + *err_code = 0; + return fw_hdr->boot.mode.aes_en; +} + +uint16 fwinfo_get_fw_crc_en(const uint8 *data, int32 *err_code) +{ + struct fwinfo_hdr *fw_hdr = NULL; + int32 ret = 0; + + ret = fwinfo_check_fwinfo_hdr(data); + if (ret) { + fwinfo_err("%s:fwinfo_check_fwinfo_hdr error!\n", __FUNCTION__); + *err_code = ret; + return -1; + } + + fw_hdr = (struct fwinfo_hdr *)data; + fwinfo_dbg("%s:Check Para:crc_en:%d\n", __FUNCTION__, fw_hdr->boot.mode.crc_en); + *err_code = 0; + return fw_hdr->boot.mode.crc_en; +} + +uint32 fwinfo_get_fw_dl_addr(const uint8 *data, int32 *err_code) +{ + struct fwinfo_hdr *fw_hdr = NULL; + int32 ret = 0; + + ret = fwinfo_check_fwinfo_hdr(data); + if (ret) { + fwinfo_err("%s:fwinfo_check_fwinfo_hdr error!\n", __FUNCTION__); + *err_code = ret; + return -1; + } + + fw_hdr = (struct fwinfo_hdr *)data; + fwinfo_dbg("%s:Check Para:download addr:%x\n", __FUNCTION__, fw_hdr->boot.boot_to_sram_addr); + *err_code = 0; + return fw_hdr->boot.boot_to_sram_addr; +} + +uint32 fwinfo_get_fw_run_addr(const uint8 *data, int32 *err_code) +{ + struct fwinfo_hdr *fw_hdr = NULL; + int32 ret = 0; + + ret = fwinfo_check_fwinfo_hdr(data); + if (ret) { + fwinfo_err("%s:fwinfo_check_fwinfo_hdr error!\n", __FUNCTION__); + *err_code = ret; + return -1; + } + + fw_hdr = (struct fwinfo_hdr *)data; + fwinfo_dbg("%s:Check Para:run addr:%x\n", __FUNCTION__, fw_hdr->boot.run_sram_addr); + *err_code = 0; + return fw_hdr->boot.run_sram_addr; +} + +uint32 fwinfo_get_fw_code_offset(const uint8 *data, int32 *err_code) +{ + struct fwinfo_hdr *fw_hdr = NULL; + int32 ret = 0; + + ret = fwinfo_check_fwinfo_hdr(data); + if (ret) { + fwinfo_err("%s:fwinfo_check_fwinfo_hdr error!\n", __FUNCTION__); + *err_code = ret; + return -1; + } + + fw_hdr = (struct fwinfo_hdr *)data; + fwinfo_dbg("%s:Check Para:code offset:%d\n", __FUNCTION__, fw_hdr->boot.boot_code_offset_addr); + *err_code = 0; + return fw_hdr->boot.boot_code_offset_addr; +} + +uint32 fwinfo_get_fw_local_crc32(const uint8 *data, int32 *err_code) +{ + struct fwinfo_hdr *fw_hdr = NULL; + int32 ret = 0; + + ret = fwinfo_check_fwinfo_hdr(data); + if (ret) { + fwinfo_err("%s:fwinfo_check_fwinfo_hdr error!\n", __FUNCTION__); + *err_code = ret; + return -1; + } + + fw_hdr = (struct fwinfo_hdr *)data; + fwinfo_dbg("%s:Check Para:local crc:%x\n", __FUNCTION__, fw_hdr->fw_infor.code_crc32); + *err_code = 0; + return fw_hdr->fw_infor.code_crc32; +} + +uint32 fwinfo_get_fw_sdk_version(const uint8 *data, int32 *err_code) +{ + struct fwinfo_hdr *fw_hdr = NULL; + int32 ret = 0; + + ret = fwinfo_check_fwinfo_hdr(data); + if (ret) { + fwinfo_err("%s:fwinfo_check_fwinfo_hdr error!\n", __FUNCTION__); + *err_code = ret; + return -1; + } + + fw_hdr = (struct fwinfo_hdr *)data; + fwinfo_dbg("%s:Current file sdk info:%d.%d.%d.%d\r\n", __FUNCTION__, + (fw_hdr->fw_infor.sdk_version >> 24) & 0xff, (fw_hdr->fw_infor.sdk_version >> 16) & 0xff, + (fw_hdr->fw_infor.sdk_version >> 8) & 0xff, (fw_hdr->fw_infor.sdk_version & 0xff)); + *err_code = 0; + return fw_hdr->fw_infor.sdk_version; +} + +uint32 fwinfo_get_fw_svn_version(const uint8 *data, int32 *err_code) +{ + struct fwinfo_hdr *fw_hdr = NULL; + int32 ret = 0; + + ret = fwinfo_check_fwinfo_hdr(data); + if (ret) { + fwinfo_err("%s:fwinfo_check_fwinfo_hdr error!\n", __FUNCTION__); + *err_code = ret; + return -1; + } + + fw_hdr = (struct fwinfo_hdr *)data; + fwinfo_dbg("%s:Current file svn info:%d\n", __FUNCTION__, fw_hdr->fw_infor.svn_version); + *err_code = 0; + return fw_hdr->fw_infor.svn_version; +} + + +uint16 fwinfo_get_fw_chipid(const uint8 *data, int32 *err_code) +{ + struct fwinfo_hdr *fw_hdr = NULL; + int32 ret = 0; + + ret = fwinfo_check_fwinfo_hdr(data); + if (ret) { + fwinfo_err("%s:fwinfo_check_fwinfo_hdr error!\n", __FUNCTION__); + *err_code = ret; + return -1; + } + + fw_hdr = (struct fwinfo_hdr *)data; + fwinfo_dbg("%s:Check Para:fw chipid:%x\n", __FUNCTION__, fw_hdr->fw_infor.chip_id); + *err_code = 0; + return fw_hdr->fw_infor.chip_id; +} + +uint8 fwinfo_get_fw_cpuid(const uint8 *data, int32 *err_code) +{ + struct fwinfo_hdr *fw_hdr = NULL; + int32 ret = 0; + + ret = fwinfo_check_fwinfo_hdr(data); + if (ret) { + fwinfo_err("%s:fwinfo_check_fwinfo_hdr error!\n", __FUNCTION__); + *err_code = ret; + return -1; + } + + fw_hdr = (struct fwinfo_hdr *)data; + fwinfo_dbg("%s:Check Para:cpu_id:%d\n", __FUNCTION__, fw_hdr->fw_infor.cpu_id); + *err_code = 0; + return fw_hdr->fw_infor.cpu_id; +} + +uint32 fwinfo_get_fw_length(const uint8 *data, int32 *err_code) +{ + int32 ret = 0; + struct fwinfo_hdr *fw_hdr = (struct fwinfo_hdr *)data; + + ret = fwinfo_check_fwinfo_hdr(data); + if (ret) { + fwinfo_err("%s:fwinfo_check_fwinfo_hdr error!\n", __FUNCTION__); + *err_code = ret; + return -1; + } + + *err_code = 0; + return fw_hdr->boot.boot_from_flash_len + fw_hdr->boot.boot_code_offset_addr; +} + +int32 fwinfo_get_fw_code_checksum(const uint8 *data, int32 len) +{ + uint32 local_chksum = 0; + uint32 cur_crc = 0; + int32 err_code = 0; + uint32 code_offset = 0; + const uint8 *code_data = NULL; + int32 code_len = 0; + + local_chksum = fwinfo_get_fw_local_crc32(data, &err_code); + if (err_code != 0) { + fwinfo_err("%s:fwinfo_get_fw_local_crc32 error!\n", __FUNCTION__); + return err_code; + } + + code_offset = fwinfo_get_fw_code_offset(data, &err_code); + if (err_code != 0) { + fwinfo_err("%s:fwinfo_get_fw_code_offset error!\n", __FUNCTION__); + return err_code; + } + + code_data = data + code_offset; + code_len = len - code_offset; + + if (code_len < 0) { + fwinfo_err("%s:Input para error!\n", __FUNCTION__); + return -EINVAL; + } + + cur_crc = fwinfo_crc32(code_data, code_len); + if (cur_crc != local_chksum && ~cur_crc != local_chksum) { + fwinfo_err("%s:Check crc32 with hdr crc error,local crc:%x,cur_crc:%x\r\n", + __FUNCTION__, local_chksum, cur_crc); + return -EFAULT; + } + fwinfo_dbg("%s:Checksum Success!\n", __FUNCTION__); + return 0; +} + diff --git a/hgota/fwinfo.h b/hgota/fwinfo.h new file mode 100644 index 0000000..a0abd69 --- /dev/null +++ b/hgota/fwinfo.h @@ -0,0 +1,111 @@ +#ifndef _HGIC_FWINFO_H_ +#define _HGIC_FWINFO_H_ + +#define fwinfo_dbg(fmt, ...) //printf("%s:%d::"fmt, __FUNCTION__, __LINE__, ##__VA_ARGS__) +#define fwinfo_err(fmt, ...) printf("%s:%d::"fmt, __FUNCTION__, __LINE__, ##__VA_ARGS__) + +#define FWINFO_BOOT_HDR 0x5a69 +#define FWINFO_SPI_HDR 0x1 +#define FWINFO_CODE_HDR 0x2 + +struct fwinfo_spiflash_header_boot { + uint16 boot_flag; /* 0 : 0x5a69, header boot flag */ + uint8 version; /* 2 : version */ + uint8 size; /* 3 : Link to Next Header */ + uint32 boot_to_sram_addr; /* 4 : spi data load to sram addr */ + uint32 run_sram_addr; /* 8 : code execute start addr */ + uint32 boot_code_offset_addr; /* 12 : HeaderLen+ParamLen=4096+512 */ + uint32 boot_from_flash_len; /* 16 : */ + uint16 boot_data_crc; /* 20 : boot data crc check */ + uint16 flash_blk_size; /* 22 : flash size in 64KB(version > 1), 512B(version== 0) */ + uint16 boot_baud_mhz : 14, /* 24 : spi clk freq in mhz(version > 1), khz(version== 0) */ + driver_strength : 2; /* io driver strength */ + + struct { + uint16 pll_src : 8, /* pll src in Mhz */ + pll_en : 1, /* PLL enable */ + debug : 1, /* debug info uart output enable */ + aes_en : 1, /* AES enable */ + crc_en : 1, /* CRC check enable */ + reserved : 4; + } mode; /* 26 : boot option */ + uint16 reserved; /* 28 : */ + uint16 head_crc16; /* (size+4) byte CRC16 check value */ +}; + +/* format : cmd + dummys_flags + dat_len + dat */ +struct fwinfo_spiflash_spec_sequnce { + uint8 cmd_nums; + uint8 cmd; + uint8 dummy; + uint8 dat_len; + uint8 dat; +}; + +#pragma pack(1) +struct fwinfo_spiflash_read_cfg { + uint8 read_cmd; /* read_cmd */ + uint8 cmd_dummy_cycles : 4, /* read dummy cycles */ + clock_mode : 2, /* clock polarity & phase */ + spec_sequnce_en : 1, /* spec sequnce to execute, maybe same with quad_wire_en */ + quad_wire_en : 1; /* spi D2/D3 enable */ + + uint8 wire_mode_cmd : 2, + wire_mode_addr : 2, + wire_mode_data : 2, + quad_wire_select : 2; /* spi D2/D3 group select */ + + uint8 reserved3; + + uint16 sample_delay; /* RX sample dalay time: 0 ~ clk_divor */ +}; + +struct fwinfo_spiflash_header_spi_info { + uint8 func_code; /* 0 : header function(0x1) */ + uint8 size; /* 1: Link to Next Header */ + + struct fwinfo_spiflash_read_cfg read_cfg; + uint8 fwinfo_spiflash_spec_sequnce[64]; + + uint16 header_crc16; /* (size+2) byte CRC16 check value */ +}; + +/* fwinfo_ah_fw_v1.0.1.1_2020.2.20.bin ?*/ + +struct fwinfo_spiflash_header_firmware_info { + uint8 func_code; /* 0 : header function(0x2) */ + uint8 size; /* 1: Link to Next Header */ + uint32 sdk_version; /* version */ + uint32 svn_version; + uint32 date; /* date */ + uint16 chip_id; /* chip_id */ + uint8 cpu_id; /* cpu id, fix 0 */ + uint32 code_crc32; /* code CRC32 */ + uint16 param_crc16; /* param CRC16 */ + uint16 crc16; /* (size+2) byte CRC16 check value */ +}; + +struct fwinfo_hdr { + struct fwinfo_spiflash_header_boot boot; + struct fwinfo_spiflash_header_spi_info spi_infor; /* func1*/ + struct fwinfo_spiflash_header_firmware_info fw_infor ; /* func2*/ +}; + +#pragma pack() +uint16 fwinfo_crc16(const uint8 *data, uint32 length); +uint32 fwinfo_crc32(const uint8 *data, uint32 length); +uint16 fwinfo_get_fw_aes_en(const uint8 *data, int32 *err_code); +uint16 fwinfo_get_fw_crc_en(const uint8 *data, int32 *err_code); +uint32 fwinfo_get_fw_dl_addr(const uint8 *data, int32 *err_code); +uint32 fwinfo_get_fw_run_addr(const uint8 *data, int32 *err_code); +uint32 fwinfo_get_fw_code_offset(const uint8 *data, int32 *err_code); +uint32 fwinfo_get_fw_local_crc32(const uint8 *data, int32 *err_code); +uint32 fwinfo_get_fw_sdk_version(const uint8 *data, int32 *err_code); +uint32 fwinfo_get_fw_svn_version(const uint8 *data, int32 *err_code); +uint16 fwinfo_get_fw_chipid(const uint8 *data, int32 *err_code); +uint8 fwinfo_get_fw_cpuid(const uint8 *data, int32 *err_code); +int32 fwinfo_get_fw_code_checksum(const uint8 *data, int32 len); +uint32 fwinfo_get_fw_lenght(const uint8 *data, int32 *err_code); + + +#endif diff --git a/hgota/hgota.c b/hgota/hgota.c new file mode 100644 index 0000000..aff4698 --- /dev/null +++ b/hgota/hgota.c @@ -0,0 +1,349 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "libota.h" + +struct hgicOTA { + uint8 rxbuf[2048]; + uint8 mac_addr[6]; + int sock; + int if_index; +} hgOTA; + + +static void hgic_show_device(void) +{ + int i = 0; + printf("\r\n------------------------------------------------------------------\r\n"); + for (i = 0; i < OTA_STA_COUNT; i++) { + if (LIBOTA.stas[i].online) { + printf("| "MACSTR", chipid:%x, version:%d.%d.%d.%d-%d |\r\n", + MAC2STR(LIBOTA.stas[i].addr), + LIBOTA.stas[i].chipid, + (LIBOTA.stas[i].version >> 24) & 0xff, + (LIBOTA.stas[i].version >> 16) & 0xff, + (LIBOTA.stas[i].version >> 8) & 0xff, + (LIBOTA.stas[i].version) & 0xff, + LIBOTA.stas[i].svn_version); + } + } + printf("------------------------------------------------------------------\r\n"); +} + +static struct hgota_sta *hgic_get_device(u8 *addr) +{ + int i = 0; + for (i = 0; i < OTA_STA_COUNT; i++) { + if (memcmp(LIBOTA.stas[i].addr, addr, 6) ==0) { + return &LIBOTA.stas[i]; + } + } + return NULL; +} + + +static void hgic_save_device(void) +{ + FILE *fp = fopen("/tmp/hgota.tmp", "w+"); + if (fp) { + fwrite(LIBOTA.stas, 1, sizeof(LIBOTA.stas), fp); + fclose(fp); + printf("save scan result into /tmp/hgota.tmp\r\n"); + } +} + +static void hgic_read_device(void) +{ + int i = 0; + + FILE *fp = fopen("/tmp/hgota.tmp", "r"); + if (fp) { + fread(LIBOTA.stas, 1, sizeof(LIBOTA.stas), fp); + fclose(fp); + printf("load device info from /tmp/hgota.tmp\r\n"); + hgic_show_device(); + } +} + +static int hgic_sock_init(char *ifname) +{ + int ret = 0; + struct ifreq req; + + int sock = socket(PF_INET, SOCK_DGRAM, 0); + if (sock == -1) { + goto __fail; + } + memset(&req, 0, sizeof(struct ifreq)); + strncpy(req.ifr_name, ifname, strlen(ifname)); + ret = ioctl(sock, SIOCGIFINDEX, &req); + if (sock == -1) { + goto __fail; + } + hgOTA.if_index = req.ifr_ifindex; + + ret = ioctl(sock, SIOCGIFHWADDR, &req); + if (sock == -1) { + goto __fail; + } + memcpy(hgOTA.mac_addr, req.ifr_hwaddr.sa_data, 6); + close(sock); + + sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_OTA)); + hgOTA.sock = sock; + //printf("%d, %s: "MACSTR"\r\n", hgOTA.if_index, ifname, MAC2STR(hgOTA.mac_addr)); + return (sock != -1); +__fail: + if (sock != -1) { + close(sock); + } + return 0; +} + +static int hgic_sock_recv(int tmo) +{ + int ret = 0; + int recv_len = 0; + fd_set rfd; + struct timeval timeout; + + FD_ZERO(&rfd); + FD_SET(hgOTA.sock, &rfd); + timeout.tv_sec = tmo / 1000; + timeout.tv_usec = (tmo % 1000) * 1000; + ret = select(hgOTA.sock + 1, &rfd, NULL, NULL, &timeout); + if (ret > 0 && FD_ISSET(hgOTA.sock, &rfd)) { + recv_len = recvfrom(hgOTA.sock, hgOTA.rxbuf, 2048, 0, NULL, NULL); + } + return (recv_len); +} + +int raw_sock_send(char *data, int len) +{ + struct sockaddr_ll dest; + memset(&dest, 0, sizeof(struct sockaddr_ll)); + dest.sll_family = AF_PACKET; + dest.sll_protocol = htons(ETH_P_OTA); + dest.sll_ifindex = hgOTA.if_index; + dest.sll_pkttype = PACKET_OUTGOING; + dest.sll_halen = 6; + memcpy(dest.sll_addr, data, 6); + memcpy(data + 6, hgOTA.mac_addr, 6); + return sendto(hgOTA.sock, data, len, 0, (const struct sockaddr *)&dest, sizeof(struct sockaddr_ll)); +} + +void hgic_scan_device(void) +{ + int i = 0; + int recv_len = 0; + FILE *fp = NULL; + + libota_scan(1); + while (i++ < 10) { + if ((i & 0xf) == 0) { libota_scan(0); } + recv_len = hgic_sock_recv(50); + if (recv_len > 0) { + libota_rx_proc(hgOTA.rxbuf, recv_len); + } + } + hgic_save_device(); + printf("OTA SCAN:%d devices:\r\n", LIBOTA.sta_count); + hgic_show_device(); +} + +void hgic_reboot_device(char *addr) +{ + uint8 mac[6]; + printf("reboot sta %s\r\n", addr); + if (6 == sscanf(addr, "%02x:%02x:%02x:%02x:%02x:%02x", mac, mac + 1, mac + 2, mac + 3, mac + 4, mac + 5)) { + libota_reboot(mac, 0); + } +} + +void hgic_loaddef_device(char *addr) +{ + uint8 mac[6]; + + printf("load default sta %s config\r\n", addr); + if (6 == sscanf(addr, "%02x:%02x:%02x:%02x:%02x:%02x", mac, mac + 1, mac + 2, mac + 3, mac + 4, mac + 5)) { + libota_reboot(mac, ETH_P_REBOOT_FLAGS_LOADDEF); + } +} + +static uint8 *hgic_read_firmware(char *path, struct hgota_fw_info *fwinfo) +{ + int err = 0; + uint32 file_len = 0; + uint8 *fwdata = NULL; + uint32 rlen = 0; + FILE *fp = fopen(path, "r"); + + if (fp) { + fseek(fp, 0, SEEK_END); + file_len = ftell(fp); + fseek(fp, 0, SEEK_SET); + fwdata = malloc(file_len); + if (fwdata) { + rlen = fread(fwdata, 1, file_len, fp); + } + fclose(fp); + + if (file_len == rlen && fwdata) { + fwinfo->chipid = fwinfo_get_fw_chipid(fwdata, &err); + fwinfo->tot_len = fwinfo_get_fw_length(fwdata, &err); + fwinfo->version = fwinfo_get_fw_sdk_version(fwdata, &err); + fwinfo->svn_version = fwinfo_get_fw_svn_version(fwdata, &err); + if (!fwinfo_get_fw_code_checksum(fwdata, fwinfo->tot_len)) { + printf("firmware Info:\r\n"); + printf(" chipid:%x\r\n", fwinfo->chipid); + printf(" version:%d.%d.%d.%d-%d\r\n", (fwinfo->version >> 24) & 0xff, + (fwinfo->version >> 16) & 0xff, + (fwinfo->version >> 8) & 0xff, + (fwinfo->version) & 0xff, + fwinfo->svn_version); + printf(" size:%d\r\n", fwinfo->tot_len); + return fwdata; + } + } + printf("invalid firmware\r\n"); + } else { + printf("open file %s fail\r\n", path); + } + if (fwdata) { + free(fwdata); + } + return NULL; +} + +void hgic_ota_device(char *addr, char *fw) +{ + uint8 mac[6]; + uint32 ota_off = 0; + uint8 *fwdata = NULL; + uint32 rx_len = 0; + uint32 err = 0; + int32 retry = 0; + struct hgota_fw_info fwinfo; + struct hgota_sta *sta = NULL; + + printf("ota sta %s, fw:%s\r\n", addr, fw); + memset(&fwinfo, 0, sizeof(fwinfo)); + if (6 == sscanf(addr, "%02x:%02x:%02x:%02x:%02x:%02x", mac, mac + 1, mac + 2, mac + 3, mac + 4, mac + 5)) { + sta = hgic_get_device(mac); + fwdata = hgic_read_firmware(fw, &fwinfo); + if (fwdata) { + if(sta && sta->svn_version == fwinfo.svn_version){ + printf("same version, do not OTA\r\n"); + return; + } + while (ota_off < fwinfo.tot_len && retry < 10) { + libota_send_fw_data(mac, &fwinfo, ota_off, fwdata + ota_off, + (fwinfo.tot_len - ota_off) > 1460 ? 1460 : (fwinfo.tot_len - ota_off)); + rx_len = hgic_sock_recv(500); + if (rx_len > 0 && (libota_rx_proc(hgOTA.rxbuf, rx_len) == HGOTA_STATUS_FW_STATUS)) { + retry = 0; + } else { + retry++; + } + ota_off = libota_sta_nexoff(mac); + printf("OTA: %d%%\r", (ota_off * 100) / fwinfo.tot_len); + fflush(stdout); + } + free(fwdata); + } + } + printf("\r\n"); + if (fwinfo.tot_len > 0) { + printf("OTA Exit: %d%% upgraded\r\n", (ota_off * 100) / fwinfo.tot_len); + } +} + +void hgic_get_config(char *addr) +{ + int i = 0; + char mac[6]; + uint32 rx_len = 0; + struct eth_ota_fwparam param; + + if (6 == sscanf(addr, "%02x:%02x:%02x:%02x:%02x:%02x", mac, mac + 1, mac + 2, mac + 3, mac + 4, mac + 5)) { + libota_query_config(mac); + rx_len = hgic_sock_recv(500); + if (rx_len > 0) { + if (HGOTA_STATUS_CONFIG_GOT == libota_rx_proc(hgOTA.rxbuf, rx_len)) { + libota_sta_config(mac, ¶m); + printf("BSS BW :%d\r\n", param.bss_bw); + printf("TX MCS :%d\r\n", param.tx_mcs); + printf("ENCRYPT:%d\r\n", param.encrypt); + printf("FORWARD:%d\r\n", param.forward); + printf("SSID :%s\r\n", param.ssid); + printf("KEY_SET:%d\r\n", param.key_set); + printf("MACST_KEY_SET:%d\r\n", param.mkey_set); + printf("FREQ RANGE:%d - %d\r\n", param.freq_start, param.freq_end); + printf("CHAN LIST:"); + for (i = 0; i < param.chan_cnt; i++) { + if (param.chan_list[0]) { printf("%d ", param.chan_list[0]); } + } + printf("\r\n"); + } + } + } +} + +int main(int argc, char *argv[]) +{ + char *ifname; + char *cmd; + FILE *fp = NULL; + + if (argc < 3) { + printf("invalid params\r\n hgota ifname cmd [args]\r\n"); + return -1; + } + ifname = argv[1]; + cmd = argv[2]; + + libota_init(raw_sock_send); + hgic_read_device(); + + if (!hgic_sock_init(ifname)) { + printf("create socket fail\r\n"); + return -1; + } + + if (strcmp(cmd, "SCAN") == 0) { + hgic_scan_device(); + } else if (strcmp(cmd, "REBOOT") == 0) { + if (argc == 3 + 1) { + hgic_reboot_device(argv[3]); + } + } else if (strcmp(cmd, "LDDEF") == 0) { + if (argc == 3 + 1) { + hgic_loaddef_device(argv[3]); + } + } else if (strcmp(cmd, "OTA") == 0) { + if (argc == 3 + 2) { + hgic_ota_device(argv[3], argv[4]); + } + } else if (strcmp(cmd, "GETCFG") == 0) { + if (argc == 3 + 1) { + hgic_get_config(argv[3]); + } + } + close(hgOTA.sock); + return 0; +} + diff --git a/hgota/libota.c b/hgota/libota.c new file mode 100644 index 0000000..2dd945a --- /dev/null +++ b/hgota/libota.c @@ -0,0 +1,251 @@ + +#include +#include +#include +#include +#include "libota.h" + +//////////////////////////////////////////////////////////////////////////////////////// +struct libota LIBOTA; + +static uint16 libota_check_sum(int8 *addr, int32 count) +{ + int32 sum = 0; + while (count > 1) { + sum = sum + *(uint16 *)addr; + addr += 2; + count -= 2; + } + if (count > 0) { + sum += *addr; + } + while (sum >> 16) { + sum = (sum & 0xffff) + (sum >> 16); + } + return (uint16)~sum; +} + +static struct hgota_sta *libota_new_sta(void) +{ + int i = 0; + for (i = 0; i < OTA_STA_COUNT; i++) { + if (!LIBOTA.stas[i].online) { + return &LIBOTA.stas[i]; + } + } + return NULL; +} + +static struct hgota_sta *libota_find_sta(char *mac) +{ + int i = 0; + + if (mac == NULL) { + return NULL; + } + + for (i = 0; i < OTA_STA_COUNT; i++) { + if (memcmp(LIBOTA.stas[i].addr, mac, 6) == 0) { + return &LIBOTA.stas[i]; + } + } + return NULL; +} + +static int libota_add_sta(struct eth_ota_scan_report *report) +{ + struct hgota_sta *sta = libota_find_sta(report->hdr.src); + if (sta) { + sta->chipid = ntohs(report->chipid); + sta->version = ntohl(report->version); + sta->svn_version = ntohl(report->svn_version); + sta->online = 1; + return 1; + } + sta = libota_new_sta(); + if (sta) { + memset(sta, 0, sizeof(struct hgota_sta)); + sta->chipid = ntohs(report->chipid); + sta->version = ntohl(report->version); + sta->svn_version = ntohl(report->svn_version); + sta->online = 1; + memcpy(sta->addr, report->hdr.src, 6); + LIBOTA.sta_count++; + return 1; + } + return 0; +} + +static void libota_clear_sta(void) +{ + LIBOTA.sta_count = 0; + memset(LIBOTA.stas, 0, sizeof(LIBOTA.stas)); +} + +void libota_clear_sta_nexoff(char *sta_mac) +{ + struct hgota_sta *sta = libota_find_sta(sta_mac); + if (sta != NULL) { + sta->next_off = 0; + } +} + +int libota_sta_nexoff(char *sta_mac) +{ + struct hgota_sta *sta = libota_find_sta(sta_mac); + return sta ? sta->next_off : 0; +} + +int libota_reboot(char *sta_mac, int flags) +{ + struct eth_ota_reboot reboot; + + if (sta_mac) { + memset(&reboot, 0, sizeof(reboot)); + memcpy(reboot.hdr.dest, sta_mac, 6); + reboot.hdr.proto = htons(ETH_P_OTA); + reboot.hdr.stype = ETH_P_OTA_REBOOT; + reboot.flags = flags; + return LIBOTA.send(&reboot, sizeof(reboot)); + } + return -1; +} + +int libota_scan(int clear) +{ + struct eth_ota_hdr scan; + + if (clear) { + libota_clear_sta(); + } + memset(&scan, 0, sizeof(scan)); + memset(scan.dest, 0xff, 6); + scan.proto = htons(ETH_P_OTA); + scan.stype = ETH_P_OTA_SCAN; + return LIBOTA.send(&scan, sizeof(scan)); +} + +int libota_send_fw_data(char *sta_mac, struct hgota_fw_info *info, int off, char *data, int len) +{ + int ret = 0; + struct eth_ota_fw_data *fw = NULL; + struct hgota_sta *sta = libota_find_sta(sta_mac); + + if ((data == NULL) || (sta_mac == NULL) || (info == NULL)) { + return 0; + } + + if (sta) { sta->next_off = 0; } + fw = malloc(sizeof(struct eth_ota_fw_data) + len); + if (fw) { + memcpy(fw->hdr.dest, sta ? sta->addr : sta_mac, 6); + fw->hdr.proto = htons(ETH_P_OTA); + fw->hdr.stype = ETH_P_OTA_FW_DATA; + fw->hdr.status = 0; + fw->version = htonl(info->version); + fw->tot_len = htonl(info->tot_len); + fw->off = htonl(off); + fw->len = htons(len); + fw->chipid = htons(info->chipid); + fw->checksum = htons(libota_check_sum(data, len)); + memcpy(fw->data, data, len); + ret = LIBOTA.send(fw, sizeof(struct eth_ota_fw_data) + len); + free(fw); + } + return ret; +} + +int libota_query_config(char *sta_mac) +{ + struct eth_ota_hdr req; + memset(&req, 0, sizeof(req)); + memset(req.dest, 0xff, 6); + req.proto = htons(ETH_P_OTA); + req.stype = ETH_P_OTA_FW_GET_PARAM; + return LIBOTA.send(&req, sizeof(req)); +} + +int libota_update_config(char *sta_mac, struct eth_ota_fwparam *param) +{ + uint16 checksum = 0; + int data_len = sizeof(struct eth_ota_fwparam); + struct eth_ota_fwparam_hdr req; + + checksum = libota_check_sum((unsigned char *)param, data_len); + memset(&req, 0, sizeof(req)); + memcpy(req.hdr.dest, sta_mac, 6); + req.hdr.proto = htons(ETH_P_OTA); + req.hdr.stype = ETH_P_OTA_FW_SET_PARAM; + req.hdr.status = 0; + req.len = htons(data_len); + req.checksum = htons(checksum); + memcpy(&req.param, param, data_len); + return LIBOTA.send(&req, sizeof(req)); +} + +int libota_sta_config(char *sta_mac, struct eth_ota_fwparam *param) +{ + struct hgota_sta *sta = libota_find_sta(sta_mac); + if (sta) { + memcpy(param, &sta->param, sizeof(struct eth_ota_fwparam)); + return 1; + } + return 0; +} + +int libota_rx_proc(char *buff, int len) +{ + int ret = 0; + uint16 checksum = 0; + struct hgota_sta *sta = NULL; + struct eth_ota_hdr *hdr = buff; + struct eth_ota_fw_data *data = buff; + struct eth_ota_fwparam_hdr *param = buff; + + if (buff == NULL || len <= 0) { + return ret; + } + + sta = libota_find_sta(hdr->src); + if (ETH_P_OTA == ntohs(hdr->proto)) { + switch (hdr->stype) { + case ETH_P_OTA_SCAN_REPORT: + //printf("scan resp\r\n"); + if (libota_add_sta((struct eth_ota_scan_report *)buff)) { + ret = HGOTA_STATUS_NEW_STA; + } + break; + case ETH_P_OTA_FW_DATA_RESPONE: + //printf("fw data resp\r\n"); + if (sta && data->hdr.status != 0xff) { + sta->next_off = ntohl(data->off) + ntohs(data->len); + } + ret = HGOTA_STATUS_FW_STATUS; + break; + case ETH_P_OTA_FW_GET_PARAM_RESP: + checksum = libota_check_sum(¶m->param, sizeof(struct eth_ota_fwparam)); + //printf("get param, sta:"MACSTR", checksum:%x,%x\r\n", MAC2STR(hdr->src), checksum, ntohs(param->checksum)); + if (ntohs(param->checksum) == checksum && sta) { + memcpy(&sta->param, ¶m->param, sizeof(struct eth_ota_fwparam)); + ret = HGOTA_STATUS_CONFIG_GOT; + } + break; + case ETH_P_OTA_FW_SET_PARAM_RESP: + //printf("set param resp\r\n"); + if (sta && hdr->status != 0xff) { + ret = HGOTA_STATUS_CONFIG_UPDATED; + } + break; + default: + break; + } + } + return ret; +} + +int libota_init(raw_send_hdl send) +{ + memset(&LIBOTA, 0, sizeof(struct libota)); + LIBOTA.send = send; +} + diff --git a/hgota/libota.h b/hgota/libota.h new file mode 100644 index 0000000..b3e5446 --- /dev/null +++ b/hgota/libota.h @@ -0,0 +1,156 @@ +#ifndef _HGOTA_H_ +#define _HGOTA_H_ +#ifdef __cplusplus +extern "C" { +#endif + +typedef char s8; +typedef char int8; +typedef short s16; +typedef short int16; +typedef int s32; +typedef int int32; +typedef long long s64; +typedef long long int64; +typedef unsigned char u8; +typedef unsigned char uint8; +typedef unsigned short u16; +typedef unsigned short uint16; +typedef unsigned int u32; +typedef unsigned int uint32; +typedef unsigned long long u64; +typedef unsigned long long uint64; + +#define OTA_STA_COUNT (32) +#define ETH_P_OTA 0x4847 +#define IS_ETH_OTA(p) ((p)==0x4748) + +#ifndef MAC2STR +#define MAC2STR(a) (a)[0]&0xff, (a)[1]&0xff, (a)[2]&0xff, (a)[3]&0xff, (a)[4]&0xff, (a)[5]&0xff +#define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x" +#endif + +enum ETH_P_OTA_STYPE { + ETH_P_OTA_REBOOT = 1, + ETH_P_OTA_SCAN, + ETH_P_OTA_SCAN_REPORT, + ETH_P_OTA_FW_DATA, + ETH_P_OTA_FW_DATA_RESPONE, + ETH_P_OTA_FW_GET_PARAM, + ETH_P_OTA_FW_GET_PARAM_RESP, + ETH_P_OTA_FW_SET_PARAM, + ETH_P_OTA_FW_SET_PARAM_RESP, +}; + +enum HGIC_OTA_RESP_ERR_CODE{ + HGIC_OTA_RESP_ERR_OK=0, + HGIC_OTA_RESP_ERR_CHECKSUM, + HGIC_OTA_RESP_ERR_WRITE, +}; + +typedef int (*raw_send_hdl)(char *data, int len); + +struct eth_ota_hdr { + uint8 dest[6]; + uint8 src[6]; + uint16 proto; + uint8 stype, status; +}; + +struct eth_ota_scan_report { + struct eth_ota_hdr hdr; + uint32 version; + uint16 chipid; + uint8 mode, rev; + uint32 svn_version; +}; + +struct eth_ota_fw_data { + struct eth_ota_hdr hdr; + uint32 version; + uint32 off, tot_len; + uint16 len, checksum; + uint16 chipid; + uint8 data[0]; +}; + +struct hgota_fw_info { + u16 chipid; + u32 version; + u32 svn_version; + u32 tot_len; +}; + +struct eth_ota_fwparam{ + uint8 bss_bw; + uint8 encrypt: 1, + forward: 1, + key_set: 1, + mkey_set: 1; + uint8 chan_cnt; + uint16 freq_start, freq_end; + uint16 chan_list[16]; + uint8 ssid[32+2]; + uint8 key[32]; + uint8 mcast_key[32]; + uint8 tx_mcs; +}; + +struct eth_ota_fwparam_hdr{ + struct eth_ota_hdr hdr; + uint16 len, checksum; + struct eth_ota_fwparam param; +}; + +#define BIT(a) ((u16)1 << (a)) +enum ETH_P_REBOOT_FLAGS { + ETH_P_REBOOT_FLAGS_LOADDEF = BIT(0), +}; + +struct eth_ota_reboot { + struct eth_ota_hdr hdr; + uint32 flags; +}; + +struct hgota_sta { + char addr[6]; + int chipid; + int version; + int svn_version; + int online; + int next_off; + struct eth_ota_fwparam param; +}; +struct libota { + struct hgota_sta stas[OTA_STA_COUNT]; + int sta_count; + raw_send_hdl send; +}; + +enum{ + HGOTA_STATUS_NEW_STA = 0x1, + HGOTA_STATUS_FW_STATUS, + HGOTA_STATUS_CONFIG_GOT, + HGOTA_STATUS_CONFIG_UPDATED, +}; + +extern struct libota LIBOTA; + +void libota_clear_sta_nexoff(char *sta_mac); + +int libota_sta_nexoff(char *sta_mac); + +int libota_reboot(char *sta_mac, int flags); + +int libota_scan(int clear); + +int libota_send_fw_data(char *sta_mac, struct hgota_fw_info *info, int off, char *data, int len); + +int libota_rx_proc(char *buff, int len); + +int libota_init(raw_send_hdl send); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/hgtest b/hgtest new file mode 100755 index 0000000..c579ff2 --- /dev/null +++ b/hgtest @@ -0,0 +1,2 @@ +#!/bin/sh +echo $1 > /proc/hgic/testmode;cat /proc/hgic/testmode \ No newline at end of file diff --git a/iwpriv b/iwpriv new file mode 100755 index 0000000..8554366 --- /dev/null +++ b/iwpriv @@ -0,0 +1,2 @@ +#!/bin/sh +echo $* > /proc/hgic/iwpriv;cat /proc/hgic/iwpriv \ No newline at end of file diff --git a/readme.txt b/readme.txt new file mode 100644 index 0000000..7978640 --- /dev/null +++ b/readme.txt @@ -0,0 +1,34 @@ + +//////////////////////////////////////////////////////////////////////////////////////////////////// +fmac + 1.޸Makefile: ޸ıá + mtk 뻷£ + #MTK SDK + ARCH := mips + COMPILER := /opt/buildroot-gcc463/usr/bin/mipsel-linux- + LINUX_KERNEL_PATH := /home/dongyun/work/disk4/RT288x_AHv1.2/source/linux-3.10.14.x + + 2. ִ: make fmac + + + : + 1. error implicit declaration of function 'PDE_DATA' + ñLinux kernel汾죬proc fs ȱʧ PDE_DATA С + error hgic_fmac/procfs.f ļ鿴δ룬 LINUX_VERSION_CODE ޸һversion codeж + #if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,36) + static inline struct proc_dir_entry *PDE(const struct inode *inode) + { + return PROC_I(inode)->pde; + } + static inline void *PDE_DATA(const struct inode *inode) + { + return PDE(inode)->data; + } + #endif + + + +hgicf.conf : fmacļapģʽ +fmac.sh : fmacű +hgtest : ģʽtoolڷͲģʽ +iwpriv : ̩оרõiwprivߡԭϵͳûiwprivʹô˹ߡ diff --git a/utils/Makefile b/utils/Makefile new file mode 100644 index 0000000..2e0568d --- /dev/null +++ b/utils/Makefile @@ -0,0 +1,5 @@ +obj-m += hgic_usb.o +obj-m += hgic_sdio.o + +hgic_usb-objs += if_usb.o +hgic_sdio-objs += if_sdio.o diff --git a/utils/ah_freqinfo.c b/utils/ah_freqinfo.c new file mode 100644 index 0000000..01f3433 --- /dev/null +++ b/utils/ah_freqinfo.c @@ -0,0 +1,218 @@ +/** + ****************************************************************************** + * @file ah_freqinfo.c + * @author HUGE-IC Application Team + * @version V1.0.0 + * @date 2021-06-23 + * @brief IEEE802.11 AH Frequency defines + ****************************************************************************** + * @attention + * + *

© COPYRIGHT 2021 HUGE-IC

+ * + ****************************************************************************** + */ + +typedef unsigned char uint8; +typedef unsigned short uint16; +#define ARRAYSIZE(arr) (sizeof(arr) / sizeof((arr)[0])) + +struct ieee80211_ah_freqinfo { + uint8 s1g_opclass, type, max_txpower, rev; + uint16 freqlist[16]; +}; + +const struct ieee80211_ah_freqinfo ah_freqs[] = { + {1, 1, 30, 0, { 9020+5*1, 9020+5*3, 9020+5*37, 9020+5*39, 9020+5*41, 9020+5*43, + 9020+5*45, 9020+5*47, 9020+5*49, 9020+5*51}}, /*U.S., 1M, type1*/ + {1, 2, 30, 0, { 9020+5*5, 9020+5*7, 9020+5*9, 9020+5*11, 9020+5*13, 9020+5*15, + 9020+5*17, 9020+5*19, 9020+5*21, 9020+5*23, 9020+5*25, 9020+5*27, + 9020+5*29, 9020+5*31, 9020+5*33, 9020+5*35}}, /*U.S., 1M, type2*/ + {2, 1, 30, 0, { 9020+5*2, 9020+5*38, 9020+5*42, 9020+5*46, 9020+5*50}}, /*U.S., 2M, type1*/ + {2, 2, 30, 0, { 9020+5*6, 9020+5*10, 9020+5*14, 9020+5*18, 9020+5*22, 9020+5*26, 9020+5*30, 9020+5*34}}, /*U.S., 2M, type2*/ + {3, 1, 30, 0, { 9020+5*40, 9020+5*48}}, /*U.S., 4M, type1*/ + {3, 2, 30, 0, { 9020+5*8, 9020+5*16, 9020+5*24, 9020+5*32}}, /*U.S., 4M, type2*/ + {4, 1, 30, 0, { 9020+5*44}}, /*U.S., 8M, type1*/ + {4, 2, 30, 0, { 9020+5*12, 9020+5*28}}, /*U.S., 8M, type2*/ + {5, 2, 30, 0, { 9020+5*20}}, /*U.S., 16M, type2*/ + + {6, 1, 14, 0, { 8630+5*1, 8630+5*3, 8630+5*5, 8630+5*7, 8630+5*9}}, /*Europe, 1M, type1*/ + {7, 1, 14, 0, { 8630+5*2, 8630+5*6}}, /*Europe, 2M, type1*/ + + {8, 1, 23, 0, { 9165+5*1, 9165+5*3, 9165+5*5, 9165+5*7, 9165+5*9, 9165+5*11, + 9165+5*13, 9165+5*15, 9165+5*17, 9165+5*19, 9165+5*21}}, /*Japan, 1M, type1*/ + + {9, 1, 10, 0, { 7550+5*1, 7550+5*3, 7550+5*5, 7550+5*7, 7550+5*9, + 7550+5*11, 7550+5*13, 7550+5*15, 7550+5*17, 7550+5*19, + 7550+5*21, 7550+5*23, 7550+5*25, 7550+5*27, + 7550+5*29, 7550+5*31}}, /*China, 1M, type1*/ + /*{10, 1, 10, 0, { 7790+5*1, 7790+5*3, 7790+5*5, 7790+5*7, 7790+5*9, + 7790+5*11, 7790+5*13, 7790+5*15}},*/ /*China, 1M, type1*/ + {11, 2, 10, 0, { 7790+5*2, 7790+5*6, 7790+5*10, 7790+5*14}}, /*China, 2M, type2*/ + {12, 2, 10, 0, { 7790+5*4, 7790+5*12}}, /*China, 4M, type2*/ + {13, 2, 10, 0, { 7790+5*8}}, /*China, 8M, type2*/ + + {14, 1, 10, 0, { 9175+5*1, 9175+5*3, 9175+5*5, 9175+5*7, 9175+5*9, 9175+5*11}}, /*Korea, 1M, type1*/ + {15, 1, 10, 0, { 9175+5*2, 9175+5*6, 9175+5*10}}, /*Korea, 2M, type1*/ + {16, 1, 10, 0, { 9175+5*8}}, /*Korea, 4M, type1*/ + + {17, 1, 26, 0, { 8630+5*7, 8630+5*9, 8630+5*11, 9020+5*37, 9020+5*39, 9020+5*41, 9020+5*43, 9020+5*45}}, /*Singapore, 1M, type1*/ + {19, 1, 26, 0, { 8630+5*10, 9020+5*38, 9020+5*42}}, /*Singapore, 2M, type1*/ + {21, 1, 26, 0, { 9020+5*40}}, /*Singapore, 4M, type1*/ + + {22, 1, 30, 0, { 9020+5*27, 9020+5*29, 9020+5*31, 9020+5*33, 9020+5*35}}, /*Australia, 1M, type1*/ + {22, 2, 30, 0, { 9020+5*37, 9020+5*39, 9020+5*41, 9020+5*43, 9020+5*45, 9020+5*47, 9020+5*49, 9020+5*51}}, /*Australia, 1M, type2*/ + {23, 1, 30, 0, { 9020+5*28, 9020+5*32}}, /*Australia, 2M, type1*/ + {23, 2, 30, 0, { 9020+5*38, 9020+5*42, 9020+5*46, 9020+5*50}}, /*Australia, 2M, type2*/ + {24, 1, 30, 0, { 9020+5*30}}, /*Australia, 4M, type1*/ + {24, 2, 30, 0, { 9020+5*40, 9020+5*48}}, /*Australia, 4M, type2*/ + {25, 2, 30, 0, { 9020+5*44}}, /*Australia, 8M, type2*/ + + {26, 1, 36, 0, { 9020+5*27, 9020+5*29, 9020+5*31, 9020+5*33, 9020+5*35, 9020+5*37, 9020+5*39, 9020+5*41, 9020+5*43}}, /*New Zealand, 1M, type1*/ + {26, 2, 36, 0, { 9020+5*45, 9020+5*47, 9020+5*49, 9020+5*51}}, /*New Zealand, 1M, type2*/ + {27, 1, 36, 0, { 9020+5*28, 9020+5*32, 9020+5*36, 9020+5*40}}, /*New Zealand, 2M, type1*/ + {27, 2, 36, 0, { 9020+5*46, 9020+5*50}}, /*New Zealand, 2M, type2*/ + {28, 1, 36, 0, { 9020+5*30, 9020+5*38}}, /*New Zealand, 4M, type1*/ + {28, 2, 36, 0, { 9020+5*48}}, /*New Zealand, 4M, type2*/ + {29, 1, 36, 0, { 9020+5*34}}, /*New Zealand, 8M, type1*/ +}; + +struct ieee80211_ah_freqinfo *hgic_get_ah_freqinfo(char *country_code, char bw, char type) +{ + int i = 0; + uint8 s1g_opclass = 0; + + if(country_code == NULL) + return NULL; + + if (strcmp(country_code, "US") == 0) { + switch (bw) { + case 1: s1g_opclass = 1; break; + case 2: s1g_opclass = 2; break; + case 4: s1g_opclass = 3; break; + case 8: s1g_opclass = 4; break; + default: break; + }; + } else if (strcmp(country_code, "EU") == 0) { + switch (bw) { + case 1: s1g_opclass = 6; break; + case 2: s1g_opclass = 7; break; + default: break; + }; + } else if (strcmp(country_code, "JP") == 0) { + switch (bw) { + case 1: s1g_opclass = 8; break; + default: break; + }; + } else if (strcmp(country_code, "CN") == 0) { + switch (bw) { + case 1: s1g_opclass = 9; break; + case 2: s1g_opclass = 11; break; + case 4: s1g_opclass = 12; break; + case 8: s1g_opclass = 13; break; + default: break; + }; + } else if (strcmp(country_code, "KR") == 0) { + switch (bw) { + case 1: s1g_opclass = 14; break; + case 2: s1g_opclass = 15; break; + case 4: s1g_opclass = 16; break; + default: break; + }; + } else if (strcmp(country_code, "SG") == 0) { + switch (bw) { + case 1: s1g_opclass = 17; break; + case 2: s1g_opclass = 19; break; + case 4: s1g_opclass = 21; break; + default: break; + }; + } else if (strcmp(country_code, "AZ") == 0) { + switch (bw) { + case 1: s1g_opclass = 22; break; + case 2: s1g_opclass = 23; break; + case 4: s1g_opclass = 24; break; + case 8: s1g_opclass = 25; break; + default: break; + }; + } else if (strcmp(country_code, "NZ") == 0) { + switch (bw) { + case 1: s1g_opclass = 26; break; + case 2: s1g_opclass = 27; break; + case 4: s1g_opclass = 28; break; + case 8: s1g_opclass = 29; break; + default: break; + }; + } + + for (i = 0; s1g_opclass && i < ARRAYSIZE(ah_freqs); i++) { + if (ah_freqs[i].s1g_opclass == s1g_opclass && ah_freqs[i].type == type){ + return &ah_freqs[i]; + } + } + return NULL; +} + +#ifdef __linux__ /*for linux*/ +void hgic_ah_set_country_region(char *country_code, char bw, char type) +{ + int i = 0; + char *ptr = NULL; + char cmd[128]; + struct ieee80211_ah_freqinfo *freqinfo = NULL; + + freqinfo = hgic_get_ah_freqinfo(country_code, bw, type); + if (freqinfo == NULL) { + printf("invalid country region: %s, bw:%d, type:%d", country_code, bw, type); + return; + } + + /*set freq list*/ + ptr = cmd; + memset(cmd, 0, sizeof(cmd)); + strcpy(ptr, "iwpriv hg0 set chan_list="); + ptr += strlen(ptr); + for (i = 0; i < 16 && freqinfo->freqlist[i]; i++) { + sprintf(ptr, "%d,", freqinfo->freqlist[i]); + ptr += strlen(ptr); + } + if (i > 0) { + *(ptr--) = 0; + system(cmd); + } + + /*set bw*/ + memset(cmd, 0, sizeof(cmd)); + sprintf(cmd, "iwpriv hg0 set bss_bw=%d", bw); + system(cmd); + + /*set tx power*/ + memset(cmd, 0, sizeof(cmd)); + sprintf(cmd, "iwpriv hg0 set txpower=%d", freqinfo->max_txpower); + system(cmd); +} +#else /*for rtos*/ +void hgic_ah_set_country_region(char *country_code, char bw, char type) +{ + int i = 0; + struct ieee80211_ah_freqinfo *freqinfo = NULL; + + freqinfo = hgic_get_ah_freqinfo(country_code, bw, type); + if (freqinfo == NULL) { + printf("invalid country region: %s, bw:%d, type:%d", country_code, bw, type); + return; + } + + /*set freq list*/ + while (i < 16 && freqinfo->freqlist[i]) i++; + if (i > 0) { + hgicf_cmd("w0", HGIC_CMD_SET_CHAN_LIST, (unsigned int)&freqinfo->freqlist, i); + } + + /*set bw*/ + hgicf_cmd("w0", HGIC_CMD_SET_BSS_BW, bw, 0); + + /*set max power*/ + hgicf_cmd("w0", HGIC_CMD_SET_TX_POWER, freqinfo->max_txpower, 0); +} +#endif + diff --git a/utils/fwctrl.c b/utils/fwctrl.c new file mode 100644 index 0000000..93a3893 --- /dev/null +++ b/utils/fwctrl.c @@ -0,0 +1,1159 @@ +#ifdef __RTOS__ +#include +#include +#include +#include +#include +#else +#include +#include +#include +#include +#include +#include +#include +#endif + +#include "../hgic_def.h" +#include "fwdl.h" +#include "fwctrl.h" +#include "utils.h" + +#define ARG_DUP(arg,len) if(ctrl->param.arg != (void *)arg){ \ + if(ctrl->param.arg) kfree(ctrl->param.arg); \ + ctrl->param.arg = hgic_strdup(arg,len);\ + } + +#define ARG_FREE(arg) do{\ + if(ctrl->param.arg) kfree(ctrl->param.arg);\ + ctrl->param.arg = NULL;\ + } while (0) + +#define STR_LEN(s) ((s)?strlen(s):0) + +static void *hgic_strdup(void *arg, u32 len) +{ + int s = 0; + u8 *ptr = NULL; + if (arg) { + s = (len > 0) ? (len) : (strlen((char *)arg) + 1); + ptr = kzalloc(s, GFP_KERNEL); + if (ptr) { + if (len > 0) { memcpy(ptr, arg, len); } + else { strcpy(ptr, (char *)arg); } + } + } + return ptr; +} + +u16 hgic_ctrl_cookie(struct hgic_fwctrl *ctrl) +{ + unsigned long flags; + uint16_t cookie = 0; + + spin_lock_irqsave(&ctrl->lock, flags); + cookie = ctrl->cookie++; + ctrl->cookie &= HGIC_TX_COOKIE_MASK; + spin_unlock_irqrestore(&ctrl->lock, flags); + return cookie; +} + +static struct sk_buff *hgic_alloc_ctrl_skb(u32 size) +{ + struct sk_buff *skb = dev_alloc_skb(sizeof(struct hgic_ctrl_hdr) + size + 4); + if (!skb) { + return skb; + } + memset(skb->data, 0, sizeof(struct hgic_ctrl_hdr) + size); + skb_reserve(skb, sizeof(struct hgic_ctrl_hdr)); + skb_put(skb, size); + return skb; +} + +static struct sk_buff *hgic_fwctrl_send_cmd(struct hgic_fwctrl *ctrl, u32 cmd_id, struct sk_buff *skb, bool sync) +{ + struct hgic_ctrl_hdr *hdr = NULL; + struct hgic_cmd_response resp; + u16 id = cmd_id - HGIC_CMD_START; + + hdr = (struct hgic_ctrl_hdr *)skb_push(skb, sizeof(struct hgic_ctrl_hdr)); + hdr->hdr.magic = HGIC_HDR_TX_MAGIC; + hdr->hdr.length = skb->len; + hdr->hdr.ifidx = 1; + hdr->hdr.cookie = hgic_ctrl_cookie(ctrl); + if (id > 255) { + hdr->hdr.type = HGIC_HDR_TYPE_CMD2; + hdr->cmd2.cmd_id = id; + hdr->cmd2.status = 0; + } else { + hdr->hdr.type = HGIC_HDR_TYPE_CMD; + hdr->cmd.cmd_id = id; + hdr->cmd.status = 0; + } + memset(&resp, 0, sizeof(resp)); + resp.cookie = hdr->hdr.cookie; + return hgic_fwctrl_send_data(ctrl, skb, &resp, HGIC_CMD_TIMEOUT); +} + +static int hgic_fwctrl_set_byte(struct hgic_fwctrl *ctrl, u32 cmd_id, u8 val) +{ + int ret = -1; + struct sk_buff *skb = NULL; + struct sk_buff *resp = NULL; + struct hgic_ctrl_hdr *hdr = NULL; + + skb = hgic_alloc_ctrl_skb(1); + if (skb) { + skb->data[0] = val; + resp = hgic_fwctrl_send_cmd(ctrl, cmd_id, skb, true); + if (resp) { + hdr = (struct hgic_ctrl_hdr *)resp->data; + ret = hdr->cmd.status; + dev_kfree_skb_any(resp); + } + } + if (ret) { + hgic_dbg("cmd:%d, ret:%d (%s)\r\n", cmd_id, ret, resp ? "Responsed" : "No Response"); + } + return ret; +} + +static int hgic_fwctrl_set_int_val(struct hgic_fwctrl *ctrl, u32 cmd_id, u32 val) +{ + int ret = -1; + struct sk_buff *skb = NULL; + struct sk_buff *resp = NULL; + struct hgic_ctrl_hdr *hdr = NULL; + + skb = hgic_alloc_ctrl_skb(4); + if (skb) { + put_unaligned_le32(val, skb->data); + resp = hgic_fwctrl_send_cmd(ctrl, cmd_id, skb, true); + if (resp) { + hdr = (struct hgic_ctrl_hdr *)resp->data; + ret = hdr->cmd.status; + dev_kfree_skb_any(resp); + } + } + if (ret) { + hgic_dbg("cmd:%d, ret:%d (%s)\r\n", cmd_id, ret, resp ? "Responsed" : "No Response"); + } + return ret; +} + +static short hgic_fwctrl_get_short_val(struct hgic_fwctrl *ctrl, u32 cmd_id) +{ + short ret = -1; + struct sk_buff *skb = NULL; + struct sk_buff *resp = NULL; + struct hgic_ctrl_hdr *hdr = NULL; + + skb = hgic_alloc_ctrl_skb(0); + if (skb) { + resp = hgic_fwctrl_send_cmd(ctrl, cmd_id, skb, true); + if (resp) { + hdr = (struct hgic_ctrl_hdr *)resp->data; + ret = hdr->cmd.status; + dev_kfree_skb_any(resp); + } + } + return ret; +} + +static int hgic_fwctrl_set_bytes(struct hgic_fwctrl *ctrl, u32 cmd_id, u8 *data, u32 len) +{ + int ret = -1; + struct sk_buff *skb = NULL; + struct sk_buff *resp = NULL; + struct hgic_ctrl_hdr *hdr = NULL; + + skb = hgic_alloc_ctrl_skb(len); + if (skb) { + if (data && len > 0) { + memcpy(skb->data, data, len); + skb->data[len] = 0; + } + resp = hgic_fwctrl_send_cmd(ctrl, cmd_id, skb, true); + if (resp) { + hdr = (struct hgic_ctrl_hdr *)resp->data; + ret = hdr->cmd.status; + dev_kfree_skb_any(resp); + } + if (ret) { + hgic_dbg("cmd:%d, ret:%d (%s)\r\n", cmd_id, ret, resp ? "Responsed" : "No Response"); + } + } + return ret; +} + +static int hgic_fwctrl_get_bytes(struct hgic_fwctrl *ctrl, u32 cmd_id, u8 *buff, u32 len) +{ + int ret = -1; + struct sk_buff *skb = NULL; + struct sk_buff *resp = NULL; + struct hgic_ctrl_hdr *hdr = NULL; + + skb = hgic_alloc_ctrl_skb(0); + if (skb) { + resp = hgic_fwctrl_send_cmd(ctrl, cmd_id, skb, true); + if (resp) { + hdr = (struct hgic_ctrl_hdr *)resp->data; + ret = hdr->cmd.status; + if (ret > 0 && len > 0) { + ret = ret > len ? len : ret; + memcpy(buff, (char *)(hdr + 1), ret); + } + dev_kfree_skb_any(resp); + } + } + if (ret < 0) { + hgic_dbg("get string: cmd:%d, ret:%d (%s)\r\n", cmd_id, ret, resp ? "Responsed" : "No Response"); + } + return ret; +} + +static int hgic_fwctrl_do_cmd(struct hgic_fwctrl *ctrl, u32 cmd_id, u8 *data, u32 len, u8 *buff, u32 size) +{ + int ret = -1; + struct sk_buff *skb = NULL; + struct sk_buff *resp = NULL; + struct hgic_ctrl_hdr *hdr = NULL; + + skb = hgic_alloc_ctrl_skb(len); + if (skb) { + if (data && len > 0) { + memcpy(skb->data, data, len); + skb->data[len] = 0; + } + resp = hgic_fwctrl_send_cmd(ctrl, cmd_id, skb, true); + if (resp) { + hdr = (struct hgic_ctrl_hdr *)resp->data; + ret = hdr->cmd.status; + if (ret > 0 && buff) { + memcpy(buff, (char *)(hdr + 1), ret > size ? size : ret); + } + dev_kfree_skb_any(resp); + } + if (ret < 0) { + hgic_dbg("cmd:%d, ret:%d (%s)\r\n", cmd_id, ret, resp ? "Responsed" : "No Response"); + } + } + return ret; +} + +static int hgic_fwctrl_rx_response(struct hgic_fwctrl *ctrl, struct sk_buff *skb) +{ + int find = 0; + unsigned long flags; + struct hgic_cmd_response *resp = NULL; + struct hgic_ctrl_hdr *cmd = (struct hgic_ctrl_hdr *)skb->data;; + + spin_lock_irqsave(&ctrl->lock, flags); + list_for_each_entry(resp, &ctrl->pd_list, list) { + if (resp->cookie == cmd->hdr.cookie) { + resp->skb = skb; + complete(&resp->cmpl); + find = 1; + break; + } + } + spin_unlock_irqrestore(&ctrl->lock, flags); + if (!find) { + dev_kfree_skb_any(skb); + } + return 0; +} + +static void hgic_fwctrl_clear_pdlist(struct hgic_fwctrl *ctrl) +{ + unsigned long flags; + struct hgic_cmd_response *sync = NULL; + + spin_lock_irqsave(&ctrl->lock, flags); + list_for_each_entry(sync, &ctrl->pd_list, list) { + complete(&sync->cmpl); + } + spin_unlock_irqrestore(&ctrl->lock, flags); +} + +static void hgic_fwctrl_work(struct work_struct *work) +{ + struct hgic_fwctrl *ctrl = NULL; + struct sk_buff *skb = NULL; + struct hgic_ctrl_hdr *hdr = NULL; + + ctrl = container_of(work, struct hgic_fwctrl, work); + while (!skb_queue_empty(&ctrl->rxq)) { + skb = skb_dequeue(&ctrl->rxq); + hdr = (struct hgic_ctrl_hdr *)skb->data; + switch (hdr->hdr.type) { + case HGIC_HDR_TYPE_CMD: + case HGIC_HDR_TYPE_CMD2: + case HGIC_HDR_TYPE_BOOTDL: + case HGIC_HDR_TYPE_OTA: + hgic_fwctrl_rx_response(ctrl, skb); + break; + case HGIC_HDR_TYPE_EVENT: + case HGIC_HDR_TYPE_EVENT2: + hgic_rx_fw_event(ctrl, skb); + break; + default: + dev_kfree_skb(skb); + break; + } + } +} + +static void hgic_fwctrl_flush_work(struct work_struct *work) +{ + struct hgic_fwctrl *ctrl = container_of(work, struct hgic_fwctrl, flush_work); + hgic_fwctrl_flush_param(ctrl); +} + +struct sk_buff *hgic_fwctrl_send_data(struct hgic_fwctrl *ctrl, struct sk_buff *skb, struct hgic_cmd_response *resp, u32 timeout) +{ + unsigned long flags; + + init_completion(&resp->cmpl); + spin_lock_irqsave(&ctrl->lock, flags); + list_add(&resp->list, &ctrl->pd_list); + spin_unlock_irqrestore(&ctrl->lock, flags); + + skb_queue_tail(&ctrl->txq, skb); + hgic_schedule(ctrl); + if (timeout) { + wait_for_completion_timeout(&resp->cmpl, msecs_to_jiffies(timeout)); + } + spin_lock_irqsave(&ctrl->lock, flags); + list_del(&resp->list); + spin_unlock_irqrestore(&ctrl->lock, flags); +#ifdef __RTOS__ + deinit_completion(&resp->cmpl); +#endif + return resp->skb; +} + +void hgic_fwctrl_rx(struct hgic_fwctrl *ctrl, struct sk_buff *skb) +{ + skb_queue_tail(&ctrl->rxq, skb); + if (ctrl->wq) { + queue_work(ctrl->wq, &ctrl->work); + } else { + hgic_err("fwctrl workqueue is NULL\r\n"); + } +} + +void hgic_fwctrl_init(struct hgic_fwctrl *ctrl, void *dev) +{ + memset(ctrl, 0, sizeof(struct hgic_fwctrl)); + ctrl->dev = dev; + ctrl->param.acs = 0x3; + ctrl->param.tx_mcs = 0xff; + ctrl->param.ps_mode = 0xff; + ctrl->param.wkio_mode = 0xff; + spin_lock_init(&ctrl->lock); + INIT_LIST_HEAD(&ctrl->pd_list); + skb_queue_head_init(&ctrl->rxq); + skb_queue_head_init(&ctrl->txq); + INIT_WORK(&ctrl->flush_work, hgic_fwctrl_flush_work); + INIT_WORK(&ctrl->work, hgic_fwctrl_work); + ctrl->wq = ALLOC_ORDERED_WORKQUEUE("fwctrl", 0); +} + +void hgic_fwctrl_release(struct hgic_fwctrl *ctrl) +{ + cancel_work_sync(&ctrl->work); + cancel_work_sync(&ctrl->flush_work); + hgic_fwctrl_clear_pdlist(ctrl); + hgic_clear_queue(&ctrl->txq); + hgic_clear_queue(&ctrl->rxq); +#ifdef __RTOS__ + skb_queue_head_deinit(&ctrl->rxq); + skb_queue_head_deinit(&ctrl->txq); + spin_lock_deinit(&ctrl->lock); +#endif + if (ctrl->wq) { + flush_workqueue(ctrl->wq); + destroy_workqueue(ctrl->wq); + } + ARG_FREE(country_code); + ARG_FREE(ssid); + ARG_FREE(bssid); + ARG_FREE(bssid_filter); + ARG_FREE(key_mgmt); + ARG_FREE(wpa_psk); + ARG_FREE(mode); + ARG_FREE(mac_addr); + ARG_FREE(chan_list); + ARG_FREE(paired_stas); + ARG_FREE(mcast_key); +} + +void hgic_fwctrl_flush_param(struct hgic_fwctrl *ctrl) +{ + //int retry = 0; + + if (ctrl->param.bss_bw) { + hgic_fwctrl_set_bss_bw(ctrl, ctrl->param.bss_bw); + } + if (ctrl->param.chan_list) { + hgic_fwctrl_set_chan_list(ctrl, (u16 *)ctrl->param.chan_list, ctrl->param.chan_cnt); + } + if (ctrl->param.freq_start) { + hgic_fwctrl_set_freq_range(ctrl, ctrl->param.freq_start, ctrl->param.freq_end, ctrl->param.chan_bw); + } + if (ctrl->param.country_code) { + hgic_fwctrl_set_countryregion(ctrl, ctrl->param.country_code); + } + if (ctrl->param.ssid) { + hgic_fwctrl_set_ssid(ctrl, ctrl->param.ssid); + } + if (ctrl->param.bssid) { + hgic_fwctrl_set_bssid(ctrl, ctrl->param.bssid); + } + if (ctrl->param.bssid_filter) { + hgic_fwctrl_set_bssid_filter(ctrl, ctrl->param.bssid_filter); + } + if (ctrl->param.key_mgmt) { + hgic_fwctrl_set_key_mgmt(ctrl, ctrl->param.key_mgmt); + } + if (ctrl->param.wpa_psk) { + hgic_fwctrl_set_wpa_psk(ctrl, ctrl->param.wpa_psk); + } + if (ctrl->param.mcast_key) { + hgic_fwctrl_set_mcast_key(ctrl, ctrl->param.mcast_key); + } + if (ctrl->param.mac_addr) { + hgic_fwctrl_set_mac(ctrl, ctrl->param.mac_addr); + } + if (ctrl->param.channel) { + hgic_fwctrl_set_channel(ctrl, ctrl->param.channel); + } + if (ctrl->param.rts_threshold) { + hgic_fwctrl_set_rts_threshold(ctrl, ctrl->param.rts_threshold); + } + if (ctrl->param.frag_threshold) { + hgic_fwctrl_set_frag_threshold(ctrl, ctrl->param.frag_threshold); + } + if (ctrl->param.listen_interval) { + hgic_fwctrl_set_listen_interval(ctrl, ctrl->param.listen_interval); + } + if (ctrl->param.center_freq) { + hgic_fwctrl_set_center_freq(ctrl, ctrl->param.center_freq); + } + if (ctrl->param.beacon_int) { + hgic_fwctrl_set_beacon_int(ctrl, ctrl->param.beacon_int); + } + if (ctrl->param.ethertype) { + hgic_fwctrl_set_ethertype(ctrl, ctrl->param.ethertype); + } + if (ctrl->param.txpower) { + hgic_fwctrl_set_txpower(ctrl, ctrl->param.txpower); + } + if (ctrl->param.primary_chan) { + hgic_fwctrl_set_primary_chan(ctrl, ctrl->param.primary_chan); + } + if (ctrl->param.mode) { + hgic_fwctrl_set_mode(ctrl, ctrl->param.mode); + } + if (ctrl->param.acs != 0x3) { + hgic_fwctrl_set_acs(ctrl, ctrl->param.acs, ctrl->param.acs_tmo); + } + if (ctrl->param.bss_max_idle) { + hgic_fwctrl_set_bss_max_idle(ctrl, ctrl->param.bss_max_idle); + } + if (ctrl->param.dtim_period) { + hgic_fwctrl_set_dtim_period(ctrl, ctrl->param.dtim_period); + } + if (ctrl->param.ps_connect) { + hgic_fwctrl_set_ps_connect(ctrl, ctrl->param.ps_connect, ctrl->param.ps_connect_roundup); + } + if (ctrl->param.agg_cnt) { + hgic_fwctrl_set_agg_cnt(ctrl, ctrl->param.agg_cnt); + } + if (ctrl->param.wkio_mode != 0xff) { + hgic_fwctrl_set_wkio_mode(ctrl, ctrl->param.wkio_mode); + } + if (ctrl->param.ps_mode != 0xff) { + hgic_fwctrl_set_ps_mode(ctrl, ctrl->param.ps_mode); + } + if (ctrl->param.aplost_time) { + hgic_fwctrl_set_aplost_time(ctrl, ctrl->param.aplost_time); + } + if (ctrl->param.tx_mcs != 0xff) { + hgic_fwctrl_set_tx_mcs(ctrl, ctrl->param.tx_mcs); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////// +int hgic_fwctrl_testmode_cmd(struct hgic_fwctrl *ctrl, u8 *cmd, u32 size) +{ + int ret = 0; + + ret = hgic_fwctrl_do_cmd(ctrl, HGIC_CMD_TESTMODE_CMD, cmd, STR_LEN(cmd), cmd, size); + if (ret < 0) { + strcpy(cmd, "failed"); + } else { + cmd[ret] = 0; + } + return ret; +} + +int hgic_fwctrl_get_status(struct hgic_fwctrl *ctrl, u8 *buff, u32 len) +{ + return hgic_fwctrl_get_bytes(ctrl, HGIC_CMD_GET_STATUS, buff, len); +} + +int hgic_fwctrl_get_conn_state(struct hgic_fwctrl *ctrl) +{ + return hgic_fwctrl_do_cmd(ctrl, HGIC_CMD_GET_CONN_STATE, NULL, 0, NULL, 0); +} + +int hgic_fwctrl_get_fwinfo(struct hgic_fwctrl *ctrl, struct hgic_fw_info *info) +{ + return hgic_fwctrl_get_bytes(ctrl, HGIC_CMD_GET_FW_INFO, (u8 *)info, sizeof(struct hgic_fw_info)); +} + +int hgic_fwctrl_set_countryregion(struct hgic_fwctrl *ctrl, u8 *country_code) +{ + ARG_DUP(country_code, 0); + return hgic_fwctrl_set_bytes(ctrl, HGIC_CMD_SET_COUNTERY, country_code, STR_LEN(country_code)); +} + +int hgic_fwctrl_set_ssid(struct hgic_fwctrl *ctrl, u8 *ssid) +{ + ARG_DUP(ssid, 0); + return hgic_fwctrl_set_bytes(ctrl, HGIC_CMD_SET_SSID, ssid, STR_LEN(ssid)); +} + +int hgic_fwctrl_set_bssid(struct hgic_fwctrl *ctrl, u8 *bssid) +{ + ARG_DUP(bssid, 6); + return hgic_fwctrl_set_bytes(ctrl, HGIC_CMD_SET_BSSID, bssid, 6); +} + +int hgic_fwctrl_set_channel(struct hgic_fwctrl *ctrl, u32 channel) +{ + ctrl->param.acs = 0; + ctrl->param.channel = channel; + return hgic_fwctrl_set_int_val(ctrl, HGIC_CMD_SET_CHANNEL, channel); +} + +int hgic_fwctrl_set_bssid_filter(struct hgic_fwctrl *ctrl, u8 *bssid_filter) +{ + ARG_DUP(bssid_filter, 0); + return hgic_fwctrl_set_bytes(ctrl, HGIC_CMD_SET_BSSID_FILTER, bssid_filter, STR_LEN(bssid_filter)); +} + +int hgic_fwctrl_set_rts_threshold(struct hgic_fwctrl *ctrl, u32 rts_threshold) +{ + ctrl->param.rts_threshold = rts_threshold; + return hgic_fwctrl_set_int_val(ctrl, HGIC_CMD_SET_RTS_THRESHOLD, rts_threshold); +} + +int hgic_fwctrl_set_frag_threshold(struct hgic_fwctrl *ctrl, u32 frag_threshold) +{ + ctrl->param.frag_threshold = frag_threshold; + return hgic_fwctrl_set_int_val(ctrl, HGIC_CMD_SET_FRG_THRESHOLD, frag_threshold); +} + +int hgic_fwctrl_set_key_mgmt(struct hgic_fwctrl *ctrl, u8 *key_mgmt) +{ + ARG_DUP(key_mgmt, 0); + return hgic_fwctrl_set_bytes(ctrl, HGIC_CMD_SET_KEY_MGMT, key_mgmt, STR_LEN(key_mgmt)); +} + +int hgic_fwctrl_set_wpa_psk(struct hgic_fwctrl *ctrl, u8 *wpa_psk) +{ + ARG_DUP(wpa_psk, 0); + return hgic_fwctrl_set_bytes(ctrl, HGIC_CMD_SET_WPA_PSK, wpa_psk, STR_LEN(wpa_psk)); +} + +int hgic_fwctrl_set_wbnat(struct hgic_fwctrl *ctrl, u32 enable) +{ + return hgic_fwctrl_set_int_val(ctrl, HGIC_CMD_SET_WBNAT, enable); +} + +int hgic_fwctrl_set_freq_range(struct hgic_fwctrl *ctrl, u32 freq_start, u32 freq_end, u32 bss_bw) +{ + u8 data[12]; + put_unaligned_le32(freq_start, data); + put_unaligned_le32(freq_end, data + 4); + put_unaligned_le32(bss_bw, data + 8); + ctrl->param.freq_start = freq_start; + ctrl->param.freq_end = freq_end; + ctrl->param.chan_bw = bss_bw; + ARG_FREE(chan_list); + return hgic_fwctrl_set_bytes(ctrl, HGIC_CMD_SET_FREQ_RANGE, data, 12); +} + +int hgic_fwctrl_set_bss_bw(struct hgic_fwctrl *ctrl, u8 bss_bw) +{ + ctrl->param.bss_bw = bss_bw; + return hgic_fwctrl_set_byte(ctrl, HGIC_CMD_SET_BSS_BW, bss_bw); +} + +int hgic_fwctrl_set_tx_bw(struct hgic_fwctrl *ctrl, u8 tx_bw) +{ + ctrl->param.tx_bw = tx_bw; + return hgic_fwctrl_set_byte(ctrl, HGIC_CMD_SET_TX_BW, tx_bw); +} + +int hgic_fwctrl_set_tx_mcs(struct hgic_fwctrl *ctrl, u8 tx_mcs) +{ + ctrl->param.tx_mcs = tx_mcs; + return hgic_fwctrl_set_byte(ctrl, HGIC_CMD_SET_TX_MCS, tx_mcs); +} + +int hgic_fwctrl_set_acs(struct hgic_fwctrl *ctrl, u8 acs, u8 acs_tmo) +{ + u8 data[2] = {acs, acs_tmo}; + ctrl->param.acs = acs; + ctrl->param.acs_tmo = acs_tmo; + if (acs) { + ctrl->param.channel = 0; + ctrl->param.center_freq = 0; + } + return hgic_fwctrl_set_bytes(ctrl, HGIC_CMD_ACS_ENABLE, data, 2); +} + +int hgic_fwctrl_set_bgrssi(struct hgic_fwctrl *ctrl, u8 bgrssi) +{ + return hgic_fwctrl_set_byte(ctrl, HGIC_CMD_SET_BG_RSSI, bgrssi); +} + +int hgic_fwctrl_set_chan_list(struct hgic_fwctrl *ctrl, u16 *chan_list, u32 cnt) +{ + int ret = -1; + int i = 0; + u8 *buf = kzalloc((1 + cnt) * sizeof(u16), GFP_KERNEL); + ARG_DUP(chan_list, cnt * sizeof(u16)); + ctrl->param.chan_cnt = cnt; + ctrl->param.freq_start = 0; + if (buf) { + put_unaligned_le16(cnt, buf); + for (i = 0; i < cnt; i++) { + put_unaligned_le16(chan_list[i], buf + (i + 1) * sizeof(u16)); + } + ret = hgic_fwctrl_set_bytes(ctrl, HGIC_CMD_SET_CHAN_LIST, buf, (1 + cnt) * sizeof(u16)); + kfree(buf); + } + return ret; +} + +int hgic_fwctrl_set_mode(struct hgic_fwctrl *ctrl, u8 *mode) +{ + ARG_DUP(mode, 0); + return hgic_fwctrl_set_bytes(ctrl, HGIC_CMD_SET_WORK_MODE, mode, STR_LEN(mode)); +} + +int hgic_fwctrl_set_paired_stas(struct hgic_fwctrl *ctrl, u8 *paired_stas, u32 len) +{ + ARG_DUP(paired_stas, len); + return hgic_fwctrl_set_bytes(ctrl, HGIC_CMD_SET_PAIRED_STATIONS, paired_stas, len); +} + +int hgic_fwctrl_set_pairing(struct hgic_fwctrl *ctrl, u8 start) +{ + return hgic_fwctrl_set_byte(ctrl, HGIC_CMD_PAIRING, start); +} + +int hgic_fwctrl_open_dev(struct hgic_fwctrl *ctrl) +{ + return hgic_fwctrl_do_cmd(ctrl, HGIC_CMD_DEV_OPEN, 0, 0, 0, 0); +} + +int hgic_fwctrl_close_dev(struct hgic_fwctrl *ctrl) +{ + return hgic_fwctrl_do_cmd(ctrl, HGIC_CMD_DEV_CLOSE, 0, 0, 0, 0); +} + +int hgic_fwctrl_set_txpower(struct hgic_fwctrl *ctrl, u32 tx_power) +{ + ctrl->param.txpower = tx_power; + return hgic_fwctrl_set_int_val(ctrl, HGIC_CMD_SET_TX_POWER, tx_power); +} + +int hgic_fwctrl_get_txpower(struct hgic_fwctrl *ctrl) +{ + return hgic_fwctrl_get_short_val(ctrl, HGIC_CMD_GET_TX_POWER); +} + +int hgic_fwctrl_set_listen_interval(struct hgic_fwctrl *ctrl, u32 listen_interval) +{ + ctrl->param.listen_interval = listen_interval; + return hgic_fwctrl_set_int_val(ctrl, HGIC_CMD_SET_LISTEN_INTERVAL, listen_interval); +} + +int hgic_fwctrl_set_center_freq(struct hgic_fwctrl *ctrl, u32 center_freq) +{ + ctrl->param.acs = 0; + ctrl->param.center_freq = center_freq; + return hgic_fwctrl_set_int_val(ctrl, HGIC_CMD_SET_CENTER_FREQ, center_freq); +} + +int hgic_fwctrl_set_tx_count(struct hgic_fwctrl *ctrl, u32 short_frm_tx_count, u32 long_frm_tx_count) +{ + u8 data[8]; + put_unaligned_le32(short_frm_tx_count, data); + put_unaligned_le32(long_frm_tx_count, data + 4); + return hgic_fwctrl_set_bytes(ctrl, HGIC_CMD_SET_TX_LCOUNT, data, 8); +} + +int hgic_fwctrl_set_key(struct hgic_fwctrl *ctrl, u8 cmd, u16 aid, u8 *key, u8 len) +{ + int ret = -ENOMEM; + u8 *buff = kmalloc(len + 4, GFP_KERNEL); + if (buff) { + buff[0] = cmd; + put_unaligned_le16(aid, buff + 1); + buff[3] = len; + memcpy(buff + 4, key, len); + ret = hgic_fwctrl_set_bytes(ctrl, HGIC_CMD_SET_KEY, buff, len + 4); + kfree(buff); + } + return ret; +} + +int hgic_fwctrl_add_sta(struct hgic_fwctrl *ctrl, u16 aid, u8 *addr) +{ + uint8_t sta_info[8]; + put_unaligned_le16(aid, sta_info); + memcpy(sta_info + 2, addr, 6); + return hgic_fwctrl_set_bytes(ctrl, HGIC_CMD_ADD_STA, sta_info, 8); +} + +int hgic_fwctrl_del_sta(struct hgic_fwctrl *ctrl, u32 aid) +{ + return hgic_fwctrl_set_int_val(ctrl, HGIC_CMD_REMOVE_STA, aid); +} + +int hgic_fwctrl_set_primary_chan(struct hgic_fwctrl *ctrl, u8 primary_chan) +{ + ctrl->param.primary_chan = primary_chan; + return hgic_fwctrl_set_byte(ctrl, HGIC_CMD_SET_PRIMARY_CHAN, primary_chan); +} + +int hgic_fwctrl_set_aid(struct hgic_fwctrl *ctrl, u32 aid) +{ + return hgic_fwctrl_set_int_val(ctrl, HGIC_CMD_SET_AID, aid); +} + +int hgic_fwctrl_set_mac(struct hgic_fwctrl *ctrl, u8 *mac_addr) +{ + ARG_DUP(mac_addr, 6); + return hgic_fwctrl_set_bytes(ctrl, HGIC_CMD_SET_MAC, mac_addr, 6); +} + +int hgic_fwctrl_get_scan_list(struct hgic_fwctrl *ctrl, u8 *buff, u32 size) +{ + return hgic_fwctrl_get_bytes(ctrl, HGIC_CMD_GET_SCAN_LIST, buff, size); +} + +int hgic_fwctrl_scan(struct hgic_fwctrl *ctrl) +{ + return hgic_fwctrl_set_byte(ctrl, HGIC_CMD_SCAN, 1); +} + +int hgic_fwctrl_set_txq_param(struct hgic_fwctrl *ctrl, u8 *txq, u32 size) +{ + return hgic_fwctrl_set_bytes(ctrl, HGIC_CMD_SET_TXQ_PARAM, txq, size); +} + +int hgic_fwctrl_get_temperature(struct hgic_fwctrl *ctrl) +{ + return hgic_fwctrl_get_short_val(ctrl, HGIC_CMD_GET_TEMPERATURE); +} + +int hgic_fwctrl_enter_sleep(struct hgic_fwctrl *ctrl, u8 sleep) +{ + return hgic_fwctrl_set_byte(ctrl, HGIC_CMD_ENTER_SLEEP, sleep); +} + +int hgic_fwctrl_get_sta_list(struct hgic_fwctrl *ctrl, struct hgic_sta_info *sta_list, u32 size) +{ + int ret = hgic_fwctrl_get_bytes(ctrl, HGIC_CMD_GET_STA_LIST, (u8 *)sta_list, size * sizeof(struct hgic_sta_info)); + return ret > 0 ? (ret / sizeof(struct hgic_sta_info)) : ret; +} + +int hgic_fwctrl_set_beacon_int(struct hgic_fwctrl *ctrl, u32 beacon_int) +{ + ctrl->param.beacon_int = beacon_int; + return hgic_fwctrl_set_int_val(ctrl, HGIC_CMD_SET_BEACON_INT, beacon_int); +} + +int hgic_fwctrl_get_ssid(struct hgic_fwctrl *ctrl, u8 *ssid, u32 size) +{ + return hgic_fwctrl_get_bytes(ctrl, HGIC_CMD_GET_SSID, ssid, size); +} + +int hgic_fwctrl_get_bssid(struct hgic_fwctrl *ctrl, u8 *bssid) +{ + return hgic_fwctrl_get_bytes(ctrl, HGIC_CMD_GET_BSSID, bssid, 6); +} + +int hgic_fwctrl_get_wpapsk(struct hgic_fwctrl *ctrl, u8 *psk, u32 size) +{ + return hgic_fwctrl_get_bytes(ctrl, HGIC_CMD_GET_WPA_PSK, psk, size); +} + +int hgic_fwctrl_save_cfg(struct hgic_fwctrl *ctrl) +{ + return hgic_fwctrl_do_cmd(ctrl, HGIC_CMD_SAVE_CFG, 0, 0, 0, 0); +} + +int hgic_fwctrl_join_group(struct hgic_fwctrl *ctrl, u8 *addr, u8 aid) +{ + u8 val[7]; + memcpy(val, addr, 6); + val[6] = aid; + return hgic_fwctrl_set_bytes(ctrl, HGIC_CMD_JOIN_GROUP, val, 7); +} + +int hgic_fwctrl_set_ethertype(struct hgic_fwctrl *ctrl, u16 ethertype) +{ + u8 vals[2]; + ctrl->param.ethertype = ethertype; + put_unaligned_le16(ethertype, vals); + return hgic_fwctrl_set_bytes(ctrl, HGIC_CMD_SET_ETHER_TYPE, vals, 2); +} + +int hgic_fwctrl_get_sta_count(struct hgic_fwctrl *ctrl) +{ + return hgic_fwctrl_do_cmd(ctrl, HGIC_CMD_GET_STA_COUNT, 0, 0, 0, 0); +} + +int hgic_fwctrl_get_agg_cnt(struct hgic_fwctrl *ctrl) +{ + return hgic_fwctrl_get_short_val(ctrl, HGIC_CMD_GET_AGG_CNT); +} + +int hgic_fwctrl_set_agg_cnt(struct hgic_fwctrl *ctrl, u8 agg_cnt) +{ + ctrl->param.agg_cnt = agg_cnt; + return hgic_fwctrl_set_byte(ctrl, HGIC_CMD_SET_AGG_CNT, agg_cnt); +} + +int hgic_fwctrl_get_bss_bw(struct hgic_fwctrl *ctrl) +{ + return hgic_fwctrl_get_short_val(ctrl, HGIC_CMD_GET_BSS_BW); +} + +int hgic_fwctrl_get_freq_range(struct hgic_fwctrl *ctrl, u32 *freq_start, u32 *freq_end, u32 *bss_bw) +{ + int ret = 0; + u32 vals[3]; + + ret = hgic_fwctrl_get_bytes(ctrl, HGIC_CMD_GET_FREQ_RANGE, (u8 *)&vals, sizeof(vals)); + if (ret == 12) { + *freq_start = vals[0]; + *freq_end = vals[1]; + *bss_bw = vals[2]; + } + return (ret == 12); +} + +int hgic_fwctrl_get_chan_list(struct hgic_fwctrl *ctrl, u16 *chan_list, u16 count) +{ + int ret = hgic_fwctrl_get_bytes(ctrl, HGIC_CMD_GET_CHAN_LIST, (u8 *)chan_list, count * sizeof(u16)); + return ret > 0 ? ret / sizeof(u16) : 0; +} + +int hgic_fwctrl_wakeup_sta(struct hgic_fwctrl *ctrl, u8 *addr) +{ + return hgic_fwctrl_set_bytes(ctrl, HGIC_CMD_SET_WAKEUP_STA, addr, 6); +} + +int hgic_fwctrl_set_ps_heartbeat(struct hgic_fwctrl *ctrl, u32 ipaddr, u32 dport, u32 period, u32 hb_tmo) +{ + u8 val[16]; + put_unaligned_le32(ipaddr, val); + put_unaligned_le32(dport, val + 4); + put_unaligned_le32(period, val + 8); + put_unaligned_le32(hb_tmo, val + 12); + hgic_dbg("ip:%x, port:%d, period:%d, hb_tmo:%d\r\n", ipaddr, dport, period, hb_tmo); + return hgic_fwctrl_set_bytes(ctrl, HGIC_CMD_SET_PS_HEARTBEAT, val, 16); +} + +int hgic_fwctrl_set_ps_heartbeat_resp(struct hgic_fwctrl *ctrl, u8 *data, u32 size) +{ + return hgic_fwctrl_set_bytes(ctrl, HGIC_CMD_SET_PS_HEARTBEAT_RESP, data, size); +} + +int hgic_fwctrl_set_ps_wakeup_data(struct hgic_fwctrl *ctrl, u8 *data, u32 size) +{ + return hgic_fwctrl_set_bytes(ctrl, HGIC_CMD_SET_PS_WAKEUP_DATA, data, size); +} + +int hgic_fwctrl_set_ps_connect(struct hgic_fwctrl *ctrl, u8 period, u8 roundup) +{ + u8 val[2] = {period, roundup}; + ctrl->param.ps_connect = period; + ctrl->param.ps_connect_roundup = roundup; + return hgic_fwctrl_set_bytes(ctrl, HGIC_CMD_SET_PS_CONNECT, val, 2); +} + +int hgic_fwctrl_radio_onoff(struct hgic_fwctrl *ctrl, u8 onoff) +{ + return hgic_fwctrl_set_byte(ctrl, HGIC_CMD_RADIO_ONOFF, onoff); +} + +int hgic_fwctrl_set_bss_max_idle(struct hgic_fwctrl *ctrl, u32 max_idle) +{ + ctrl->param.bss_max_idle = max_idle; + return hgic_fwctrl_set_int_val(ctrl, HGIC_CMD_SET_BSS_MAX_IDLE, max_idle); +} + +int hgic_fwctrl_set_wkio_mode(struct hgic_fwctrl *ctrl, u8 mode) +{ + ctrl->param.wkio_mode = mode; + return hgic_fwctrl_set_byte(ctrl, HGIC_CMD_SET_WKIO_MODE, mode); +} + +int hgic_fwctrl_set_dtim_period(struct hgic_fwctrl *ctrl, u32 period) +{ + ctrl->param.dtim_period = period; + return hgic_fwctrl_set_int_val(ctrl, HGIC_CMD_SET_DTIM_PERIOD, period); +} + +int hgic_fwctrl_set_ps_mode(struct hgic_fwctrl *ctrl, u8 mode) +{ + ctrl->param.ps_mode = mode; + return hgic_fwctrl_set_byte(ctrl, HGIC_CMD_SET_PS_MODE, mode); +} + +int hgic_fwctrl_set_load_def(struct hgic_fwctrl *ctrl, u8 rst) +{ + return hgic_fwctrl_set_byte(ctrl, HGIC_CMD_LOAD_DEF, rst); +} + +int hgic_fwctrl_disassoc_sta(struct hgic_fwctrl *ctrl, u8 *addr) +{ + return hgic_fwctrl_set_bytes(ctrl, HGIC_CMD_DISASSOC_STA, addr, 6); +} + +int hgic_fwctrl_set_aplost_time(struct hgic_fwctrl *ctrl, u32 aplost_time) +{ + return hgic_fwctrl_set_int_val(ctrl, HGIC_CMD_SET_APLOST_TIME, aplost_time); +} + +int hgic_fwctrl_get_wkreason(struct hgic_fwctrl *ctrl) +{ + return hgic_fwctrl_do_cmd(ctrl, HGIC_CMD_GET_WAKEUP_REASON, 0, 0, 0, 0); +} + +int hgic_fwctrl_unpair(struct hgic_fwctrl *ctrl, u8 *addr) +{ + ARG_FREE(ssid); + ARG_FREE(bssid); + ARG_FREE(bssid_filter); + ARG_FREE(key_mgmt); + ARG_FREE(wpa_psk); + ARG_FREE(paired_stas); + ARG_FREE(mcast_key); + return hgic_fwctrl_set_bytes(ctrl, HGIC_CMD_UNPAIR, addr, 6); +} + +int hgic_fwctrl_set_auto_chswitch(struct hgic_fwctrl *ctrl, u8 enable) +{ + return hgic_fwctrl_set_byte(ctrl, HGIC_CMD_SET_AUTO_CHAN_SWITCH, enable); +} + +int hgic_fwctrl_set_mcast_key(struct hgic_fwctrl *ctrl, u8 *mcast_key) +{ + ARG_DUP(mcast_key, 0); + return hgic_fwctrl_set_bytes(ctrl, HGIC_CMD_SET_MCAST_KEY, mcast_key, STR_LEN(mcast_key)); +} + +int hgic_fwctrl_set_reassoc_wkhost(struct hgic_fwctrl *ctrl, u8 enable) +{ + return hgic_fwctrl_set_byte(ctrl, HGIC_CMD_SET_REASSOC_WKHOST, enable); +} + +int hgic_fwctrl_set_wakeup_io(struct hgic_fwctrl *ctrl, u8 io, u8 edge) +{ + u8 val[2] = {io, edge}; + return hgic_fwctrl_set_bytes(ctrl, HGIC_CMD_SET_WAKEUP_IO, val, 2); +} + +int hgic_fwctrl_set_dbginfo_output(struct hgic_fwctrl *ctrl, u8 enable) +{ + return hgic_fwctrl_set_byte(ctrl, HGIC_CMD_DBGINFO_OUTPUT, enable); +} + +int hgic_fwctrl_set_sysdbg(struct hgic_fwctrl *ctrl, u8 *cmd) +{ + return hgic_fwctrl_set_bytes(ctrl, HGIC_CMD_SET_SYSDBG, cmd, STR_LEN(cmd)); +} + +int hgic_fwctrl_set_autosleep_time(struct hgic_fwctrl *ctrl, u8 time) +{ + return hgic_fwctrl_set_byte(ctrl, HGIC_CMD_SET_AUTO_SLEEP_TIME, time); +} + +int hgic_fwctrl_get_key_mgmt(struct hgic_fwctrl *ctrl, u8 *ssid, u32 size) +{ + return hgic_fwctrl_get_bytes(ctrl, HGIC_CMD_GET_KEY_MGMT, ssid, size); +} + +int hgic_fwctrl_set_supper_pwr(struct hgic_fwctrl *ctrl, u8 enable) +{ + return hgic_fwctrl_set_byte(ctrl, HGIC_CMD_SET_SUPPER_PWR, enable); +} + +int hgic_fwctrl_set_repeater_ssid(struct hgic_fwctrl *ctrl, u8 *ssid) +{ + return hgic_fwctrl_set_bytes(ctrl, HGIC_CMD_SET_REPEATER_SSID, ssid, STR_LEN(ssid)); +} + +int hgic_fwctrl_set_repeater_psk(struct hgic_fwctrl *ctrl, u8 *wpa_psk) +{ + return hgic_fwctrl_set_bytes(ctrl, HGIC_CMD_SET_REPEATER_PSK, wpa_psk, STR_LEN(wpa_psk)); +} + +int hgic_fwctrl_set_auto_save(struct hgic_fwctrl *ctrl, u8 enable) +{ + return hgic_fwctrl_set_byte(ctrl, HGIC_CMD_CFG_AUTO_SAVE, enable); +} + +int hgic_fwctrl_set_pair_autostop(struct hgic_fwctrl *ctrl, u8 enable) +{ + return hgic_fwctrl_set_byte(ctrl, HGIC_CMD_SET_PAIR_AUTOSTOP, enable); +} + +int hgic_fwctrl_send_cust_mgmt(struct hgic_fwctrl *ctrl, u8 *data, u32 len) +{ + return hgic_fwctrl_set_bytes(ctrl, HGIC_CMD_SEND_CUST_MGMT, data, len); +} + +int hgic_fwctrl_get_battery_level(struct hgic_fwctrl *ctrl) +{ + return hgic_fwctrl_get_short_val(ctrl, HGIC_CMD_GET_BATTERY_LEVEL); +} + +int hgic_fwctrl_set_dcdc13v(struct hgic_fwctrl *ctrl, u8 enable) +{ + return hgic_fwctrl_set_byte(ctrl, HGIC_CMD_SET_DCDC13, enable); +} + +int hgic_fwctrl_set_acktmo(struct hgic_fwctrl *ctrl, u32 tmo) +{ + return hgic_fwctrl_set_int_val(ctrl, HGIC_CMD_SET_ACKTMO, tmo); +} + +int hgic_fwctrl_get_module_type(struct hgic_fwctrl *ctrl) +{ + return hgic_fwctrl_get_short_val(ctrl, HGIC_CMD_GET_MODULETYPE); +} + +int hgic_fwctrl_set_pa_pwrctl_dis(struct hgic_fwctrl *ctrl, u8 dis) +{ + return hgic_fwctrl_set_byte(ctrl, HGIC_CMD_PA_PWRCTRL_DIS, dis); +} + +int hgic_fwctrl_set_dhcpc(struct hgic_fwctrl *ctrl, u8 en) +{ + return hgic_fwctrl_set_byte(ctrl, HGIC_CMD_SET_DHCPC, en); +} + +int hgic_fwctrl_get_dhcpc_result(struct hgic_fwctrl *ctrl, u8 *buff, int len) +{ + return hgic_fwctrl_get_bytes(ctrl, HGIC_CMD_GET_DHCPC_RESULT, buff, len); +} + +int hgic_fwctrl_set_wkdata_mask(struct hgic_fwctrl *ctrl, u16 offset, u8 *mask, u8 mask_len) +{ + u8 data[128]; + if(mask_len > 16) mask_len = 16; + memset(data, 0, sizeof(data)); + put_unaligned_le16(offset, data); + memcpy(data + 2, mask, mask_len); + return hgic_fwctrl_set_bytes(ctrl, HGIC_CMD_SET_WKUPDATA_MASK, data, mask_len + 2); +} + +int hgic_fwctrl_get_wkdata_buff(struct hgic_fwctrl *ctrl, u8 *buff, int len) +{ + return hgic_fwctrl_get_bytes(ctrl, HGIC_CMD_GET_WKDATA_BUFF, buff, len); +} + +int hgic_fwctrl_get_disassoc_reason(struct hgic_fwctrl *ctrl) +{ + return hgic_fwctrl_get_short_val(ctrl, HGIC_CMD_GET_DISASSOC_REASON); +} + +int hgic_fwctrl_set_wkdata_save(struct hgic_fwctrl *ctrl, u8 save) +{ + return hgic_fwctrl_set_byte(ctrl, HGIC_CMD_SET_WKUPDATA_SAVEEN, save); +} + +int hgic_fwctrl_set_cust_driver_data(struct hgic_fwctrl *ctrl, u8 *data, u32 len) +{ + return hgic_fwctrl_set_bytes(ctrl, HGIC_CMD_SET_CUST_DRIVER_DATA, data, len); +} + +int hgic_fwctrl_set_mcast_txparam(struct hgic_fwctrl *ctrl, struct hgic_mcast_txparam *param) +{ + return hgic_fwctrl_set_bytes(ctrl, HGIC_CMD_SET_MCAST_TXPARAM, (u8 *)param, sizeof(struct hgic_mcast_txparam)); +} + +int hgic_fwctrl_set_freqinfo(struct hgic_fwctrl *ctrl, u8 *data, u32 len) +{ + return hgic_fwctrl_set_bytes(ctrl, HGIC_CMD_SET_STA_FREQINFO, data, len); +} + +int hgic_fwctrl_reset_sta(struct hgic_fwctrl *ctrl, u8 *addr) +{ + return hgic_fwctrl_set_bytes(ctrl, HGIC_CMD_SET_RESET_STA, addr, 6); +} + +int hgic_fwctrl_set_ant_auto(struct hgic_fwctrl *ctrl, u8 en) +{ + return hgic_fwctrl_set_byte(ctrl, HGIC_CMD_SET_ANT_AUTO, en); +} + +int hgic_fwctrl_select_ant(struct hgic_fwctrl *ctrl, u8 ant) +{ + return hgic_fwctrl_set_byte(ctrl, HGIC_CMD_SET_ANT_SEL, ant); +} + +int hgic_fwctrl_get_ant_sel(struct hgic_fwctrl *ctrl) +{ + return hgic_fwctrl_get_short_val(ctrl, HGIC_CMD_GET_ANT_SEL); +} + +int hgic_fwctrl_set_wkhost_reasons(struct hgic_fwctrl *ctrl, u8 *reasons, u8 count) +{ + return hgic_fwctrl_set_bytes(ctrl, HGIC_CMD_SET_WKUP_HOST_REASON, reasons, count); +} + +int hgic_fwctrl_set_mac_filter(struct hgic_fwctrl *ctrl, u8 en) +{ + return hgic_fwctrl_set_byte(ctrl, HGIC_CMD_SET_MAC_FILTER_EN, en); +} + +int hgic_fwctrl_set_atcmd(struct hgic_fwctrl *ctrl, char *atcmd) +{ + return hgic_fwctrl_set_bytes(ctrl, HGIC_CMD_SET_ATCMD, atcmd, strlen(atcmd)); +} + +int hgic_fwctrl_set_roaming(struct hgic_fwctrl *ctrl, u8 en, u8 same_freq) +{ + u8 vals[2] = {en, same_freq}; + return hgic_fwctrl_set_bytes(ctrl, HGIC_CMD_SET_ROAMING, vals, 2); +} + +int hgic_fwctrl_set_ap_hide(struct hgic_fwctrl *ctrl, u8 hide) +{ + return hgic_fwctrl_set_byte(ctrl, HGIC_CMD_SET_AP_HIDE, hide); +} + +int hgic_fwctrl_set_frm_tx_maxcnt(struct hgic_fwctrl *ctrl, u8 txcnt) +{ + return hgic_fwctrl_set_byte(ctrl, HGIC_CMD_SET_MAX_TCNT, txcnt); +} + +int hgic_fwctrl_set_assert_holdup(struct hgic_fwctrl *ctrl, u8 holdup) +{ + return hgic_fwctrl_set_byte(ctrl, HGIC_CMD_SET_ASSERT_HOLDUP, holdup); +} + diff --git a/utils/fwctrl.h b/utils/fwctrl.h new file mode 100644 index 0000000..c695308 --- /dev/null +++ b/utils/fwctrl.h @@ -0,0 +1,196 @@ + +#ifndef _HGIC_FWCTRL_H_ +#define _HGIC_FWCTRL_H_ + +struct hgic_fw_info; + +#include +#include + +//command response +struct hgic_cmd_response { + struct list_head list; + unsigned short cookie; + struct completion cmpl; + struct sk_buff *skb; +}; + +struct hgic_fwctrl_params { + u8 *country_code; + u8 *ssid; + u8 *bssid; + u8 *bssid_filter; + u8 *key_mgmt; + u8 *wpa_psk; + u8 *mcast_key; + u8 *mode; + u8 *mac_addr; + u8 *paired_stas; + u8 *chan_list; + u8 channel, chan_bw, bss_bw, tx_mcs, tx_bw; + u16 rts_threshold, frag_threshold; + u16 freq_start, freq_end; + u16 listen_interval; + u16 center_freq; + u16 beacon_int; + u16 ethertype; + u8 acs: 2, acs_tmo: 6; + u8 chan_cnt, txpower, primary_chan, agg_cnt; + u8 ps_mode, wkio_mode, ps_connect, ps_connect_roundup; + u32 dtim_period, bss_max_idle; + u16 aplost_time; +}; + +struct hgic_fwctrl { + struct device *dev; + u16 cookie; + spinlock_t lock; /*ctrl packet pending lock*/ + struct list_head pd_list; /*ctrl packet pending queue (for sync cmd)*/ + struct sk_buff_head rxq; /*fw ctrl packet rx queue*/ + struct sk_buff_head txq; /*fw ctrl packet tx queue*/ + struct work_struct work; /*fw ctrl rx packet process work*/ + struct workqueue_struct *wq; + struct hgic_fwctrl_params param; + struct work_struct flush_work; + u8 qc_mode; +}; + +/* +int hgic_fwctrl_set_byte(struct hgic_fwctrl *ctrl, int cmd_id, u8 val); +int hgic_fwctrl_set_int_val(struct hgic_fwctrl *ctrl, int cmd_id, int val); +int hgic_fwctrl_get_int_val(struct hgic_fwctrl *ctrl, int cmd_id); +int hgic_fwctrl_get_bytes(struct hgic_fwctrl *ctrl, int cmd_id, char *buff, int len); +int hgic_fwctrl_do_cmd(struct hgic_fwctrl *ctrl, int cmd_id, char *data, int len, char *buff, int size); +int hgic_fwctrl_rx_proc(struct hgic_fwctrl *ctrl, struct sk_buff *skb); +struct sk_buff *hgic_fwctrl_send_data(struct hgic_fwctrl *ctrl, struct sk_buff *skb, struct hgic_cmd_response *resp, u32 timeout); +void hgic_fwctrl_clear_pdlist(struct hgic_fwctrl *ctrl); +int hgic_fwctrl_set_bytes(struct hgic_fwctrl *ctrl, int cmd_id, char *data, int len); +*/ +u16 hgic_ctrl_cookie(struct hgic_fwctrl *ctrl); +struct sk_buff *hgic_fwctrl_send_data(struct hgic_fwctrl *ctrl, struct sk_buff *skb, struct hgic_cmd_response *resp, u32 timeout); + +void hgic_fwctrl_init(struct hgic_fwctrl *ctrl, void *dev); +void hgic_fwctrl_release(struct hgic_fwctrl *ctrl); +void hgic_fwctrl_rx(struct hgic_fwctrl *ctrl, struct sk_buff *skb); +void hgic_rx_fw_event(struct hgic_fwctrl *ctrl, struct sk_buff *skb); +void hgic_schedule(struct hgic_fwctrl *ctrl); +void hgic_fwctrl_flush_param(struct hgic_fwctrl *ctrl); +int hgic_fwctrl_testmode_cmd(struct hgic_fwctrl *ctrl, u8 *cmd, u32 size); +int hgic_fwctrl_get_status(struct hgic_fwctrl *ctrl, u8 *buff, u32 len); +int hgic_fwctrl_get_conn_state(struct hgic_fwctrl *ctrl); +int hgic_fwctrl_get_fwinfo(struct hgic_fwctrl *ctrl, struct hgic_fw_info *info); +int hgic_fwctrl_set_countryregion(struct hgic_fwctrl *ctrl, u8 *country_code); +int hgic_fwctrl_set_ssid(struct hgic_fwctrl *ctrl, u8 *ssid); +int hgic_fwctrl_set_bssid(struct hgic_fwctrl *ctrl, u8 *bssid); +int hgic_fwctrl_set_channel(struct hgic_fwctrl *ctrl, u32 channel); +int hgic_fwctrl_set_bssid_filter(struct hgic_fwctrl *ctrl, u8 *filter); +int hgic_fwctrl_set_rts_threshold(struct hgic_fwctrl *ctrl, u32 rts_threshold); +int hgic_fwctrl_set_frag_threshold(struct hgic_fwctrl *ctrl, u32 frag_threshold); +int hgic_fwctrl_set_key_mgmt(struct hgic_fwctrl *ctrl, u8 *key_mgmt); +int hgic_fwctrl_set_wpa_psk(struct hgic_fwctrl *ctrl, u8 *psk); +int hgic_fwctrl_set_wbnat(struct hgic_fwctrl *ctrl, u32 enable); +int hgic_fwctrl_set_freq_range(struct hgic_fwctrl *ctrl, u32 freq_start, u32 freq_end, u32 bss_bw); +int hgic_fwctrl_set_bss_bw(struct hgic_fwctrl *ctrl, u8 bss_bw); +int hgic_fwctrl_set_tx_bw(struct hgic_fwctrl *ctrl, u8 tx_bw); +int hgic_fwctrl_set_tx_mcs(struct hgic_fwctrl *ctrl, u8 tx_mcs); +int hgic_fwctrl_set_acs(struct hgic_fwctrl *ctrl, u8 acs, u8 acs_tm); +int hgic_fwctrl_set_bgrssi(struct hgic_fwctrl *ctrl, u8 bgrssi); +int hgic_fwctrl_set_chan_list(struct hgic_fwctrl *ctrl, u16 *chan_list, u32 cnt); +int hgic_fwctrl_set_mode(struct hgic_fwctrl *ctrl, u8 *mode); +int hgic_fwctrl_set_paired_stas(struct hgic_fwctrl *ctrl, u8 *stas, u32 len); +int hgic_fwctrl_set_pairing(struct hgic_fwctrl *ctrl, u8 start); +int hgic_fwctrl_open_dev(struct hgic_fwctrl *ctrl); +int hgic_fwctrl_close_dev(struct hgic_fwctrl *ctrl); +int hgic_fwctrl_set_txpower(struct hgic_fwctrl *ctrl, u32 tx_power); +int hgic_fwctrl_get_txpower(struct hgic_fwctrl *ctrl); +int hgic_fwctrl_set_listen_interval(struct hgic_fwctrl *ctrl, u32 listen_interval); +int hgic_fwctrl_set_center_freq(struct hgic_fwctrl *ctrl, u32 channel); +int hgic_fwctrl_set_tx_count(struct hgic_fwctrl *ctrl, u32 short_frm_tx_count, u32 long_frm_tx_count); +int hgic_fwctrl_set_key(struct hgic_fwctrl *ctrl, u8 cmd, u16 aid, u8 *key, u8 len); +int hgic_fwctrl_add_sta(struct hgic_fwctrl *ctrl, u16 aid, u8 *addr); +int hgic_fwctrl_del_sta(struct hgic_fwctrl *ctrl, u32 aid); +int hgic_fwctrl_set_primary_chan(struct hgic_fwctrl *ctrl, u8 primary_chan); +int hgic_fwctrl_set_aid(struct hgic_fwctrl *ctrl, u32 aid); +int hgic_fwctrl_set_mac(struct hgic_fwctrl *ctrl, u8 *mac); +int hgic_fwctrl_get_scan_list(struct hgic_fwctrl *ctrl, u8 *buff, u32 size); +int hgic_fwctrl_scan(struct hgic_fwctrl *ctrl); +int hgic_fwctrl_set_txq_param(struct hgic_fwctrl *ctrl, u8 *txq, u32 size); +int hgic_fwctrl_get_temperature(struct hgic_fwctrl *ctrl); +int hgic_fwctrl_enter_sleep(struct hgic_fwctrl *ctrl, u8 sleep); +int hgic_fwctrl_get_sta_list(struct hgic_fwctrl *ctrl, struct hgic_sta_info *sta_list, u32 size); +int hgic_fwctrl_set_beacon_int(struct hgic_fwctrl *ctrl, u32 beacon_int); +int hgic_fwctrl_get_ssid(struct hgic_fwctrl *ctrl, u8 *ssid, u32 size); +int hgic_fwctrl_get_wpapsk(struct hgic_fwctrl *ctrl, u8 *psk, u32 size); +int hgic_fwctrl_save_cfg(struct hgic_fwctrl *ctrl); +int hgic_fwctrl_join_group(struct hgic_fwctrl *ctrl, u8 *addr, u8 aid); +int hgic_fwctrl_set_ethertype(struct hgic_fwctrl *ctrl, u16 type); +int hgic_fwctrl_get_sta_count(struct hgic_fwctrl *ctrl); +int hgic_fwctrl_get_bss_bw(struct hgic_fwctrl *ctrl); +int hgic_fwctrl_get_freq_range(struct hgic_fwctrl *ctrl, u32 *freq_start, u32 *freq_end, u32 *bss_bw); +int hgic_fwctrl_get_chan_list(struct hgic_fwctrl *ctrl, u16 *chan_list, u16 count); +int hgic_fwctrl_get_agg_cnt(struct hgic_fwctrl *ctrl); +int hgic_fwctrl_set_agg_cnt(struct hgic_fwctrl *ctrl, u8 agg_cnt); +int hgic_fwctrl_set_ps_addr(struct hgic_fwctrl *ctrl, u32 dport); +int hgic_fwctrl_wakeup_sta(struct hgic_fwctrl *ctrl, u8 *addr); +int hgic_fwctrl_set_ps_heartbeat(struct hgic_fwctrl *ctrl, u32 ipaddr, u32 dport, u32 period, u32 hb_tmo); +int hgic_fwctrl_set_ps_heartbeat_resp(struct hgic_fwctrl *ctrl, u8 *data, u32 size); +int hgic_fwctrl_set_ps_wakeup_data(struct hgic_fwctrl *ctrl, u8 *data, u32 size); +int hgic_fwctrl_set_ps_connect(struct hgic_fwctrl *ctrl, u8 period, u8 roundup); +int hgic_fwctrl_set_ps_connect_count(struct hgic_fwctrl *ctrl, u8 try_cnt); +int hgic_fwctrl_set_ps_connect_time(struct hgic_fwctrl *ctrl, u32 time); +int hgic_fwctrl_get_ps_connect(struct hgic_fwctrl *ctrl); +int hgic_fwctrl_get_ps_connect_count(struct hgic_fwctrl *ctrl); +int hgic_fwctrl_get_ps_connect_time(struct hgic_fwctrl *ctrl); +int hgic_fwctrl_radio_onoff(struct hgic_fwctrl *ctrl, u8 onoff); +int hgic_fwctrl_set_bss_max_idle(struct hgic_fwctrl *ctrl, u32 max_idle); +int hgic_fwctrl_set_wkio_mode(struct hgic_fwctrl *ctrl, u8 mode); +int hgic_fwctrl_set_ps_mode(struct hgic_fwctrl *ctrl, u8 mode); +int hgic_fwctrl_set_load_def(struct hgic_fwctrl *ctrl, u8 rst); +int hgic_fwctrl_disassoc_sta(struct hgic_fwctrl *ctrl, u8 *addr); +int hgic_fwctrl_set_dtim_period(struct hgic_fwctrl *ctrl, u32 period); +int hgic_fwctrl_set_aplost_time(struct hgic_fwctrl *ctrl, u32 aplost_time); +int hgic_fwctrl_get_wkreason(struct hgic_fwctrl *ctrl); +int hgic_fwctrl_unpair(struct hgic_fwctrl *ctrl, u8 *addr); +int hgic_fwctrl_set_auto_chswitch(struct hgic_fwctrl *ctrl, u8 enable); +int hgic_fwctrl_set_mcast_key(struct hgic_fwctrl *ctrl, u8 *mcast_key); +int hgic_fwctrl_set_reassoc_wkhost(struct hgic_fwctrl *ctrl, u8 enable); +int hgic_fwctrl_set_wakeup_io(struct hgic_fwctrl *ctrl, u8 io, u8 edge); +int hgic_fwctrl_set_dbginfo_output(struct hgic_fwctrl *ctrl, u8 enable); +int hgic_fwctrl_set_sysdbg(struct hgic_fwctrl *ctrl, u8 *cmd); +int hgic_fwctrl_set_autosleep_time(struct hgic_fwctrl *ctrl, u8 time); +int hgic_fwctrl_get_key_mgmt(struct hgic_fwctrl *ctrl, u8 *ssid, u32 size); +int hgic_fwctrl_get_bssid(struct hgic_fwctrl *ctrl, u8 *bssid); +int hgic_fwctrl_set_supper_pwr(struct hgic_fwctrl *ctrl, u8 enable); +int hgic_fwctrl_set_repeater_ssid(struct hgic_fwctrl *ctrl, u8 *ssid); +int hgic_fwctrl_set_repeater_psk(struct hgic_fwctrl *ctrl, u8 *wpa_psk); +int hgic_fwctrl_set_auto_save(struct hgic_fwctrl *ctrl, u8 enable); +int hgic_fwctrl_set_pair_autostop(struct hgic_fwctrl *ctrl, u8 enable); +int hgic_fwctrl_send_cust_mgmt(struct hgic_fwctrl *ctrl, u8 *data, u32 len); +int hgic_fwctrl_get_battery_level(struct hgic_fwctrl *ctrl); +int hgic_fwctrl_set_dcdc13v(struct hgic_fwctrl *ctrl, u8 enable); +int hgic_fwctrl_set_acktmo(struct hgic_fwctrl *ctrl, u32 tmo); +int hgic_fwctrl_get_module_type(struct hgic_fwctrl *ctrl); +int hgic_fwctrl_set_pa_pwrctl_dis(struct hgic_fwctrl *ctrl, u8 dis); +int hgic_fwctrl_set_dhcpc(struct hgic_fwctrl *ctrl, u8 en); +int hgic_fwctrl_get_dhcpc_result(struct hgic_fwctrl *ctrl, u8* buff, int len); +int hgic_fwctrl_set_wkdata_mask(struct hgic_fwctrl *ctrl, u16 offset, u8 *mask, u8 mask_len); +int hgic_fwctrl_get_wkdata_buff(struct hgic_fwctrl *ctrl, u8* buff, int len); +int hgic_fwctrl_get_disassoc_reason(struct hgic_fwctrl *ctrl); +int hgic_fwctrl_set_wkdata_save(struct hgic_fwctrl *ctrl, u8 save); +int hgic_fwctrl_set_cust_driver_data(struct hgic_fwctrl *ctrl, u8 *data, u32 len); +int hgic_fwctrl_set_mcast_txparam(struct hgic_fwctrl *ctrl, struct hgic_mcast_txparam *param); +int hgic_fwctrl_set_freqinfo(struct hgic_fwctrl *ctrl, u8 *data, u32 len); +int hgic_fwctrl_reset_sta(struct hgic_fwctrl *ctrl, u8 *addr); +int hgic_fwctrl_set_ant_auto(struct hgic_fwctrl *ctrl, u8 en); +int hgic_fwctrl_select_ant(struct hgic_fwctrl *ctrl, u8 ant); +int hgic_fwctrl_get_ant_sel(struct hgic_fwctrl *ctrl); +int hgic_fwctrl_set_wkhost_reasons(struct hgic_fwctrl *ctrl, u8 *reasons, u8 len); +int hgic_fwctrl_set_mac_filter(struct hgic_fwctrl *ctrl, u8 en); +int hgic_fwctrl_set_atcmd(struct hgic_fwctrl *ctrl, char *atcmd); +int hgic_fwctrl_set_roaming(struct hgic_fwctrl *ctrl, u8 en, u8 same_freq); +int hgic_fwctrl_set_ap_hide(struct hgic_fwctrl *ctrl, u8 hide); +int hgic_fwctrl_set_frm_tx_maxcnt(struct hgic_fwctrl *ctrl, u8 txcnt); +int hgic_fwctrl_set_assert_holdup(struct hgic_fwctrl *ctrl, u8 holdup); + +#endif + diff --git a/utils/fwdl.c b/utils/fwdl.c new file mode 100644 index 0000000..cde155f --- /dev/null +++ b/utils/fwdl.c @@ -0,0 +1,438 @@ +#ifdef __RTOS__ +#include +#include +#include +#else +#include +#include +#include +#include +#include +#include +#include +#include +#endif + +#include "../hgic_def.h" +#include "fwinfo.h" +#include "fwctrl.h" +#include "fwdl.h" +#include "utils.h" + +#define BOOT_CMD_KEY "@huge-ic" +#define BOOT_CMD_KEY_SIZE 8 + +void hgic_bootdl_release(struct hgic_bootdl *hg, int mode) +{ + if (hg == NULL) { + return; + } + + hgic_dbg("Enter ...\n"); + + hgic_dbg("Leave ...\n"); +} + +u32 hgic_bootdl_init(struct hgic_bootdl *hg_fwdl, struct hgic_bus *bus, struct hgic_fwctrl *ctrl) +{ + if (hg_fwdl == NULL) { + printk("%s:%d:Input para error!\n", __FUNCTION__, __LINE__); + return EINVAL; + } + + hgic_dbg("Enter ...\n"); + memset(hg_fwdl, 0, sizeof(struct hgic_bootdl)); + hg_fwdl->checksum_mode = bus->bootdl_cksum; + hg_fwdl->frag_size = bus->bootdl_pktlen; + hg_fwdl->ctrl = ctrl; + hg_fwdl->bus = bus; + hgic_dbg("Leave ...each packet len:%d\n", hg_fwdl->frag_size); + return 0; +} + +static u8 crc8(char *p_buf, u32 len) +{ + return 0; +} + +static struct sk_buff *hgic_bootdl_alloc_cmd_skb(struct hgic_bootdl *hg, u8 cmd_id) +{ + struct sk_buff *skb = NULL; + struct hgic_bootdl_cmd_hdr *cmd_hdr = NULL; + + skb = dev_alloc_skb(sizeof(struct hgic_bootdl_cmd_hdr) + 4); + if (!skb) { + return skb; + } + cmd_hdr = (struct hgic_bootdl_cmd_hdr *)skb->data; + cmd_hdr->cmd = cmd_id; + cmd_hdr->cmd_len = 12; + cmd_hdr->cmd_flag = hg->checksum_mode; + skb_put(skb, sizeof(struct hgic_bootdl_cmd_hdr)); + return skb; +} + +static u8 hgic_bootdl_cmd_check_val(u8 check_mode, u8 *p_buf, u32 len) +{ + u8 check_value = 0; + u32 i = 0; + + if (HGIC_BUS_BOOTDL_CHECK_SUM == check_mode) { + for (i = 0; i < len; i++) { + check_value += *p_buf++; + } + } else if (HGIC_BUS_BOOTDL_CHECK_CRC8 == check_mode) { + check_value = crc8(p_buf, len); + } else if (HGIC_BUS_BOOTDL_CHECK_0XFD == check_mode) { + check_value = 0xFD; + } else { + return 0; + } + return check_value; +} + +static int hgic_bootdl_cmd_rsp_handle(unsigned char cmd_flag, unsigned char cmd, struct hgic_bootdl_resp_hdr *resp) +{ + if (resp->check != hgic_bootdl_cmd_check_val(cmd_flag, (u8 *)&resp->cmd, 8)) { + hgic_dbg("Checksum error,return HG_BOOTDL_ERR_RSP_PACKET!\r\n"); + return HG_BOOTDL_ERR_RSP_PACKET; + } + if (resp->cmd != cmd) { + hgic_dbg("Cmd not match,resp_cmd:<%d>,cmd:<%d>\n", resp->cmd, cmd); + return HG_BOOTDL_ERR_RSP_PACKET; + } + + return resp->rsp; +} + + +struct sk_buff *hgic_bootdl_send_cmd(struct hgic_bootdl *hg_fwdl, struct sk_buff *skb, u32 timeout) +{ + struct hgic_bootdl_cmd_hdr *hdr = NULL; + struct hgic_cmd_response resp; + + hdr = (struct hgic_bootdl_cmd_hdr *)skb->data; + hdr->hdr.magic = HGIC_HDR_TX_MAGIC; + hdr->hdr.type = HGIC_HDR_TYPE_BOOTDL; + hdr->hdr.length = skb->len; + hdr->hdr.cookie = hgic_ctrl_cookie(hg_fwdl->ctrl); + + //printk("hg_fwdl->ctrl=%p\r\n", hg_fwdl->ctrl); + memset(&resp, 0, sizeof(resp)); + resp.cookie = hdr->hdr.cookie; + hg_fwdl->last_cookie = resp.cookie; + return hgic_fwctrl_send_data(hg_fwdl->ctrl, skb, &resp, timeout); +} + +struct sk_buff *hgic_bootdl_send_data(struct hgic_bootdl *hg_fwdl, struct sk_buff *skb, u32 timeout) +{ + struct hgic_bootdl_cmd_hdr *hdr = NULL; + struct hgic_cmd_response resp; + + hdr = (struct hgic_bootdl_cmd_hdr *)skb_push(skb, sizeof(struct hgic_bootdl_cmd_hdr)); + hdr->hdr.magic = HGIC_HDR_TX_MAGIC; + hdr->hdr.type = HGIC_HDR_TYPE_BOOTDL_DATA; + memset(&resp, 0, sizeof(struct hgic_cmd_response)); + resp.cookie = hg_fwdl->last_cookie; + return hgic_fwctrl_send_data(hg_fwdl->ctrl, skb, &resp, timeout); +} + +static int hgic_bootdl_parse_fw(struct hgic_bootdl *hg, const char *fw_name) +{ + const struct firmware *fw; + int ret = 0; + s32 err_code = 0; + + ret = request_firmware(&fw, fw_name, hg->ctrl->dev); + if (ret) { + printk("%s,%d:request_firmware failed!ret:%d\n", __FUNCTION__, __LINE__, ret); + return ret; + } + hg->fw = (struct firmware *)fw; + + hg->fw_info.write_addr = hgic_get_fw_dl_addr(fw->data, &err_code); + if (err_code) { + hgic_dbg("hgic_get_fw_dl_addr error!\n"); + return err_code; + } + + hg->fw_info.aes_en = hgic_get_fw_aes_en(fw->data, &err_code); + if (err_code) { + hgic_dbg("hgic_get_aes_en error!\n"); + return err_code; + } + + hg->fw_info.crc_en = hgic_get_fw_crc_en(fw->data, &err_code); + if (err_code) { + hgic_dbg("hgic_get_fw_crc_en error!\n"); + return err_code; + } + + hg->fw_info.local_crc32 = hgic_get_fw_local_crc32(fw->data, &err_code); + if (err_code) { + hgic_dbg("hgic_get_fw_local_crc32 error!\n"); + return err_code; + } + + hg->fw_info.hdr_len = hgic_get_fw_code_offset(fw->data, &err_code); + if (err_code) { + hgic_dbg("hgic_get_fw_code_offset error!\n"); + return err_code; + } + + hg->fw_info.sp_cfg = 1; + hg->fw_info.fw_len = fw->size - hg->fw_info.hdr_len; + + hgic_dbg("firmware hdr len : %d\r\n", hg->fw_info.hdr_len); + hgic_dbg("firmware run addr : %x\r\n", hg->fw_info.write_addr); + hgic_dbg("firmware size : %d\r\n", hg->fw_info.fw_len); + hgic_dbg("firmware aes_en:%d,crc_en:%d\r\n", hg->fw_info.aes_en, hg->fw_info.crc_en); + + return 0; +} + +static int hgic_bootdl_send_fw(struct hgic_bootdl *hg, struct sk_buff *skb, u32 tmo) +{ + struct sk_buff *resp_skb = NULL; + struct hgic_bootdl_resp_hdr *data_resp = NULL; + int ret = 0; + + if (skb == NULL || hg == NULL) { + hgic_dbg("input para error\n"); + return EINVAL; + } + + resp_skb = hgic_bootdl_send_data(hg, skb, tmo); + if (resp_skb) { + data_resp = (struct hgic_bootdl_resp_hdr *)resp_skb->data; + ret = data_resp->rsp; + if (ret != 0) { + hgic_dbg("Send fw data error!ret:<%d>\n", ret); + } else { + hgic_dbg("Send fw data success!\n"); + } + dev_kfree_skb_any(resp_skb); + return ret; + } else { + hgic_dbg("send fw data error, no resp!\n"); + return EFAULT; + } +} + +static int hgic_bootdl_send_cmd_tmo(struct hgic_bootdl *hg, + struct sk_buff *skb, + u32 timeout, + u32 *resp_data) +{ + struct sk_buff *resp = NULL; + struct hgic_bootdl_cmd_hdr *cmd_hdr = (struct hgic_bootdl_cmd_hdr *)skb->data; + struct hgic_bootdl_resp_hdr *boot_resp = NULL; + int ret = 0; + unsigned char cmd_flag = 0; + unsigned char cmd = 0; + + if (hg == NULL || skb == NULL) { + return -EINVAL; + } + + cmd_flag = cmd_hdr->cmd_flag; + cmd = cmd_hdr->cmd; + resp = hgic_bootdl_send_cmd(hg, skb, timeout); + if (resp) { + boot_resp = (struct hgic_bootdl_resp_hdr *)resp->data; + ret = hgic_bootdl_cmd_rsp_handle(cmd_flag, cmd, boot_resp); + if (ret != 0 && ret != HG_BOOTDL_RSP_ERR_IN_FW) { + hgic_err("cmd %d, error:%d\n", cmd, ret); + dev_kfree_skb_any(resp); + return ret; + } + if (resp_data != NULL) { + *resp_data = get_unaligned_le32(boot_resp->rsp_data); + } + dev_kfree_skb_any(resp); + } else { + hgic_err("cmd: %d, no responce!!\r\n", cmd); + ret = -1; + } + return ret; +} + +int hgic_bootdl_cmd_enter(struct hgic_bootdl *hg) +{ + struct sk_buff *skb = NULL; + struct hgic_bootdl_cmd_hdr *cmd_hdr = NULL; + int ret = 0; + + skb = hgic_bootdl_alloc_cmd_skb(hg, HG_BOOTDL_CMD_ENTER); + if (skb) { + cmd_hdr = (struct hgic_bootdl_cmd_hdr *)skb->data; + memcpy((void *)cmd_hdr->addr, (void *)BOOT_CMD_KEY, BOOT_CMD_KEY_SIZE); + cmd_hdr->check = hgic_bootdl_cmd_check_val(hg->checksum_mode, (u8 *)&cmd_hdr->cmd, 11); + } else { + hgic_err("malloc skb failed!\n"); + return ENOMEM; + } + ret = hgic_bootdl_send_cmd_tmo(hg, skb, HG_BOOTDL_CMD_NORMAL_TMO, NULL); + if (ret == 0 || ret == HG_BOOTDL_RSP_ERR_IN_FW) { + if (ret == HG_BOOTDL_RSP_ERR_IN_FW) { + //hgic_err("In firmware state\n"); + return STATE_FW; + } else { + //hgic_err("In bootdl state\n"); + return STATE_BOOT; + } + } else { + hgic_err("failed! ret:%d\n", ret); + return ret; + } +} + +static int hgic_bootdl_cmd_write_memory(struct hgic_bootdl *hg, u32 write_addr, u32 data_len) +{ + struct sk_buff *skb = NULL; + struct hgic_bootdl_cmd_hdr *cmd_hdr = NULL; + + skb = hgic_bootdl_alloc_cmd_skb(hg, HG_BOOTDL_CMD_WRITE_MEM); + if (skb) { + cmd_hdr = (struct hgic_bootdl_cmd_hdr *)skb->data; + put_unaligned_le32(write_addr, cmd_hdr->addr); + if (hg->bus->type == HGIC_BUS_SDIO) { + put_unaligned_le32(data_len, cmd_hdr->len); + } else { + put_unaligned_le32((data_len + 1024), cmd_hdr->len); + } + cmd_hdr->check = hgic_bootdl_cmd_check_val(hg->checksum_mode, (u8 *)&cmd_hdr->cmd, 11); + } else { + hgic_dbg("malloc skb failed!\n"); + return ENOMEM; + } + return hgic_bootdl_send_cmd_tmo(hg, skb, HG_BOOTDL_CMD_NORMAL_TMO, NULL); +} + +static int hgic_bootdl_cmd_run(struct hgic_bootdl *hg) +{ + struct sk_buff *skb = NULL; + struct hgic_bootdl_cmd_hdr *cmd_hdr = NULL; + u32 temp = 0; + + skb = hgic_bootdl_alloc_cmd_skb(hg, HG_BOOTDL_CMD_RUN); + if (skb) { + cmd_hdr = (struct hgic_bootdl_cmd_hdr *)skb->data; + put_unaligned_le32(hg->fw_info.write_addr, cmd_hdr->addr); + + temp = hg->fw_info.fw_len; + if (hg->fw_info.aes_en) { + temp |= HG_BOOT_CMD_RUN_PREACT_AES_DEC; + } + if (hg->fw_info.crc_en) { + temp |= HG_BOOT_CMD_RUN_PREACT_CRC_CHK; + } + if (hg->fw_info.sp_cfg) { + temp |= HG_BOOT_CMD_RUN_PREACT_SP_CFG; + } + put_unaligned_le32(temp, cmd_hdr->len); + + cmd_hdr->check = hgic_bootdl_cmd_check_val(hg->checksum_mode, (u8 *)&cmd_hdr->cmd, 11); + } else { + hgic_dbg("malloc skb failed!\n"); + return ENOMEM; + } + return hgic_bootdl_send_cmd_tmo(hg, skb, HG_BOOTDL_CMD_NORMAL_TMO, NULL); +} + +static unsigned int hgic_bootdl_fragment_proc(struct hgic_bootdl *hg, unsigned int copy_len) +{ + unsigned int xfer_len = 0; + unsigned int blk = 0; + + if (hg->bus->type == HGIC_BUS_SDIO) { + if (copy_len < hg->frag_size) { + blk = copy_len / hg->bus->blk_size; + if (blk) { + xfer_len = blk * hg->bus->blk_size; + } else { + //xfer_len = copy_len; + xfer_len = ALIGN(copy_len, 4); + } + } else { + xfer_len = hg->frag_size; + } + } else { + xfer_len = copy_len > hg->frag_size ? hg->frag_size : copy_len; + } + return xfer_len; +} + +int hgic_bootdl_download(struct hgic_bootdl *hg, const char *fw_path) +{ + int ret = 0; + struct sk_buff *skb = NULL; + u32 write_addr = 0; + u32 copy_len = 0; + u32 xfer_len = 0; + u32 addr_offset = 0; + char *data = NULL; + + if (hg == NULL || fw_path == NULL) { + printk("%s,%d:input para error!\n", __FUNCTION__, __LINE__); + return EINVAL; + } + + hgic_enter(); + ret = hgic_bootdl_parse_fw(hg, fw_path); + if (ret != 0 || !hg->fw) { + hgic_dbg("hgic_bootdl_parse_fw error,ret:<%d>,path:%s\n", ret, fw_path); + goto __finish; + } + + copy_len = hg->fw_info.fw_len; + write_addr = hg->fw_info.write_addr; + data = (char *)(hg->fw->data + hg->fw_info.hdr_len); + while (copy_len) { + //xfer_len = copy_len > hg->frag_size ? hg->frag_size : copy_len; + xfer_len = hgic_bootdl_fragment_proc(hg, copy_len); + skb = dev_alloc_skb(xfer_len + sizeof(struct hgic_bootdl_cmd_hdr)); + if (skb == NULL) { + printk("%s: no memory\r\n", __FUNCTION__); + ret = -ENOMEM; + goto __finish; + } + + skb_reserve(skb, sizeof(struct hgic_bootdl_cmd_hdr)); + memcpy(skb->data, data + addr_offset, xfer_len); + skb_put(skb, xfer_len); + + ret = hgic_bootdl_cmd_write_memory(hg, write_addr, skb->len); + if (ret != 0) { + hgic_dbg("hgic_bootdl_cmd_write_memory error!\n"); + dev_kfree_skb_any(skb); + goto __finish; + } + + ret = hgic_bootdl_send_fw(hg, skb, HG_BOOTDL_CMD_WRITE_MEM_TMO); + if (ret != 0) { + hgic_dbg("hgic_bootdl_send_fw error!\n"); + goto __finish; + } + write_addr += xfer_len; + copy_len -= xfer_len; + addr_offset += xfer_len; + skb = NULL; + } + + ret = hgic_bootdl_cmd_run(hg); + if (ret) { + printk("%s: Cmd run failed:%d\r\n", __FUNCTION__, ret); + } +__finish: + if (hg->fw) { + hgic_dbg("Release boot download firmware...\n"); + release_firmware(hg->fw); + hg->fw = NULL; + } + hgic_leave(); + return ret; +} + + + diff --git a/utils/fwdl.h b/utils/fwdl.h new file mode 100644 index 0000000..445f1b1 --- /dev/null +++ b/utils/fwdl.h @@ -0,0 +1,91 @@ +#ifndef _HGIC_BOOT_HOST_H_ +#define _HGIC_BOOT_HOST_H_ + +#include "../hgic_def.h" + +#define HG_BOOTDL_CMD_ENTER (0x00) +#define HG_BOOTDL_CMD_GET_SC (0x01) +#define HG_BOOTDL_CMD_WRITE_MEM (0x02) +#define HG_BOOTDL_CMD_READ_MEM (0x03) +#define HG_BOOTDL_CMD_RUN (0x04) +#define HG_BOOTDL_CMD_VERIFY_MEM (0x05) +#define HG_BOOTDL_CMD_CHIP_RESET (0x06) +#define HG_BOOTDL_CMD_WRITE_REG (0x07) +#define HG_BOOTDL_CMD_READ_REG (0x08) +#define HG_BOOTDL_CMD_SPEED (0x09) +#define HG_BOOTDL_CMD_EXIT (0xFF) + +#define HG_BOOT_CMD_RUN_PREACT_AES_DEC BIT(31) +#define HG_BOOT_CMD_RUN_PREACT_CRC_CHK BIT(30) +#define HG_BOOT_CMD_RUN_PREACT_SP_CFG BIT(29) + +#define HG_BOOTDL_CMD_LEN 12 +#define HG_BOOTDL_CMD_WRITE_MEM_TMO 1000 +#define HG_BOOTDL_CMD_NORMAL_TMO 100 +#define HG_BOOTDL_INFO_HDR_SIZE 512 + +enum hg_device_state { + STATE_FW = 0, + STATE_BOOT, +}; + +enum hg_boot_host_check{ + HG_BOOTDL_CHECK_SUM = 0, + HG_BOOTDL_CHECK_CRC8, + HG_BOOTDL_CHECK_0XFD, + HG_BOOTDL_CHECK_OFF = 0xFF +} ; + +enum hg_boot_host_rsp_err{ + HG_BOOTDL_RSP_OK = 0, + HG_BOOTDL_RSP_ERR_CMD, + HG_BOOTDL_RSP_ERR_ADDR, + HG_BOOTDL_RSP_ERR_LEN, + HG_BOOTDL_RSP_ERR_PERMISSION, + HG_BOOTDL_RSP_ERR_CMD_CHECK, + HG_BOOTDL_RSP_ERR_DATA_CHECK, + HG_BOOTDL_RSP_ERR_TO, + HG_BOOTDL_RSP_ERR_IN_FW = 0xFF, +} ; + +/** + * @brief huge-ic boot state machine + */ +enum hg_boot_host_err { + HG_BOOTDL_ERR_NONE = 0, + HG_BOOTDL_ERR_UNSUPPORT_CMD, + HG_BOOTDL_ERR_RSP_PACKET, + HG_BOOTDL_ERR_XFER, + HG_BOOTDL_ERR_TO, +} ; + +struct hgic_bootdl_fw_info { + u32 write_addr; + u8 aes_en; + u8 crc_en; + u8 sp_cfg; + u32 fw_version; + u32 fw_len; + u32 local_crc32; + u32 hdr_len; +}; + +struct hgic_bootdl { + struct hgic_bootdl_fw_info fw_info; + u8 checksum_mode; + u16 last_cookie; + u32 frag_size; + //struct sk_buff_head fw_dataq; + struct hgic_bus *bus; + struct hgic_fwctrl *ctrl; + struct firmware *fw; +}; + +u32 hgic_bootdl_init(struct hgic_bootdl *hg_fwdl, struct hgic_bus *bus, struct hgic_fwctrl *ctrl); +void hgic_bootdl_release(struct hgic_bootdl *hg,int mode); +int hgic_bootdl_download(struct hgic_bootdl *hg, const char *fw_path); +int hgic_bootdl_cmd_enter(struct hgic_bootdl *hg); +struct sk_buff *hgic_bootdl_send_cmd(struct hgic_bootdl *hg_fwdl,struct sk_buff *skb,u32 timeout); +struct sk_buff *hgic_bootdl_send_data(struct hgic_bootdl *hg_fwdl,struct sk_buff *skb,u32 timeout); + +#endif diff --git a/utils/fwinfo.c b/utils/fwinfo.c new file mode 100644 index 0000000..da1a824 --- /dev/null +++ b/utils/fwinfo.c @@ -0,0 +1,322 @@ +#ifdef __RTOS__ +#include +#include +#include +#else +#include +#include +#include +#include +#include +#include +#include +#include +#endif + +#include "../hgic_def.h" +#include "fwinfo.h" + +/****************************************************************************** +* Name: CRC-16/MODBUS x16+x15+x2+1 +* Poly: 0x8005 +* Init: 0xFFFF +* Refin: True +* Refout: True +* Xorout: 0x0000 +* Note: +*****************************************************************************/ +u16 hgic_crc16(const u8 *data, u32 length) +{ + u8 i; + u16 crc = 0xffff; // Initial value + while (length--) + { + crc ^= *data++; // crc ^= *data; data++; + for (i = 0; i < 8; ++i) + { + if (crc & 1) + crc = (crc >> 1) ^ 0xA001; // 0xA001 = reverse 0x8005 + else + crc = (crc >> 1); + } + } + return crc; +} + + +/****************************************************************************** +* Name: CRC-32 x32+x26+x23+x22+x16+x12+x11+x10+x8+x7+x5+x4+x2+x+1 +* Poly: 0x4C11DB7 +* Init: 0xFFFFFFF +* Refin: True +* Refout: True +* Xorout: 0xFFFFFFF +* Alias: CRC_32/ADCCP +* Use: WinRAR,ect. +*****************************************************************************/ +u32 hgic_crc32(const u8 *data, u32 length) +{ + u8 i; + u32 crc = 0xffffffff; // Initial value + while (length--) + { + crc ^= *data++; // crc ^= *data; data++; + for (i = 0; i < 8; ++i) + { + if (crc & 1) + crc = (crc >> 1) ^ 0xEDB88320;// 0xEDB88320= reverse 0x04C11DB7 + else + crc = (crc >> 1); + } + } + return ~crc; +} + + +s32 hgic_check_fwinfo_hdr(const u8 *data) +{ + struct hgic_fw_info_hdr *fw_hdr = NULL; + + if (data == NULL) { + hgic_err("%s:Input para error!\n", __FUNCTION__); + return -EINVAL; + } + + fw_hdr = (struct hgic_fw_info_hdr *)data; + if (fw_hdr->boot.boot_flag != HGIC_FWINFO_BOOT_HDR) { + hgic_err("%s:Can not find boot header!\n", __FUNCTION__); + return -EINVAL;; + } + if (fw_hdr->fw_infor.func_code != HGIC_FWINFO_CODE_HDR) { + hgic_err("%s:Can not find fw infor header!\n", __FUNCTION__); + return -EINVAL;; + } + if (fw_hdr->spi_infor.func_code != HGIC_FWINFO_SPI_HDR) { + hgic_err("%s:Can not find spi info header!\n", __FUNCTION__); + return -EINVAL; + } + return 0; +} + +u16 hgic_get_fw_aes_en(const u8 *data, s32 *err_code) +{ + struct hgic_fw_info_hdr *fw_hdr = NULL; + s32 ret = 0; + + ret = hgic_check_fwinfo_hdr(data); + if (ret) { + hgic_err("%s:hgic_check_fwinfo_hdr error!\n", __FUNCTION__); + *err_code = ret; + return -1; + } + + fw_hdr = (struct hgic_fw_info_hdr *)data; + hgic_dbg("%s:Check Para:aes_en:%d\n", __FUNCTION__, fw_hdr->boot.mode.aes_en); + *err_code = 0; + return fw_hdr->boot.mode.aes_en; +} + +u16 hgic_get_fw_crc_en(const u8 *data, s32 *err_code) +{ + struct hgic_fw_info_hdr *fw_hdr = NULL; + s32 ret = 0; + + ret = hgic_check_fwinfo_hdr(data); + if (ret) { + hgic_err("%s:hgic_check_fwinfo_hdr error!\n", __FUNCTION__); + *err_code = ret; + return -1; + } + + fw_hdr = (struct hgic_fw_info_hdr *)data; + hgic_dbg("%s:Check Para:crc_en:%d\n", __FUNCTION__, fw_hdr->boot.mode.crc_en); + *err_code = 0; + return fw_hdr->boot.mode.crc_en; +} + +u32 hgic_get_fw_dl_addr(const u8 *data, s32 *err_code) +{ + struct hgic_fw_info_hdr *fw_hdr = NULL; + s32 ret = 0; + + ret = hgic_check_fwinfo_hdr(data); + if (ret) { + hgic_err("%s:hgic_check_fwinfo_hdr error!\n", __FUNCTION__); + *err_code = ret; + return -1; + } + + fw_hdr = (struct hgic_fw_info_hdr *)data; + hgic_dbg("%s:Check Para:download addr:%x\n", __FUNCTION__, fw_hdr->boot.boot_to_sram_addr); + *err_code = 0; + return fw_hdr->boot.boot_to_sram_addr; +} + +u32 hgic_get_fw_run_addr(const u8 *data, s32 *err_code) +{ + struct hgic_fw_info_hdr *fw_hdr = NULL; + s32 ret = 0; + + ret = hgic_check_fwinfo_hdr(data); + if (ret) { + hgic_err("%s:hgic_check_fwinfo_hdr error!\n", __FUNCTION__); + *err_code = ret; + return -1; + } + + fw_hdr = (struct hgic_fw_info_hdr *)data; + hgic_dbg("%s:Check Para:run addr:%x\n", __FUNCTION__, fw_hdr->boot.run_sram_addr); + *err_code = 0; + return fw_hdr->boot.run_sram_addr; +} + +u32 hgic_get_fw_code_offset(const u8 *data, s32 *err_code) +{ + struct hgic_fw_info_hdr *fw_hdr = NULL; + s32 ret = 0; + + ret = hgic_check_fwinfo_hdr(data); + if (ret) { + hgic_err("%s:hgic_check_fwinfo_hdr error!\n", __FUNCTION__); + *err_code = ret; + return -1; + } + + fw_hdr = (struct hgic_fw_info_hdr *)data; + hgic_dbg("%s:Check Para:code offset:%d\n", __FUNCTION__, fw_hdr->boot.boot_code_offset_addr); + *err_code = 0; + return fw_hdr->boot.boot_code_offset_addr; +} + +u32 hgic_get_fw_local_crc32(const u8 *data, s32 *err_code) +{ + struct hgic_fw_info_hdr *fw_hdr = NULL; + s32 ret = 0; + + ret = hgic_check_fwinfo_hdr(data); + if (ret) { + hgic_err("%s:hgic_check_fwinfo_hdr error!\n", __FUNCTION__); + *err_code = ret; + return -1; + } + + fw_hdr = (struct hgic_fw_info_hdr *)data; + hgic_dbg("%s:Check Para:local crc:%x\n", __FUNCTION__, fw_hdr->fw_infor.code_crc32); + *err_code = 0; + return fw_hdr->fw_infor.code_crc32; +} + +u32 hgic_get_fw_sdk_version(const u8 *data, s32 *err_code) +{ + struct hgic_fw_info_hdr *fw_hdr = NULL; + s32 ret = 0; + + ret = hgic_check_fwinfo_hdr(data); + if (ret) { + hgic_err("%s:hgic_check_fwinfo_hdr error!\n", __FUNCTION__); + *err_code = ret; + return -1; + } + + fw_hdr = (struct hgic_fw_info_hdr *)data; + hgic_dbg("%s:Current file sdk info:%d.%d.%d.%d\r\n", __FUNCTION__, + (fw_hdr->fw_infor.sdk_version >> 24) & 0xff, (fw_hdr->fw_infor.sdk_version >> 16) & 0xff, + (fw_hdr->fw_infor.sdk_version >> 8) & 0xff, (fw_hdr->fw_infor.sdk_version & 0xff)); + *err_code = 0; + return fw_hdr->fw_infor.sdk_version; +} + +u32 hgic_get_fw_svn_version(const u8 *data, s32 *err_code) +{ + struct hgic_fw_info_hdr *fw_hdr = NULL; + s32 ret = 0; + + ret = hgic_check_fwinfo_hdr(data); + if (ret) { + hgic_err("%s:hgic_check_fwinfo_hdr error!\n", __FUNCTION__); + *err_code = ret; + return -1; + } + + fw_hdr = (struct hgic_fw_info_hdr *)data; + hgic_dbg("%s:Current file svn info:%d\n", __FUNCTION__,fw_hdr->fw_infor.svn_version); + *err_code = 0; + return fw_hdr->fw_infor.svn_version; +} + + +u16 hgic_get_fw_chipid(const u8 *data, s32 *err_code) +{ + struct hgic_fw_info_hdr *fw_hdr = NULL; + s32 ret = 0; + + ret = hgic_check_fwinfo_hdr(data); + if (ret) { + hgic_err("%s:hgic_check_fwinfo_hdr error!\n", __FUNCTION__); + *err_code = ret; + return -1; + } + + fw_hdr = (struct hgic_fw_info_hdr *)data; + hgic_dbg("%s:Check Para:fw chipid:%x\n", __FUNCTION__, fw_hdr->fw_infor.chip_id); + *err_code = 0; + return fw_hdr->fw_infor.chip_id; +} + +u8 hgic_get_fw_cpuid(const u8 *data, s32 *err_code) +{ + struct hgic_fw_info_hdr *fw_hdr = NULL; + s32 ret = 0; + + ret = hgic_check_fwinfo_hdr(data); + if (ret) { + hgic_err("%s:hgic_check_fwinfo_hdr error!\n", __FUNCTION__); + *err_code = ret; + return -1; + } + + fw_hdr = (struct hgic_fw_info_hdr *)data; + hgic_dbg("%s:Check Para:cpu_id:%d\n", __FUNCTION__, fw_hdr->fw_infor.cpu_id); + *err_code = 0; + return fw_hdr->fw_infor.cpu_id; +} + +s32 hgic_get_fw_code_checksum(const u8 *data,s32 len) +{ + u32 local_chksum = 0; + u32 cur_crc = 0; + s32 err_code = 0; + u32 code_offset = 0; + const u8 *code_data = NULL; + s32 code_len = 0; + + local_chksum = hgic_get_fw_local_crc32(data, &err_code); + if(err_code != 0) { + hgic_err("%s:hgic_get_fw_local_crc32 error!\n", __FUNCTION__); + return err_code; + } + + code_offset = hgic_get_fw_code_offset(data, &err_code); + if(err_code != 0) { + hgic_err("%s:hgic_get_fw_code_offset error!\n", __FUNCTION__); + return err_code; + } + + code_data = data + code_offset; + code_len = len - code_offset; + + if(code_len < 0) { + hgic_err("%s:Input para error!\n", __FUNCTION__); + return -EINVAL; + } + + cur_crc = hgic_crc32(code_data, code_len); + if(cur_crc != local_chksum && ~cur_crc != local_chksum) { + hgic_err("%s:Check crc32 with hdr crc error,local crc:%x,cur_crc:%x\r\n", + __FUNCTION__, local_chksum,cur_crc); + return -EFAULT; + } + hgic_dbg("%s:Checksum Success!\n",__FUNCTION__); + return 0; +} + diff --git a/utils/fwinfo.h b/utils/fwinfo.h new file mode 100644 index 0000000..057da43 --- /dev/null +++ b/utils/fwinfo.h @@ -0,0 +1,98 @@ +#ifndef _HGIC_FWINFO_H_ +#define _HGIC_FWINFO_H_ + +#include "../hgic.h" + +#define HGIC_FWINFO_BOOT_HDR 0x5a69 +#define HGIC_FWINFO_SPI_HDR 0x1 +#define HGIC_FWINFO_CODE_HDR 0x2 + +struct hgic_spiflash_header_boot { + u16 boot_flag; /* 0 : 0x5a69, header boot flag */ + u8 version; /* 2 : version */ + u8 size; /* 3 : Link to Next Header */ + u32 boot_to_sram_addr; /* 4 : spi data load to sram addr */ + u32 run_sram_addr; /* 8 : code execute start addr */ + u32 boot_code_offset_addr; /* 12 : HeaderLen+ParamLen=4096+512 */ + u32 boot_from_flash_len; /* 16 : */ + u16 boot_data_crc; /* 20 : boot data crc check */ + u16 flash_blk_size; /* 22 : flash size in 64KB(version > 1), 512B(version== 0) */ + u16 boot_baud_mhz : 14, /* 24 : spi clk freq in mhz(version > 1), khz(version== 0) */ + driver_strength : 2; /* io driver strength */ + + struct { + u16 pll_src : 8, /* pll src in Mhz */ + pll_en : 1, /* PLL enable */ + debug : 1, /* debug info uart output enable */ + aes_en : 1, /* AES enable */ + crc_en : 1, /* CRC check enable */ + reserved : 4; + } mode; /* 26 : boot option */ + u16 reserved; /* 28 : */ + u16 head_crc16; /* (size+4) byte CRC16 check value */ +}__packed; + +struct hgic_spiflash_read_cfg { + u8 read_cmd; /* read_cmd */ + u8 cmd_dummy_cycles : 4, /* read dummy cycles */ + clock_mode : 2, /* clock polarity & phase */ + spec_sequnce_en : 1, /* spec sequnce to execute, maybe same with quad_wire_en */ + quad_wire_en : 1; /* spi D2/D3 enable */ + + u8 wire_mode_cmd : 2, + wire_mode_addr : 2, + wire_mode_data : 2, + quad_wire_select : 2; /* spi D2/D3 group select */ + + u8 reserved3; + + u16 sample_delay; /* RX sample dalay time: 0 ~ clk_divor */ +}__packed; + +struct hgic_spiflash_header_spi_info { + u8 func_code; /* 0 : header function(0x1) */ + u8 size; /* 1: Link to Next Header */ + + struct hgic_spiflash_read_cfg read_cfg; + u8 hgic_spiflash_spec_sequnce[64]; + + u16 header_crc16; /* (size+2) byte CRC16 check value */ +}__packed; + +/* hgic_ah_fw_v1.0.1.1_2020.2.20.bin ?*/ + +struct hgic_spiflash_header_firmware_info { + u8 func_code; /* 0 : header function(0x2) */ + u8 size; /* 1: Link to Next Header */ + u32 sdk_version; /* version */ + u32 svn_version; + u32 date; /* date */ + u16 chip_id; /* chip_id */ + u8 cpu_id; /* cpu id, fix 0 */ + u32 code_crc32; /* code CRC32 */ + u16 param_crc16; /* param CRC16 */ + u16 crc16; /* (size+2) byte CRC16 check value */ +}__packed; + +struct hgic_fw_info_hdr { + struct hgic_spiflash_header_boot boot; + struct hgic_spiflash_header_spi_info spi_infor; /* func1*/ + struct hgic_spiflash_header_firmware_info fw_infor ; /* func2*/ +} __packed; + +u16 hgic_crc16(const u8 *data, u32 length); +u32 hgic_crc32(const u8 *data, u32 length); +u16 hgic_get_fw_aes_en(const u8 *data, s32 *err_code); +u16 hgic_get_fw_crc_en(const u8 *data, s32 *err_code); +u32 hgic_get_fw_dl_addr(const u8 *data, s32 *err_code); +u32 hgic_get_fw_run_addr(const u8 *data, s32 *err_code); +u32 hgic_get_fw_code_offset(const u8 *data, s32 *err_code); +u32 hgic_get_fw_local_crc32(const u8 *data, s32 *err_code); +u32 hgic_get_fw_sdk_version(const u8 *data, s32 *err_code); +u32 hgic_get_fw_svn_version(const u8 *data, s32 *err_code); +u16 hgic_get_fw_chipid(const u8 *data, s32 *err_code); +u8 hgic_get_fw_cpuid(const u8 *data, s32 *err_code); +s32 hgic_get_fw_code_checksum(const u8 *data,s32 len); + + +#endif diff --git a/utils/hgic_fmac.c b/utils/hgic_fmac.c new file mode 100644 index 0000000..2df0dd6 --- /dev/null +++ b/utils/hgic_fmac.c @@ -0,0 +1,1141 @@ +/** + ****************************************************************************** + * @file hgic_fmac.c + * @author HUGE-IC Application Team + * @version V1.0.0 + * @date 2021-06-23 + * @brief fmac api for application. + ****************************************************************************** + * @attention + * + *

© COPYRIGHT 2021 HUGE-IC

+ * + ****************************************************************************** + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define fmac_dbg(fmt, ...) printf("%s:%d::"fmt, __FUNCTION__, __LINE__, ##__VA_ARGS__) + +#define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x" +#define MACARG(a) (a)[0]&0xff, (a)[1]&0xff, (a)[2]&0xff, (a)[3]&0xff, (a)[4]&0xff, (a)[5]&0xff +#define IPSTR "%d.%d.%d.%d" +#define IPARG(a) ((a)>>24)&0xff, ((a)>>16)&0xff, ((a)>>8)&0xff, (a)&0xff +#define MAC2STR(mac, str) (sprintf((str), MACSTR, MACARG(mac))) +#define STR_EQ(s1,s2) (strcmp(s1,s2)==0) +#define MAC_EQ(a1,a2) (memcmp(a1,a2,6)==0) + +typedef unsigned char uint8; +typedef unsigned short uint16; +typedef unsigned int uint32; + +enum FMAC_CONNECT_REASON { + FMAC_ASSOC_SUCCESS = 0, + FMAC_ASSOC_REFUSED = 1, + FMAC_ASSOC_DENIED_NO_MORE_STAS = 17, + FMAC_ASSOC_DENIED_INSUFFICIENT_BANDWIDTH = 33, + FMAC_ASSOC_REFUSED_AP_OUT_OF_MEMORY = 93, + FMAC_ASSOC_NO_AP = 0xff +}; + +struct hgic_tx_info { + uint8 band; + uint8 tx_bw; + uint8 tx_mcs; + uint8 freq_idx: 5, antenna: 3; + uint32 tx_flags; + uint16 tx_flags2; + uint8 priority; + uint8 tx_power; +}; + +struct hgic_freqinfo { + unsigned char bss_bw, chan_cnt; + unsigned short freq_start, freq_end; + unsigned short chan_list[16]; +}; + +enum HGIC_MODULE_TYPE{ + HGIC_MODULE_TYPE_700M = 1, + HGIC_MODULE_TYPE_900M = 2, + HGIC_MODULE_TYPE_860M = 3, + HGIC_MODULE_TYPE_810M = 5, +}; + +struct hgic_module_hwinfo{ + union{ + struct{ + unsigned char type; + unsigned char saw:1, rev:7; + }; + unsigned short v; + }; +}; + +int hgic_str2mac(char *mac_str, unsigned char *mac) +{ + int tmp[6]; + if (mac_str && mac) { + memset(tmp, 0, sizeof(tmp)); + memset(mac, 0, 6); + if (6 == sscanf(mac_str, MACSTR, &tmp[0], &tmp[1], &tmp[2], &tmp[3], &tmp[4], &tmp[5])) { + mac[0] = (unsigned char)tmp[0]; + mac[1] = (unsigned char)tmp[1]; + mac[2] = (unsigned char)tmp[2]; + mac[3] = (unsigned char)tmp[3]; + mac[4] = (unsigned char)tmp[4]; + mac[5] = (unsigned char)tmp[5]; + return 1; + } + } + return 0; +} + +///////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////// +/* iwpriv api */ +int hgic_iwpriv_read(char *buff, int len) +{ + int ret = 0; + int fd = -1; + + if (buff == NULL || len <= 0) { + return 0; + } + memset(buff, 0, len); + fd = open("/proc/hgic/iwpriv", O_RDONLY); + if (fd != -1) { + ret = read(fd, buff, len); + close(fd); + } + return ret; +} + +int hgic_iwpriv_write(char *buff, int len) +{ + int ret = 0; + int fd = -1; + + if (buff == NULL || len <= 0) { + return 0; + } + fd = open("/proc/hgic/iwpriv", O_WRONLY); + if (fd != -1) { + ret = write(fd, buff, len); + close(fd); + } + return ret > 0; +} + +int hgic_iwpriv_do(char *ifname, char *cmd, char *args) +{ + int ret = 0; + int fd = -1; + char buff[512]; + char *ptr = NULL; + + memset(buff, 0, sizeof(buff)); + if (args) { + sprintf(buff, "%s %s %s", ifname, cmd, args); + } else { + sprintf(buff, "%s %s", ifname, cmd); + } + if (hgic_iwpriv_write(buff, strlen(buff))) { + if (STR_EQ(cmd, "set") || STR_EQ(cmd, "scan") || STR_EQ(cmd, "save")) { + if (hgic_iwpriv_read(buff, sizeof(buff)) && strstr(buff, "OK")) { + ret = 1; + } else { + ptr = strstr(buff, "error:"); + if (ptr) { + ret = atoi(ptr + 6); + fmac_dbg("error: %s\r\n", strerror(ret)); + } + } + } + } + return ret; +} + +int hgic_iwpriv_set_int(char *ifname, char *name, int value) +{ + char arg[64]; + memset(arg, 0, sizeof(arg)); + sprintf(arg, "%s=%d", name, value); + return hgic_iwpriv_do(ifname, "set", arg); +} + +int hgic_iwpriv_set_mac(char *ifname, char *name, char *mac) +{ + char arg[64]; + memset(arg, 0, sizeof(arg)); + sprintf(arg, "%s="MACSTR, name, MACARG(mac)); + return hgic_iwpriv_do(ifname, "set", arg); +} + +int hgic_iwpriv_set_ints(char *ifname, char *name, int cnt, ...) +{ + int i = 0; + int val = 0; + char arg[512]; + char *ptr = arg; + va_list argptr; + + memset(arg, 0, sizeof(arg)); + sprintf(ptr, "%s=", name); + ptr += strlen(ptr); + + va_start(argptr, cnt); + for (i = 0; i < cnt; i++) { + val = va_arg(argptr, int); + sprintf(ptr, (i == 0 ? "%d" : ",%d"), val); + ptr += strlen(ptr); + } + va_end(argptr); + return hgic_iwpriv_do(ifname, "set", arg); +} + +int hgic_iwpriv_set_intarray(char *ifname, char *name, int *values, int cnt) +{ + int i = 0; + char arg[512]; + char *ptr = arg; + + memset(arg, 0, sizeof(arg)); + sprintf(ptr, "%s=", name); + ptr += strlen(ptr); + + for (i = 0; i < cnt; i++) { + sprintf(ptr, (i == 0 ? "%d" : ",%d"), values[i]); + ptr += strlen(ptr); + } + return hgic_iwpriv_do(ifname, "set", arg); +} + +int hgic_iwpriv_set_bytes(char *ifname, char *name, char *value) +{ + char arg[512]; + memset(arg, 0, sizeof(arg)); + sprintf(arg, "%s=%s", name, value); + return hgic_iwpriv_do(ifname, "set", arg); +} + +int hgic_iwpriv_get_int(char *ifname, char *name) +{ + char buff[8]; + hgic_iwpriv_do(ifname, "get", name); + hgic_iwpriv_read(buff, 8); + return atoi(buff); +} + +int hgic_iwpriv_get_bytes(char *ifname, char *name, char *buff, int count) +{ + hgic_iwpriv_do(ifname, "get", name); + return hgic_iwpriv_read(buff, count); +} + +int hgic_iwpriv_get_mac(char *ifname, char *name, char *mac) +{ + char str[32]; + hgic_iwpriv_do(ifname, "get", name); + hgic_iwpriv_read(str, sizeof(str)); + return hgic_str2mac(str, mac); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////// +int hgic_iwpriv_set_ssid(char *ifname, char *ssid) +{ + return hgic_iwpriv_set_bytes(ifname, "ssid", ssid); +} +int hgic_iwpriv_set_bssid(char *ifname, char *bssid) +{ + return hgic_iwpriv_set_mac(ifname, "bssid", bssid); +} +int hgic_iwpriv_set_channel(char *ifname, int channel) +{ + return hgic_iwpriv_set_int(ifname, "channel", channel); +} +int hgic_iwpriv_set_keymgmt(char *ifname, char *key_mgmt) +{ + return hgic_iwpriv_set_bytes(ifname, "key_mgmt", key_mgmt); +} +int hgic_iwpriv_set_wpapsk(char *ifname, char *wpa_psk) +{ + return hgic_iwpriv_set_bytes(ifname, "wpa_psk", wpa_psk); +} +int hgic_iwpriv_set_freqrange(char *ifname, int freq_start, int freq_end, int bw) +{ + return hgic_iwpriv_set_ints(ifname, "freq_range", 3, freq_start, freq_end, bw); +} +int hgic_iwpriv_set_bss_bw(char *ifname, int bss_bw) +{ + return hgic_iwpriv_set_int(ifname, "bss_bw", bss_bw); +} +int hgic_iwpriv_set_tx_bw(char *ifname, int tx_bw) +{ + return hgic_iwpriv_set_int(ifname, "tx_bw", tx_bw); +} +int hgic_iwpriv_set_tx_mcs(char *ifname, int tx_mcs) +{ + return hgic_iwpriv_set_int(ifname, "tx_mcs", tx_mcs); +} +int hgic_iwpriv_set_acs(char *ifname, int start, int tmo) +{ + return hgic_iwpriv_set_ints(ifname, "acs", 2, start, tmo); +} +int hgic_iwpriv_set_chan_list(char *ifname, int *chan_list, int chan_count) +{ + return hgic_iwpriv_set_intarray(ifname, "chan_list", chan_list, chan_count); +} +int hgic_iwpriv_set_mode(char *ifname, char *mode) +{ + return hgic_iwpriv_set_bytes(ifname, "mode", mode); +} +int hgic_iwpriv_set_paired_stas(char *ifname, char *paired_stas) +{ + return hgic_iwpriv_set_bytes(ifname, "paired_stas", paired_stas); +} +int hgic_iwpriv_set_pairing(char *ifname, int start) +{ + return hgic_iwpriv_set_int(ifname, "pairing", start); +} +int hgic_iwpriv_set_beacon_int(char *ifname, int beacon_int) +{ + return hgic_iwpriv_set_int(ifname, "beacon_int", beacon_int); +} +int hgic_iwpriv_set_radio_off(char *ifname, int off) +{ + return hgic_iwpriv_set_int(ifname, "radio_off", off); +} +int hgic_iwpriv_set_join_group(char *ifname, char *mcast_addr, int aid) +{ + char arg[64]; + memset(arg, 0, sizeof(arg)); + sprintf(arg, "join_group="MACSTR",%d", MACARG(mcast_addr), aid); + return hgic_iwpriv_do(ifname, "set", arg); +} +int hgic_iwpriv_set_txpower(char *ifname, int txpower) +{ + return hgic_iwpriv_set_int(ifname, "txpower", txpower); +} +int hgic_iwpriv_set_ps_connect(char *ifname, int period, int roundup) +{ + return hgic_iwpriv_set_ints(ifname, "ps_connect", 2, period, roundup); +} +int hgic_iwpriv_set_bss_max_idle(char *ifname, int bss_max_idle) +{ + return hgic_iwpriv_set_int(ifname, "bss_max_idle", bss_max_idle); +} +int hgic_iwpriv_set_wkio_mode(char *ifname, int wkio_mode) +{ + return hgic_iwpriv_set_int(ifname, "wkio_mode", wkio_mode); +} +int hgic_iwpriv_set_dtim_period(char *ifname, int dtim_period) +{ + return hgic_iwpriv_set_int(ifname, "dtim_period", dtim_period); +} +int hgic_iwpriv_set_ps_mode(char *ifname, int ps_mode) +{ + return hgic_iwpriv_set_int(ifname, "ps_mode", ps_mode); +} +int hgic_iwpriv_set_aplost_time(char *ifname, int aplost_time) +{ + return hgic_iwpriv_set_int(ifname, "aplost_time", aplost_time); +} +int hgic_iwpriv_unpair(char *ifname, char *mac) +{ + return hgic_iwpriv_set_mac(ifname, "unpair", mac); +} +int hgic_iwpriv_scan(char *ifname) +{ + return hgic_iwpriv_do(ifname, "scan", NULL); +} +int hgic_iwpriv_save(char *ifname) +{ + return hgic_iwpriv_do(ifname, "save", NULL); +} +int hgic_iwpriv_set_auto_chswitch(char *ifname, int enable) +{ + return hgic_iwpriv_set_int(ifname, "auto_chswitch", enable); +} +int hgic_iwpriv_set_mcast_key(char *ifname, char *mcast_key) +{ + return hgic_iwpriv_set_bytes(ifname, "mcast_key", mcast_key); +} +int hgic_iwpriv_set_reassoc_wkhost(char *ifname, int enable) +{ + return hgic_iwpriv_set_int(ifname, "reassoc_wkhost", enable); +} +int hgic_iwpriv_set_wakeup_io(char *ifname, int wakeup_io) +{ + return hgic_iwpriv_set_int(ifname, "wakeup_io", wakeup_io); +} +int hgic_iwpriv_set_dbginfo(char *ifname, int enable) +{ + return hgic_iwpriv_set_int(ifname, "dbginfo", enable); +} +int hgic_iwpriv_set_sysdbg(char *ifname, char *sysdbg) +{ + return hgic_iwpriv_set_bytes(ifname, "sysdbg", sysdbg); +} +int hgic_iwpriv_set_primary_chan(char *ifname, int primary_chan) +{ + return hgic_iwpriv_set_int(ifname, "primary_chan", primary_chan); +} +int hgic_iwpriv_set_autosleep_time(char *ifname, int autosleep_time) +{ + return hgic_iwpriv_set_int(ifname, "autosleep_time", autosleep_time); +} +int hgic_iwpriv_set_supper_pwr(char *ifname, int enable) +{ + return hgic_iwpriv_set_int(ifname, "supper_pwr", enable); +} +int hgic_iwpriv_set_r_ssid(char *ifname, char *r_ssid) +{ + return hgic_iwpriv_set_bytes(ifname, "r_ssid", r_ssid); +} +int hgic_iwpriv_set_r_psk(char *ifname, char *r_psk) +{ + return hgic_iwpriv_set_bytes(ifname, "r_psk", r_psk); +} +int hgic_iwpriv_set_auto_save(char *ifname, int enable) +{ + return hgic_iwpriv_set_int(ifname, "auto_save", enable); +} +int hgic_iwpriv_set_pair_autostop(char *ifname, int enable) +{ + return hgic_iwpriv_set_int(ifname, "pair_autostop", enable); +} +int hgic_iwpriv_set_dcdc13(char *ifname, int enable) +{ + return hgic_iwpriv_set_int(ifname, "dcdc13", enable); +} +int hgic_iwpriv_set_acktmo(char *ifname, int acktmo) +{ + return hgic_iwpriv_set_int(ifname, "acktmo", acktmo); +} +int hgic_iwpriv_get_sta_list(char *ifname, char *buff, int size) +{ + return hgic_iwpriv_get_bytes(ifname, "sta_list", buff, size); +} +int hgic_iwpriv_get_scan_list(char *ifname, char *buff, int size) +{ + return hgic_iwpriv_get_bytes(ifname, "scan_list", buff, size); +} +int hgic_iwpriv_get_ssid(char *ifname, char *buff, int size) +{ + return hgic_iwpriv_get_bytes(ifname, "ssid", buff, size); +} +int hgic_iwpriv_get_bssid(char *ifname, char *mac) +{ + return hgic_iwpriv_get_mac(ifname, "bssid", mac); +} +int hgic_iwpriv_get_wpa_psk(char *ifname, char *buff, int size) +{ + return hgic_iwpriv_get_bytes(ifname, "wpa_psk", buff, size); +} +int hgic_iwpriv_get_txpower(char *ifname) +{ + return hgic_iwpriv_get_int(ifname, "txpower"); +} +int hgic_iwpriv_get_aggcnt(char *ifname) +{ + return hgic_iwpriv_get_int(ifname, "agg_cnt"); +} +int hgic_iwpriv_get_bss_bw(char *ifname) +{ + return hgic_iwpriv_get_int(ifname, "bss_bw"); +} +int hgic_iwpriv_get_chan_list(char *ifname, char *buff, int size) +{ + return hgic_iwpriv_get_bytes(ifname, "chan_list", buff, size); +} +int hgic_iwpriv_get_freq_range(char *ifname, char *buff, int size) +{ + return hgic_iwpriv_get_bytes(ifname, "freq_range", buff, size); +} +int hgic_iwpriv_get_key_mgmt(char *ifname, char *buff, int size) +{ + return hgic_iwpriv_get_bytes(ifname, "key_mgmt", buff, size); +} +int hgic_iwpriv_get_battery_level(char *ifname) +{ + return hgic_iwpriv_get_int(ifname, "battery_level"); +} +int hgic_iwpriv_get_module_type(char *ifname) +{ + return hgic_iwpriv_get_int(ifname, "module_type"); +} +int hgic_iwpriv_set_pa_pwrctrl_dis(char *ifname, int disable) +{ + return hgic_iwpriv_set_int(ifname, "pa_pwrctl_dis", disable); +} +int hgic_iwpriv_set_dhcpc(char *ifname, int enable) +{ + return hgic_iwpriv_set_int(ifname, "dhcpc", enable); +} +int hgic_iwpriv_get_disassoc_reason(char *ifname) +{ + return hgic_iwpriv_get_int(ifname, "disassoc_reason"); +} +int hgic_iwpriv_set_wkdata_save(char *ifname, int enable) +{ + return hgic_iwpriv_set_int(ifname, "wkdata_save", enable); +} +int hgic_iwpriv_set_mcast_txparam(char *ifname, int dupcnt, int tx_bw, int tx_mcs, int clearch) +{ + return hgic_iwpriv_set_ints(ifname, "mcast_txparam", 4, dupcnt, tx_bw, tx_mcs, clearch); +} +int hgic_iwpriv_reset_sta(char *ifname, char *mac_addr) +{ + return hgic_iwpriv_set_mac(ifname, "reset_sta", mac_addr); +} +int hgic_iwpriv_ant_auto(char *ifname, int en) +{ + return hgic_iwpriv_set_int(ifname, "ant_auto", en); +} +int hgic_iwpriv_set_ant_sel(char *ifname, int ant) +{ + return hgic_iwpriv_set_int(ifname, "ant_sel", ant); +} +int hgic_iwpriv_get_ant_sel(char *ifname) +{ + return hgic_iwpriv_get_int(ifname, "ant_sel"); +} +int hgic_iwpriv_set_macfilter(char *ifname, int enable) +{ + return hgic_iwpriv_set_int(ifname, "macfilter", enable); +} +int hgic_iwpriv_send_atcmd(char *ifname, char *atcmd) +{ + return hgic_iwpriv_set_bytes(ifname, "atcmd", atcmd); +} +int hgic_iwpriv_set_roaming(char *ifname, int enable) +{ + return hgic_iwpriv_set_int(ifname, "roaming", enable); +} +int hgic_iwpriv_set_max_txcnt(char *ifname, int txcnt) +{ + return hgic_iwpriv_set_int(ifname, "max_txcnt", txcnt); +} + +///////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////// +/* proc fs api */ +int hgic_proc_read_bytes(char *name, char *buff, int len) +{ + char fname[32]; + int ret = 0; + int fd = -1; + + if (buff == NULL || len <= 0) { + return 0; + } + memset(buff, 0, len); + memset(fname, 0, sizeof(fname)); + sprintf(fname, "/proc/hgic/%s", name); + fd = open(fname, O_RDONLY); + if (fd != -1) { + ret = read(fd, buff, len); + close(fd); + } + return ret; +} + +int hgic_proc_read_int(char *name) +{ + char buff[32]; + hgic_proc_read_bytes(name, buff, 32); + return atoi(buff); +} + +int hgic_proc_read_mac(char *name, char *mac) +{ + char buff[32]; + hgic_proc_read_bytes(name, buff, 32); + return hgic_str2mac(buff, mac); +} + +int hgic_proc_write_bytes(char *name, char *buff, int len) +{ + int ret = 0; + char fname[32]; + int fd = -1; + + if (buff == NULL || len == 0) { + return 0; + } + memset(fname, 0, sizeof(fname)); + sprintf(fname, "/proc/hgic/%s", name); + fd = open(fname, O_WRONLY); + if (fd != -1) { + ret = write(fd, buff, len); + close(fd); + if(ret < 0) perror("error"); + } + return ret > 0; +} + +int hgic_proc_write_int(char *name, int val) +{ + char buff[12]; + memset(buff, 0, sizeof(buff)); + sprintf(buff, "%d", val); + return hgic_proc_write_bytes(name, buff, strlen(buff)); +} + +int hgic_proc_write_mac(char *name, char *mac) +{ + char str[18]; + memset(str, 0, sizeof(str)); + MAC2STR(mac, str); + return hgic_proc_write_bytes(name, str, strlen(str)); +} + +int hgic_proc_write_ints(char *name, int cnt, ...) +{ + int i = 0; + int val = 0; + char buff[512]; + char *ptr = buff; + va_list argptr; + + memset(buff, 0, sizeof(buff)); + va_start(argptr, cnt); + for (i = 0; i < cnt; i++) { + val = va_arg(argptr, int); + sprintf(ptr, (i==0?"%d":",%d"), val); + ptr += strlen(ptr); + } + va_end(argptr); + return hgic_proc_write_bytes(name, buff, strlen(buff)); +} + +int hgic_proc_write_intarray(char *name, int *values, int cnt) +{ + int i = 0; + char buff[512]; + char *ptr = buff; + + memset(buff, 0, sizeof(buff)); + for (i = 0; i < cnt; i++) { + sprintf(ptr, (i==0?"%d":",%d"), values[i]); + ptr += strlen(ptr); + } + return hgic_proc_write_bytes(name, buff, strlen(buff)); +} + +///////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////// +int hgic_proc_read_event(void) +{ + return hgic_proc_read_int("event"); +} +int hgic_proc_read_tx_bitrate(void) +{ + return hgic_proc_read_int("tx_bitrate"); +} +int hgic_proc_read_signal(void) +{ + return hgic_proc_read_int("signal"); +} +int hgic_proc_read_evm(void) +{ + return hgic_proc_read_int("evm"); +} +int hgic_proc_read_conn_state(void) +{ + char state[12]; + hgic_proc_read_bytes("conn_state", state, 12); + return (strstr(state, "CONNECTED") != NULL); +} +int hgic_proc_read_temperature(void) +{ + return hgic_proc_read_int("temperature"); +} +int hgic_proc_read_paired_stas(char *buff, int len) +{ + return hgic_proc_read_bytes("paired_stas", buff, len); +} +int hgic_proc_read_sta_count(void) +{ + return hgic_proc_read_int("sta_count"); +} +int hgic_proc_read_wkreason(void) +{ + return hgic_proc_read_int("wkreason"); +} +int hgic_proc_read_customer_mgmt(char *buff, int len) +{ + return hgic_proc_read_bytes("mgmt", buff, len); +} +int hgic_proc_read_fw_dbgifno(char *buff, int len) +{ + return hgic_proc_read_bytes("fwdbginfo", buff, len); +} +int hgic_proc_read_firmware_ver(char *ver_str, int size) +{ + return hgic_proc_read_bytes("fw_ver", ver_str, size); +} +int hgic_proc_read_battery_level(void) +{ + return hgic_proc_read_int("battery_level"); +} +int hgic_proc_set_heartbeat(int ipaddr, int port, int period, int timeout) +{ + struct in_addr in = { .s_addr = (in_addr_t)ipaddr }; + return hgic_proc_write_ints("heartbeat", 4, inet_ntoa(in), port, period, timeout); +} +int hgic_proc_set_heartbeat_resp_data(char *resp_data, int len) +{ + return hgic_proc_write_bytes("heartbeat_resp", resp_data, len); +} +int hgic_proc_set_wakeup_data(char *wakeup_data, int len) +{ + return hgic_proc_write_bytes("wakeup_data", wakeup_data, len); +} +int hgic_proc_wakeup_sta(char *addr) +{ + return hgic_proc_write_mac("wakeup", addr); +} +int hgic_proc_sleep(int sleep) +{ + return hgic_proc_write_int("sleep", sleep); +} +int hgic_proc_ota(char *fw_file) +{ + return hgic_proc_write_bytes("ota", fw_file, strlen(fw_file)); +} +int hgic_proc_read_pairing_sta(char *sta_mac) +{ + return hgic_proc_read_mac("pairing_sta", sta_mac); +} +int hgic_proc_send_custmgmt(char *dest, struct hgic_tx_info *txinfo, char *mgmt, int len) +{ + int ret = -1; + char *buf = malloc(len+sizeof(struct hgic_tx_info)+6); + if(buf){ + memcpy(buf, dest, 6); + memcpy(buf+6, txinfo, sizeof(struct hgic_tx_info)); + memcpy(buf+6+sizeof(struct hgic_tx_info), mgmt, len); + ret = hgic_proc_write_bytes("mgmt", buf, len+6+sizeof(struct hgic_tx_info)); + free(buf); + } + return ret; +} +int hgic_proc_read_dhcpc_result(char *buff, int len) +{ + return hgic_proc_read_bytes("dhcpc", buff, len); +} +int hgic_proc_read_wkdata_buff(char *buff, int len) +{ + return hgic_proc_read_bytes("wkdata_buff", buff, len); +} +int hgic_proc_set_wkdata_mask(unsigned short offset, char *mask, int mask_len) +{ + char buff[128]; + if(mask_len > 16) mask_len = 16; + memcpy(buff, &offset, 2); + memcpy(buff+2, mask, mask_len); + return hgic_proc_write_bytes("wkdata_mask", buff, mask_len + 2); +} +int hgic_proc_read_cust_driverdata(char *buff, int len) +{ + return hgic_proc_read_bytes("cust_driverdata", buff, len); +} +int hgic_proc_set_cust_driverdata(char *buff, int len) +{ + if(len > 1500){ + printf("data len:%d too big\r\n", len); + return -1; + } + return hgic_proc_write_bytes("cust_driverdata", buff, len); +} +int hgic_proc_set_stafreqinfo(char *sta_addr, struct hgic_freqinfo *freqinfo) +{ + int ret = -1; + char *buff = malloc(6+sizeof(struct hgic_freqinfo)); + if(buff){ + memset(buff, 0, 6+sizeof(struct hgic_freqinfo)); + if(sta_addr) memcpy(buff, sta_addr, 6); + memcpy(buff+6, freqinfo, sizeof(struct hgic_freqinfo)); + ret = hgic_proc_write_bytes("freqinfo", buff, 6+sizeof(struct hgic_freqinfo)); + free(buff); + } + return ret; +} + +/////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////// +#if 1 //sample code +#define IFNAME "hg0" + +#define AH_STA_COUNT (8) +struct hgic_ah_sta { + unsigned char mac[6]; + unsigned char used: 1, ps: 1, paired: 1, connected: 1, update:1; + unsigned char aid; + char rssi, evm; +}; + +struct hgic_ah_sta_mgr { + struct hgic_ah_sta stas[AH_STA_COUNT]; + int count; +}ah_sta_mgr; + +struct hgic_dhcp_result{ + unsigned int ipaddr, netmask, svrip, router, dns1, dns2; +}; + +static int hgic_parse_paired_stas(struct hgic_ah_sta *stas, char *paired_stas) +{ + int i = 0; + int cnt = 0; + char mac[6]; + char *ptr = paired_stas; + + memset(stas, 0, AH_STA_COUNT*sizeof(struct hgic_ah_sta)); + if(paired_stas && stas){ + cnt = strlen(paired_stas)/17; + if(cnt > AH_STA_COUNT) cnt = AH_STA_COUNT; + for(i=0; ipaired = 1; + sta->update = 1; + sta->used = 1; + } + } + } + else { + if(new_stas[i].connected){ + sta = hgic_find_sta(ah_sta_mgr.stas, new_stas[i].mac); + if(sta == NULL){ + sta = hgic_new_sta(new_stas[i].mac); + } + if(sta){ + sta->ps = new_stas[i].ps; + sta->rssi = new_stas[i].rssi; + sta->evm = new_stas[i].evm; + sta->aid = new_stas[i].aid; + sta->connected = 1; + sta->update = 1; + sta->used = 1; + } + } + } + } + + for(i=0; i 0){ + buff[i] = 0; + printf("get wkdata, len:%d, %s\r\n", i, buff+42); + } + }while(i>0); + free(buff); +} + +int hgic_event_proc(int event) +{ + int i = 0; + struct hgic_dhcp_result result; + struct hgic_ah_sta *sta = NULL; + struct hgic_ah_sta tmpstas[AH_STA_COUNT]; + char *buff = malloc(4096); + switch (event) { + case 5: /*HGIC_EVENT_SCANNING*/ + fmac_dbg("start scan ...\r\n"); + break; + case 6: /*HGIC_EVENT_SCAN_DONE*/ + fmac_dbg("scan done!\r\n"); + hgic_iwpriv_get_scan_list(IFNAME, buff, 4096); + if(buff) printf("%s\r\n", buff); + break; + case 7: /*HGIC_EVENT_TX_BITRATE*/ + fmac_dbg("estimate tx bitrate:%dKbps\r\n", hgic_proc_read_tx_bitrate()); + break; + case 8: /*HGIC_EVENT_PAIR_START*/ + fmac_dbg("start pairing ...\r\n"); + break; + case 9: /*HGIC_EVENT_PAIR_SUCCESS*/ + hgic_proc_read_pairing_sta(buff); + fmac_dbg("pairing success! ["MACSTR"]\r\n", MACARG(buff)); + hgic_iwpriv_set_pairing(IFNAME, 0); //stop pair + break; + case 10: /*HGIC_EVENT_PAIR_DONE*/ + fmac_dbg("pairing done!\r\n"); + hgic_proc_read_paired_stas(buff, 4096); + hgic_parse_paired_stas(tmpstas, buff); + for(i=0; ipaired){ + printf("new sta paired! "MACSTR"\r\n", MACARG(tmpstas[i].mac)); + } + } + } + hgic_update_stas(tmpstas, 1); + break; + case 11: /*HGIC_EVENT_CONECT_START*/ + fmac_dbg("start connecting ...\r\n"); + break; + case 12: /*HGIC_EVENT_CONECTED*/ + fmac_dbg("connected!\r\n"); + hgic_iwpriv_get_sta_list(IFNAME, buff, 4096); + hgic_parse_sta_list(tmpstas, buff); + for(i=0; iconnected){ + printf("new sta connected! "MACSTR"\r\n", MACARG(tmpstas[i].mac)); + } + } + } + hgic_update_stas(tmpstas, 0); + hgic_test_read_wakeupdata(); + break; + case 13: /*HGIC_EVENT_DISCONECTED*/ + fmac_dbg("disconnected!\r\n"); + hgic_iwpriv_get_sta_list(IFNAME, buff, 4096); + hgic_parse_sta_list(tmpstas, buff); + for(i=0;iconnected){ + printf("sta disconnected! "MACSTR"\r\n", MACARG(ah_sta_mgr.stas[i].mac)); + } + } + } + hgic_update_stas(tmpstas, 0); + break; + case 14: /*HGIC_EVENT_SIGNAL*/ + fmac_dbg("signal changed: rssi:%d, evm:%d\r\n", hgic_proc_read_signal(), hgic_proc_read_event()); + break; + case 18: /*HGIC_EVENT_FWDBG_INFO*/ + hgic_proc_read_fw_dbgifno(buff, 4096); + if(buff) printf("%s", buff); + break; + case 19: /*HGIC_EVENT_CUSTOMER_MGMT*/ + i = hgic_proc_read_customer_mgmt(buff, 4096); + fmac_dbg("rx customer mgmt frame from "MACSTR", %d bytes \r\n", MACARG(buff+6), i-6); + break; + case 21: /*HGIC_EVENT_DHCPC_DONE*/ + hgic_proc_read_dhcpc_result((char *)&result, sizeof(result)); + fmac_dbg("fw dhcpc result: ipaddr:"IPSTR", netmask:"IPSTR", svrip:"IPSTR", router:"IPSTR", dns:"IPSTR"/"IPSTR"\r\n", + IPARG(result.ipaddr), IPARG(result.netmask), IPARG(result.svrip), + IPARG(result.router), IPARG(result.dns1), IPARG(result.dns2)); + break; + case 22: /*HGIC_EVENT_CONNECT_FAIL*/ + fmac_dbg("connect fail, reason:%d\r\n", hgic_iwpriv_get_disassoc_reason(IFNAME)); + break; + case 23: /*HGIC_EVENT_CUST_DRIVER_DATA*/ + fmac_dbg("rx customer driver data %d bytes\r\n", hgic_proc_read_cust_driverdata(buff, 1024)); + break; + case 24: /*HGIC_EVENT_UNPAIR_STA*/ + fmac_dbg("unpair\r\n"); + break; + } + if (buff) { free(buff); } +} + +int main(int argc, char *argv[]) +{ + int event = 0; + char mask[4] = { 0x3, 0xff, 0x0f, 0xff}; + char dest[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + struct hgic_tx_info txinfo; + struct hgic_module_hwinfo hwinfo; + + struct hgic_freqinfo freqinfo={ + .bss_bw = 8, + .chan_cnt = 3, + .chan_list = {9080, 9160, 9240}, + }; + char *custmgmt = "this is customer mgmt data"; + + hwinfo.v = hgic_iwpriv_get_module_type("hg0"); + printf("Module HW info: type:%d, %s\r\n", hwinfo.type, hwinfo.saw?"SAW":"NO SAW"); + + /*test: send customer mgmt frame.*/ + memset(&txinfo, 0, sizeof(txinfo)); + txinfo.tx_mcs = 0xff; //auto mcs + txinfo.freq_idx = 1; //tx at channel 1 + hgic_proc_send_custmgmt(dest, &txinfo, custmgmt, strlen(custmgmt)); + txinfo.freq_idx = 2; //tx at channel 2 + hgic_proc_send_custmgmt(dest, &txinfo, custmgmt, strlen(custmgmt)); + txinfo.freq_idx = 3; //tx at channel 3 + hgic_proc_send_custmgmt(dest, &txinfo, custmgmt, strlen(custmgmt)); + + /*test: enable dhcp client function in firmware.*/ + hgic_iwpriv_set_dhcpc("hg0", 1); + + /*test: set wakedata match mask*/ + hgic_proc_set_wkdata_mask(42, mask, 4); + + /*test: enable firmware save wakeup data.*/ + hgic_iwpriv_set_wkdata_save("hg0", 1); + + /*test: get current bssid (for sta mode)*/ + hgic_iwpriv_get_bssid("hg0", dest); + printf("current bssid:"MACSTR"\r\n", MACARG(dest)); + + /*test: set remote AP's freqency info.*/ + hgic_proc_set_stafreqinfo(dest, &freqinfo); + + /*test: reset remote AP.*/ + hgic_iwpriv_reset_sta("hg0", dest); + + /*test: read wakeup data from firmware.*/ + hgic_test_read_wakeupdata(); + + /*loop read driver event and proc event.*/ + while (1) { + event = hgic_proc_read_event(); + if(event){ + hgic_event_proc(event); + } + } +} +#endif + diff --git a/utils/if_sdio.c b/utils/if_sdio.c new file mode 100644 index 0000000..45d1871 --- /dev/null +++ b/utils/if_sdio.c @@ -0,0 +1,899 @@ + +#ifdef __RTOS__ +#include +#include +#include +#include +#include +#include +#include "porting/sdio.h" +#else +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define sdio_func_t sdio_func +#define mmc_command_t mmc_command +#define sdio_device_id_t sdio_device_id +#define sdio_driver_t sdio_driver +#define vendor_id vendor +#define device_id device +#define f_num num +#endif + +#include "../hgic_def.h" +#include "utils.h" + +#define SDIO_BLOCK_SIZE 64 +#define SDIO_DATA_ADDR 0x0 +#define SDIO_TRANS_COUNT_ADDR 0x1C +#define SDIO_INIT_STATUS_ADDR 0x04 +#define SDIO_INIT_STATUS_ADDR2 0x48 +#define SDIO_TRANS_COUNT_ADDR2 0x49 +#define SDIO_INIT_STATUS_DATA_READY 0x01 +#define SDIO_STATUS_STOP BIT(0) +#define SDIO_STATUS_ERROR BIT(1) +#define SDIO_TX_HEADROM (4) + +struct hgic_sdio { + struct sdio_func_t *func; + u32 status; + u32 data_addr; /*sdio data register address*/ + u32 trans_cnt_addr; /*sdio data length register address*/ + u32 int_status_addr; /*interrupt status register address*/ + struct hgic_bus bus; + u32 rx_retry; +}; + +const struct sdio_device_id_t hgic_sdio_wdev_ids[] = { + {SDIO_DEVICE(HGIC_VENDOR_ID, HGIC_WLAN_AH_4001)}, + {SDIO_DEVICE(HGIC_VENDOR_ID, HGIC_WLAN_AH_4002)}, + {SDIO_DEVICE(HGIC_VENDOR_ID, HGIC_WLAN_AH_4102)}, + { /* end: all zeroes */ }, +}; + +#ifndef __RTOS__ +#ifndef SDIO_BUS_WIDTH_MASK +#define SDIO_BUS_WIDTH_MASK 3 +#endif +#ifndef mmc_card_highspeed +#define mmc_card_highspeed mmc_card_hs +#endif +#ifndef mmc_card_set_highspeed +#define mmc_card_set_highspeed(func) +#endif + +#define FUNC_DEV(f) (&((f)->dev)) + +#define SDIO_CAP_IRQ(func) ((func)->card->host->caps & MMC_CAP_SDIO_IRQ) +#define SDIO_CAP_POLL(func) ((func)->card->host->caps & MMC_CAP_NEEDS_POLL) +#define HOST_SPI_CRC(func, crc) (func)->card->host->use_spi_crc=crc + +//#define mmc_card_disable_cd(c) (1) +#define hgic_card_disable_cd(func) mmc_card_disable_cd((func)->card) +#define hgic_card_set_highspeed(func) mmc_card_set_highspeed((func)->card) +#define hgic_host_is_spi(func) mmc_host_is_spi((func)->card->host) +#define hgic_card_cccr_widebus(func) ((func)->card->cccr.low_speed && !(func)->card->cccr.wide_bus) +#define hgic_card_cccr_highspeed(func) ((func)->card->cccr.high_speed) +#define hgic_host_highspeed(func) ((func)->card->host->caps & MMC_CAP_SD_HIGHSPEED) +#define hgic_host_supp_4bit(func) ((func)->card->host->caps & MMC_CAP_4_BIT_DATA) +#define hgic_card_highspeed(func) mmc_card_highspeed((func)->card) +#define hgic_func_rca(func) ((func)->card->rca) +#define hgic_card_max_clock(func) ((func)->card->cis.max_dtr) + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(3,10,14) +#define hgic_card_ocr(func) ((func)->card->host->ocr) +#else +#define hgic_card_ocr(func) ((func)->card->ocr) +#endif + +static inline void hgic_mmc_set_timing(struct sdio_func_t *func, unsigned int timing) +{ + func->card->host->ios.timing = timing; + func->card->host->ops->set_ios(func->card->host, &func->card->host->ios); +} +static inline void hgic_mmc_set_bus_width(struct sdio_func_t *func, unsigned int width) +{ + func->card->host->ios.bus_width = width; + func->card->host->ops->set_ios(func->card->host, &func->card->host->ios); +} +static inline void hgic_mmc_set_clock(struct sdio_func_t *func, unsigned int hz) +{ + if (func->card->host->f_max && hz > func->card->host->f_max) { + hz = func->card->host->f_max; + } + func->card->host->ios.clock = hz; + func->card->host->ops->set_ios(func->card->host, &func->card->host->ios); +} +static inline int hgic_mmc_send_cmd(struct sdio_func_t *func, struct mmc_command_t *cmd, int retries) +{ + return mmc_wait_for_cmd(func->card->host, cmd, retries); +} +#endif + +static int hgic_mmc_io_rw_direct(struct sdio_func_t *func, int write, unsigned fn, + unsigned addr, u8 in, u8 *out) +{ + struct mmc_command_t cmd = {0}; + int err; + + /* sanity check */ + if (addr & ~0x1FFFF) { + return -EINVAL; + } + + cmd.opcode = SD_IO_RW_DIRECT; + cmd.arg = write ? 0x80000000 : 0x00000000; + cmd.arg |= fn << 28; + cmd.arg |= (write && out) ? 0x08000000 : 0x00000000; + cmd.arg |= addr << 9; + cmd.arg |= in; + cmd.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_AC; + + err = hgic_mmc_send_cmd(func, &cmd, 0); + if (err) { + return err; + } + + if (hgic_host_is_spi(func)) { + /* host driver already reported errors */ + } else { + if (cmd.resp[0] & R5_ERROR) { + return -EIO; + } + if (cmd.resp[0] & R5_FUNCTION_NUMBER) { + return -EINVAL; + } + if (cmd.resp[0] & R5_OUT_OF_RANGE) { + return -ERANGE; + } + } + + if (out) { + if (hgic_host_is_spi(func)) { + *out = (cmd.resp[0] >> 8) & 0xFF; + } else { + *out = cmd.resp[0] & 0xFF; + } + } + return 0; +} + +static int hgic_sdio_reset(struct sdio_func_t *func) +{ + int ret; + u8 abort; + + /* SDIO Simplified Specification V2.0, 4.4 Reset for SDIO */ + + ret = hgic_mmc_io_rw_direct(func, 0, 0, SDIO_CCCR_ABORT, 0, &abort); + if (ret) { + abort = 0x08; + } else { + abort |= 0x08; + } + + ret = hgic_mmc_io_rw_direct(func, 1, 0, SDIO_CCCR_ABORT, abort, NULL); + return ret; +} + +static int hgic_sdio_go_idle(struct sdio_func_t *func) +{ + int err; + struct mmc_command_t cmd = {0}; + cmd.opcode = MMC_GO_IDLE_STATE; + cmd.arg = 0; + cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_NONE | MMC_CMD_BC; + err = hgic_mmc_send_cmd(func, &cmd, 0); + msleep(1); + HOST_SPI_CRC(func, 0); + return err; +} + +static int hgic_mmc_send_if_cond(struct sdio_func_t *func, u32 ocr) +{ + struct mmc_command_t cmd = {0}; + int err; + static const u8 test_pattern = 0xAA; + u8 result_pattern; + + /* + * To support SD 2.0 cards, we must always invoke SD_SEND_IF_COND + * before SD_APP_OP_COND. This command will harmlessly fail for + * SD 1.0 cards. + */ + cmd.opcode = SD_SEND_IF_COND; + cmd.arg = ((ocr & 0xFF8000) != 0) << 8 | test_pattern; + cmd.flags = MMC_RSP_SPI_R7 | MMC_RSP_R7 | MMC_CMD_BCR; + + err = hgic_mmc_send_cmd(func, &cmd, 0); + if (err) { + return err; + } + + if (hgic_host_is_spi(func)) { + result_pattern = cmd.resp[1] & 0xFF; + } else { + result_pattern = cmd.resp[0] & 0xFF; + } + + if (result_pattern != test_pattern) { + return -EIO; + } + + return 0; +} + +static int hgic_send_io_op_cond(struct sdio_func_t *func, u32 ocr, u32 *rocr) +{ + struct mmc_command_t cmd = {0}; + int i, err = 0; + + BUG_ON(!func); + + cmd.opcode = SD_IO_SEND_OP_COND; + cmd.arg = ocr; + cmd.flags = MMC_RSP_SPI_R4 | MMC_RSP_R4 | MMC_CMD_BCR; + + for (i = 10; i; i--) { + err = hgic_mmc_send_cmd(func, &cmd, 3); + if (err) { + break; + } + + /* if we're just probing, do a single pass */ + if (ocr == 0) { + break; + } + + /* otherwise wait until reset completes */ + if (hgic_host_is_spi(func)) { + /* + * Both R1_SPI_IDLE and MMC_CARD_BUSY indicate + * an initialized card under SPI, but some cards + * (Marvell's) only behave when looking at this + * one. + */ + if (cmd.resp[1] & MMC_CARD_BUSY) { + break; + } + } else { + if (cmd.resp[0] & MMC_CARD_BUSY) { + break; + } + } + + err = -ETIMEDOUT; + + mdelay(10); + } + + if (rocr) { + *rocr = cmd.resp[hgic_host_is_spi(func) ? 1 : 0]; + } + + return err; +} + + +static int hgic_sdio_get_card_addr(struct sdio_func_t *func, u32 *rca) +{ + int err; + struct mmc_command_t cmd = {0}; + cmd.opcode = SD_SEND_RELATIVE_ADDR; + cmd.arg = 0; + cmd.flags = MMC_RSP_R6 | MMC_CMD_BCR; + err = hgic_mmc_send_cmd(func, &cmd, 3); + if (err) { + return err; + } + *rca = cmd.resp[0] >> 16; + return 0; +} + +static u32 hgic_sdio_set_highspeed(struct sdio_func_t *func, int enable) +{ + int ret; + u8 speed; + + if (!hgic_host_highspeed(func)) { + return 0; + } + + if (!hgic_card_cccr_highspeed(func)) { + return 0; + } + + ret = hgic_mmc_io_rw_direct(func, 0, 0, SDIO_CCCR_SPEED, 0, &speed); + if (ret) { + return ret; + } + + if (enable) { + speed |= SDIO_SPEED_EHS; + } else { + speed &= ~SDIO_SPEED_EHS; + } + + ret = hgic_mmc_io_rw_direct(func, 1, 0, SDIO_CCCR_SPEED, speed, NULL); + if (ret) { + return ret; + } + + hgic_card_set_highspeed(func); + return 1; +} + +static int hgic_sdio_disable_cd(struct sdio_func_t *func) +{ + int ret; + u8 ctrl; + + if (!hgic_card_disable_cd(func)) { + return 0; + } + + ret = hgic_mmc_io_rw_direct(func, 0, 0, SDIO_CCCR_IF, 0, &ctrl); + if (ret) { + return ret; + } + ctrl |= SDIO_BUS_CD_DISABLE; + return hgic_mmc_io_rw_direct(func, 1, 0, SDIO_CCCR_IF, ctrl, NULL); +} + +static int hgic_sdio_set_wire_width(struct sdio_func_t *func) +{ + int ret; + u8 ctrl; + + if (!hgic_host_supp_4bit(func)) { + return 0; + } + + if (hgic_card_cccr_widebus(func)) { + return 0; + } + + ret = hgic_mmc_io_rw_direct(func, 0, 0, SDIO_CCCR_IF, 0, &ctrl); + if (ret) { + return ret; + } + + /* set as 4-bit bus width */ + ctrl &= ~ SDIO_BUS_WIDTH_MASK; + ctrl |= SDIO_BUS_WIDTH_4BIT; + ret = hgic_mmc_io_rw_direct(func, 1, 0, SDIO_CCCR_IF, ctrl, NULL); + if (ret) { + return ret; + } + + hgic_mmc_set_bus_width(func, MMC_BUS_WIDTH_4); + return 1; +} + +static u32 hgic_sdio_select_card(struct sdio_func_t *func) +{ + int err; + struct mmc_command_t cmd = {0}; + + cmd.opcode = MMC_SELECT_CARD; + if (func) { + cmd.arg = hgic_func_rca(func) << 16; + cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; + } else { + cmd.arg = 0; + cmd.flags = MMC_RSP_NONE | MMC_CMD_AC; + } + + err = hgic_mmc_send_cmd(func, &cmd, 3); + if (err) { + return err; + } + + return 0; +} + +static int hgic_sdio_int_enable(struct sdio_func_t *func, u8 enable) +{ + u8 temp; + u8 dat; + + if (enable) { + dat = (1 << func->f_num) | 0x1; + } else { + dat = 0x1; + } + return hgic_mmc_io_rw_direct(func, 1, 0, SDIO_CCCR_IENx, dat, &temp); +} + +static int hgic_mmc_spi_set_crc(struct sdio_func_t *func, int use_crc) +{ + struct mmc_command_t cmd = {0}; + int err; + + cmd.opcode = MMC_SPI_CRC_ON_OFF; + cmd.flags = MMC_RSP_SPI_R1; + cmd.arg = use_crc; + + err = hgic_mmc_send_cmd(func, &cmd, 0); + if (!err) { + HOST_SPI_CRC(func, use_crc); + } + return err; +} + + +static int hgic_sdio_reinit_card(struct sdio_func_t *func) +{ + int i = 3; + u32 ocr = 0; + int retry = 0; + struct hgic_sdio *sdiodev = sdio_get_drvdata(func); + u32 rca = 0; + + hgic_enter(); +__RETRY: + if (!sdiodev || + test_bit(HGIC_BUS_FLAGS_DISABLE_REINIT, &sdiodev->bus.flags) || + test_bit(HGIC_BUS_FLAGS_SLEEP, &sdiodev->bus.flags)) { + hgic_err("leave, can not reinit\r\n"); + return -1; + } + if (retry++ > 2) { + hgic_err("leave, reinit fail\r\n"); + return -1; + } + + i = 5; + ocr = 0; + hgic_mmc_set_clock(func, 400000); + hgic_mmc_set_timing(func, MMC_TIMING_LEGACY); + hgic_mmc_set_bus_width(func, MMC_BUS_WIDTH_1); + + hgic_sdio_reset(func); + hgic_sdio_go_idle(func); + hgic_mmc_send_if_cond(func, ocr); + + if (hgic_send_io_op_cond(func, 0, (u32 *)&ocr)) { + goto __RETRY; + } + if (hgic_send_io_op_cond(func, hgic_card_ocr(func), (u32 *)&ocr)) { + goto __RETRY; + } + + if (hgic_host_is_spi(func)) { + if (hgic_mmc_spi_set_crc(func, 1)) { + goto __RETRY; + } + } else { + rca = hgic_func_rca(func); + if (hgic_sdio_get_card_addr(func, (u32 *)&rca)) { + goto __RETRY; + } + if (hgic_sdio_select_card(func)) { + goto __RETRY; + } + if (hgic_sdio_disable_cd(func) < 0) { + goto __RETRY; + } + } + + if (hgic_sdio_set_highspeed(func, 1) < 0) { + goto __RETRY; + } + + hgic_mmc_set_clock(func, hgic_card_highspeed(func) ? 50000000 : hgic_card_max_clock(func)); + hgic_mmc_set_timing(func, MMC_TIMING_SD_HS); + if (hgic_sdio_set_wire_width(func) < 0) { + goto __RETRY; + } + if (sdio_set_block_size(func, SDIO_BLOCK_SIZE)) { + goto __RETRY; + } + if (hgic_sdio_int_enable(func, 1)) { + goto __RETRY; + } + if (sdio_enable_func(func)) { + goto __RETRY; + } + hgic_leave(); +#ifndef __RTOS__ + if (func->card->host->sdio_irq_thread) { + wake_up_process(func->card->host->sdio_irq_thread); + } +#endif + return 0; +} + +static int hgic_sdio_reinit(void *bus) +{ + int ret = 0; + struct hgic_sdio *sdiodev = container_of(bus, struct hgic_sdio, bus); + sdio_claim_host(sdiodev->func); + ret = hgic_sdio_reinit_card(sdiodev->func); + sdio_release_host(sdiodev->func); + return ret; +} + +static int hgic_sdio_readb(struct sdio_func_t *func, u32 addr, int *err) +{ + u8 val = 0; + int retry = 2; + + val = sdio_readb(func, addr, err); + if (val == 0) { *err = -1; } //if read 0, try again. + while (*err && retry-- > 0) { + val = sdio_readb(func, addr, err); + } + return val; +} + +static void hgic_sdio_writeb(struct sdio_func_t *func, u8 b, u32 addr, int *err) +{ + int retry = 4; + do { + sdio_writeb(func, b, addr, err); + } while (*err && retry-- > 0); +} + +static int hgic_sdio_copy_fromio(struct sdio_func_t *func, u8 *dest, u32 addr, int count) +{ + int err = 0; + err = sdio_memcpy_fromio(func, dest, addr, count); + if (err) { + hgic_err("err=%d\r\n", err); + hgic_sdio_reinit_card(func); + } + return err; +} + +static int hgic_sdio_copy_toio(struct sdio_func_t *func, u32 addr, u8 *src, int count) +{ + int err = 0; + + err = sdio_memcpy_toio(func, addr, src, count); + if (err) { + hgic_err("err=%d\r\n", err); + if (!hgic_sdio_reinit_card(func)) { + err = sdio_memcpy_toio(func, addr, src, count); + } + } + return err; +} + +static int hgic_sdio_abort(struct sdio_func_t *func) +{ + int err_ret = 0; + struct sdio_func_t func0; + memcpy(&func0, func, sizeof(func0)); + func0.f_num = 0; + hgic_sdio_writeb(&func0, 1, 6, &err_ret); + return err_ret; +} +static int hgic_sdio_read_cccr(struct sdio_func_t *func, u8 *pending) +{ + int ret = 0; + struct sdio_func_t func0; + memcpy(&func0, func, sizeof(func0)); + func0.f_num = 0; + *pending = hgic_sdio_readb(&func0, 0x5, &ret); + return ret; +} + +static u32 hgic_sdio_data_len(struct hgic_sdio *sdiodev) +{ + int err = 0; + u32 len = 0; + u8 ret = 0; + u16 addr = test_bit(HGIC_BUS_FLAGS_INBOOT, &sdiodev->bus.flags) ? + sdiodev->trans_cnt_addr : SDIO_TRANS_COUNT_ADDR2; + + ret = hgic_sdio_readb(sdiodev->func, addr, &err); + if (err) { return 0xffffffff; } + len = ret; + ret = hgic_sdio_readb(sdiodev->func, addr + 1, &err); + if (err) { return 0xffffffff; } + len |= ret << 8; + + if (len == 0) { + if (test_bit(HGIC_BUS_FLAGS_INBOOT, &sdiodev->bus.flags)) { + len = 16; + } else { + len = 0xFFFFFFFF; + hgic_err("get len error\r\n"); + } + } + return len; +} + + +static int hgic_sdio_is_data_ready(struct hgic_sdio *sdiodev, struct sdio_func_t *func) +{ + int ret = 0; + u8 pending = 0; + u8 int_status1 = 0; + u8 int_status2 = 0; + + ret = hgic_sdio_read_cccr(func, &pending); + if (!ret && (pending & 02)) { + int_status1 = sdio_readb(func, sdiodev->int_status_addr, &ret); + if (ret) { int_status1 = sdio_readb(func, sdiodev->int_status_addr, &ret); } + int_status1 &= SDIO_INIT_STATUS_DATA_READY; + if (!ret && !int_status1 && !test_bit(HGIC_BUS_FLAGS_INBOOT, &sdiodev->bus.flags)) { + int_status2 = hgic_sdio_readb(func, SDIO_INIT_STATUS_ADDR2, &ret); + if (int_status1 != int_status2) { + hgic_err("INTID(%x:%x)\r\n", int_status1, int_status2); + int_status1 = int_status2; + } + } + if (!int_status1) { hgic_sdio_abort(func); } + } + return int_status1; +} + +static void hgic_sdio_interrupt(struct sdio_func_t *func) +{ + int ret = 0; + u32 len = 0; + int read_more = 0; + struct sk_buff *skb = NULL; + struct hgic_sdio *sdiodev = sdio_get_drvdata(func); + + if (sdiodev == NULL || (sdiodev->status&SDIO_STATUS_STOP) || test_bit(HGIC_BUS_FLAGS_SLEEP, &sdiodev->bus.flags)) { + return; + } + read_more = sdiodev->rx_retry; + do { + if (hgic_sdio_is_data_ready(sdiodev, func)) { + read_more = sdiodev->rx_retry; + len = hgic_sdio_data_len(sdiodev); + if (len == 0xFFFFFFFF) { + hgic_err("data len error\r\n"); + hgic_sdio_abort(func); + hgic_sdio_reinit_card(func); + return; + } else if (len > 0) { + skb = dev_alloc_skb(len); + if (skb == NULL) { + hgic_err("no memory\r\n"); + hgic_sdio_abort(func); + return; + } + ret = hgic_sdio_copy_fromio(sdiodev->func, skb->data, + sdiodev->data_addr, len); + if (ret) { + dev_kfree_skb(skb); + hgic_err("sdio_copy_fromio err!, ret:%d\r\n", ret); + return; + } + ret = sdiodev->bus.rx_packet(sdiodev->bus.bus_priv, skb, len); + if (ret) { + hgic_sdio_abort(func); + } + } else { + hgic_sdio_abort(func); + } + } else if (read_more > 0) { + udelay(20); + } + } while (!ret && read_more-- > 0); +} + +static int hgic_sdio_tx_packet(void *bus, struct sk_buff *skb) +{ + int ret = 0; + struct hgic_sdio *sdiodev = container_of(bus, struct hgic_sdio, bus); + int len = skb->len > SDIO_BLOCK_SIZE ? ALIGN(skb->len, SDIO_BLOCK_SIZE) : ALIGN(skb->len, 4); + + sdio_claim_host(sdiodev->func); + if ((sdiodev->status & SDIO_STATUS_STOP) || test_bit(HGIC_BUS_FLAGS_SLEEP, &sdiodev->bus.flags)) { + sdiodev->bus.tx_complete(sdiodev->bus.bus_priv, skb, 0); + sdio_release_host(sdiodev->func); + return -1; + } + ret = hgic_sdio_copy_toio(sdiodev->func, sdiodev->data_addr, skb->data, len); + sdio_release_host(sdiodev->func); + sdiodev->bus.tx_complete(sdiodev->bus.bus_priv, skb, !ret); + return ret; +} + +static struct hgic_bus hgic_ifbus_sdio = { + .type = HGIC_BUS_SDIO, + .drv_tx_headroom = SDIO_TX_HEADROM, + .tx_packet = hgic_sdio_tx_packet, + .reinit = hgic_sdio_reinit, + .bootdl_pktlen = 32704, + .bootdl_cksum = HGIC_BUS_BOOTDL_CHECK_0XFD, +}; + +static int hgic_sdio_enable(struct sdio_func_t *func) +{ + int ret; + + hgic_dbg("Enter\n"); + sdio_claim_host(func); + ret = sdio_set_block_size(func, SDIO_BLOCK_SIZE); + if (ret) { + hgic_err("Set sdio block size %d failed: %d)\n", SDIO_BLOCK_SIZE, ret); + goto out; + } + ret = sdio_claim_irq(func, hgic_sdio_interrupt); + if (ret) { + hgic_err("Set sdio interrupte handle failed: %d\n", ret); + goto out; + } + ret = sdio_enable_func(func); + if (ret) { + hgic_err("enable sdio function failed: %d)\n", ret); + goto out; + } + hgic_dbg("ok\n"); + +out: + sdio_release_host(func); + return ret; +} + +int hgic_sdio_probe(struct sdio_func_t *func, const struct sdio_device_id_t *id) +{ + int ret = 0; + struct hgic_sdio *sdiodev = NULL; + + hgic_dbg("Enter, func->num:%d, devid:%x\r\n", func->f_num, func->device_id); + if (func->f_num != 1) { + return -ENODEV; + } + + sdiodev = kzalloc(sizeof(struct hgic_sdio), GFP_KERNEL); + if (!sdiodev) { + return -ENOMEM; + } + + hgic_dbg("new sdio card: vendor:%x, id:%x\n", id->vendor_id, id->device_id); + sdiodev->func = func; + sdiodev->data_addr = SDIO_DATA_ADDR; + sdiodev->trans_cnt_addr = SDIO_TRANS_COUNT_ADDR; + sdiodev->int_status_addr = SDIO_INIT_STATUS_ADDR; + sdiodev->status = SDIO_STATUS_STOP; + sdiodev->bus = hgic_ifbus_sdio; + sdiodev->bus.dev_id = id->device_id; + sdiodev->bus.blk_size = SDIO_BLOCK_SIZE; + sdiodev->rx_retry = SDIO_CAP_IRQ(func) ? 0 : 5; + if (!SDIO_CAP_POLL(func)) { + set_bit(HGIC_BUS_FLAGS_NOPOLL, &sdiodev->bus.flags); + } + if (hgic_host_is_spi(func)) { + sdiodev->bus.bootdl_pktlen = 4096; + } + + hgic_core_probe(FUNC_DEV(func), &sdiodev->bus); + if (sdiodev->bus.bus_priv == NULL) { + hgic_err("err\r\n"); + goto __failed; + } + + sdio_set_drvdata(func, sdiodev); + ret = hgic_sdio_enable(func); + if (ret) { + goto __failed; + } + + sdiodev->status &= ~SDIO_STATUS_STOP; + hgic_core_probe_post(sdiodev->bus.bus_priv); + hgic_dbg("ok\n"); + return ret; + +__failed: + hgic_dbg("failed\n"); + sdio_set_drvdata(func, NULL); + hgic_core_remove(sdiodev->bus.bus_priv); + kfree(sdiodev); + hgic_dbg("failed 2\n"); + return ret; +} + +void hgic_sdio_remove(struct sdio_func_t *func) +{ + struct hgic_sdio *sdiodev = NULL; + + hgic_dbg("Enter\n"); + sdiodev = (struct hgic_sdio *)sdio_get_drvdata(func); + if (sdiodev) { + set_bit(HGIC_BUS_FLAGS_DISABLE_REINIT, &sdiodev->bus.flags); + sdiodev->status |= SDIO_STATUS_STOP; + hgic_dbg("remove ... 1\n"); + sdio_claim_host(func); + sdio_release_irq(func); + sdio_release_host(func); + hgic_dbg("remove ... 2\n"); + hgic_core_remove(sdiodev->bus.bus_priv); + hgic_dbg("remove ... 3\n"); + sdio_claim_host(func); + sdio_set_drvdata(func, NULL); + kfree(sdiodev); + sdio_release_host(func); + hgic_dbg("remove ... 4\n"); + } + hgic_dbg("Leave\n"); +} + +#ifdef CONFIG_PM +static int hgic_sdio_suspend(struct device *dev) +{ + int ret = 0; + struct sdio_func_t *func = dev_to_sdio_func(dev); + struct hgic_sdio *sdiodev = (struct hgic_sdio *)sdio_get_drvdata(func); + ret = hgic_core_suspend(sdiodev->bus.bus_priv); + sdio_claim_host(func); + ret = sdio_release_irq(func); + sdio_release_host(func); + return ret; +} +static int hgic_sdio_resume(struct device *dev) +{ + int ret = 0; + struct sdio_func_t *func = dev_to_sdio_func(dev); + struct hgic_sdio *sdiodev = (struct hgic_sdio *)sdio_get_drvdata(func); + sdio_claim_host(func); + ret = sdio_claim_irq(func, hgic_sdio_interrupt); + sdio_release_host(func); + ret = hgic_core_resume(sdiodev->bus.bus_priv); + return ret; +} +static const struct dev_pm_ops hgic_sdio_pm_ops = { + .suspend = hgic_sdio_suspend, + .resume = hgic_sdio_resume, +}; +#endif +#ifdef SDIO_DRIVER_EXT +extern struct sdio_driver_t hgic_sdio_driver; +#else +static struct sdio_driver_t hgic_sdio_driver = { + .name = "hgic_sdio_wlan", + .id_table = hgic_sdio_wdev_ids, + .probe = hgic_sdio_probe, + .remove = hgic_sdio_remove, +#ifdef CONFIG_PM + .drv = { + .pm = &hgic_sdio_pm_ops, + }, +#endif +}; +#endif + +int __init hgic_sdio_init(void) +{ + int ret; + + hgic_dbg("Enter\n"); + ret = sdio_register_driver(&hgic_sdio_driver); + hgic_dbg("Leave\n"); + return ret; +} + +void __exit hgic_sdio_exit(void) +{ + hgic_dbg("Enter\n"); + sdio_unregister_driver(&hgic_sdio_driver); + hgic_dbg("Leave\n"); +} + +#ifndef CONFIG_HGIC_SDIOIN +module_init(hgic_sdio_init); +module_exit(hgic_sdio_exit); +MODULE_DESCRIPTION("HUGE-IC WLAN Driver"); +MODULE_AUTHOR("Dongyun"); +MODULE_LICENSE("GPL"); +#endif + diff --git a/utils/if_usb.c b/utils/if_usb.c new file mode 100644 index 0000000..f221eb3 --- /dev/null +++ b/utils/if_usb.c @@ -0,0 +1,378 @@ + +#include +#include +#include +#include +#if LINUX_VERSION_CODE > KERNEL_VERSION(3,0,0) +#include +#else +#include +#endif +#include +#include + +#include "../hgic_def.h" +#include "utils.h" + +#define HGIC_PKT_MAX_LEN 2048 +#define HGIC_TX_URB_CNT 64 +#define HGIC_RX_URB_CNT 64 + +#define HGIC_USB_STATUS_STOP BIT(0) +#define HGIC_USB_STATUS_ERR BIT(1) + +#define HGIC_USB_BUF_FLAG_USED 0x00000001 +#define USB_TX_HEADROM 4 + +struct hgic_usb_buf { + struct list_head list; + struct hgic_usb *usbdev; + struct urb *urb; + struct sk_buff *skb; + int flag; +}; + +struct hgic_usb { + struct usb_device *udev; + uint ep_in, ep_out; + uint ep_out_size, ep_in_size; + uint status; + struct list_head tx_freeq; + struct list_head rx_freeq; + struct list_head used; + struct semaphore tx_sema; + spinlock_t qlock; + struct hgic_bus bus; +}; + +static int txq_cnt = HGIC_TX_URB_CNT; +static int rxq_cnt = HGIC_RX_URB_CNT; + +static const struct usb_device_id hgic_usb_wdev_ids[] = { + { USB_DEVICE(HGIC_VENDOR_ID, HGIC_WLAN_AH_4001) }, + { USB_DEVICE(HGIC_VENDOR_ID, HGIC_WLAN_AH_4002) }, + { USB_DEVICE(HGIC_VENDOR_ID, HGIC_WLAN_AH_4102) }, + { /* end: all zeroes */ }, +}; +MODULE_DEVICE_TABLE(usb, hgic_usb_wdev_ids); +static void hgic_usb_receive(struct urb *urb); + +static void hgic_usb_free(struct hgic_usb *usbdev) +{ + unsigned long flags; + struct hgic_usb_buf *buf, *n; + + spin_lock_irqsave(&usbdev->qlock, flags); + list_for_each_entry_safe(buf, n, &usbdev->tx_freeq, list) { + list_del(&buf->list); + usb_free_urb(buf->urb); + kfree(buf); + //hgic_dbg("free usb tx buff\r\n"); + } + list_for_each_entry_safe(buf, n, &usbdev->rx_freeq, list) { + list_del(&buf->list); + usb_free_urb(buf->urb); + kfree(buf); + //hgic_dbg("free usb rx buff\r\n"); + } + list_for_each_entry_safe(buf, n, &usbdev->used, list) { + usb_kill_urb(buf->urb); + list_del(&buf->list); + usb_free_urb(buf->urb); + kfree(buf); + } + spin_unlock_irqrestore(&usbdev->qlock, flags); +} + +static int hgic_usb_qinit(struct hgic_usb *usb, struct list_head *q, int qsize) +{ + int i = 0; + struct hgic_usb_buf *buf = NULL; + + for (i = 0; i < qsize; i++) { + buf = kzalloc(sizeof(struct hgic_usb_buf), GFP_ATOMIC); + if (buf == NULL) { + break; + } + + buf->usbdev = usb; + buf->urb = usb_alloc_urb(0, GFP_KERNEL); + if (buf->urb) { + list_add_tail(&buf->list, q); + } else { + kfree(buf); + break; + } + } + return i; +} + +struct hgic_usb_buf *hgic_usb_deq(struct hgic_usb *usbdev, struct list_head *q) +{ + struct hgic_usb_buf *buf = NULL; + unsigned long flags; + + spin_lock_irqsave(&usbdev->qlock, flags); + if (!list_empty(q)) { + buf = list_first_entry(q, struct hgic_usb_buf, list); + buf->flag |= HGIC_USB_BUF_FLAG_USED; + list_del(&buf->list); + list_add_tail(&buf->list, &usbdev->used); + } + spin_unlock_irqrestore(&usbdev->qlock, flags); + return buf; +} + +void hgic_usb_enq(struct hgic_usb_buf *buf, struct list_head *q) +{ + unsigned long flags; + + spin_lock_irqsave(&buf->usbdev->qlock, flags); + buf->skb = NULL; + buf->flag &= ~HGIC_USB_BUF_FLAG_USED; + list_del(&buf->list); + list_add_tail(&buf->list, q); + spin_unlock_irqrestore(&buf->usbdev->qlock, flags); +} + +static int hgic_usb_submit_rx_urb(struct hgic_usb_buf *buf) +{ + int ret = -1; + + buf->skb = dev_alloc_skb(HGIC_PKT_MAX_LEN); + if (!buf->skb) { + hgic_err("alloc skb failed\r\n"); + hgic_usb_enq(buf, &buf->usbdev->rx_freeq); + return -1; + } + usb_fill_bulk_urb(buf->urb, buf->usbdev->udev, + usb_rcvbulkpipe(buf->usbdev->udev, buf->usbdev->ep_in), + buf->skb->data, HGIC_PKT_MAX_LEN, hgic_usb_receive, buf); + ret = usb_submit_urb(buf->urb, GFP_ATOMIC); + if (ret) { + hgic_err("submit rx urb failed: %d\n", ret); + dev_kfree_skb_any(buf->skb); + hgic_usb_enq(buf, &buf->usbdev->rx_freeq); + buf->usbdev->status |= HGIC_USB_STATUS_ERR; + return -1; + } + return 0; +} + +static int hgic_usb_submit_rx_urbs(struct hgic_usb *usbdev) +{ + int ret = 0; + struct hgic_usb_buf *buf = NULL; + + while ((buf = hgic_usb_deq(usbdev, &usbdev->rx_freeq))) { + ret = hgic_usb_submit_rx_urb(buf); + if (ret) { + break; + } + } + return ret; +} + +static void hgic_usb_receive(struct urb *urb) +{ + struct hgic_usb_buf *buf = (struct hgic_usb_buf *)urb->context; + + if (buf->usbdev->status & (HGIC_USB_STATUS_STOP | HGIC_USB_STATUS_ERR)) { + dev_kfree_skb_any(buf->skb); + hgic_usb_enq(buf, &buf->usbdev->rx_freeq); + return; + } + + if (urb->actual_length > 0) { + buf->usbdev->bus.rx_packet(buf->usbdev->bus.bus_priv, buf->skb, urb->actual_length); + } else { + dev_kfree_skb_any(buf->skb); + } + hgic_usb_submit_rx_urb(buf); +} + +static void hgic_usb_tx_complete(struct urb *urb) +{ + struct hgic_usb_buf *buf = (struct hgic_usb_buf *)urb->context; + //hgic_skip_padding(buf->skb); + buf->usbdev->bus.tx_complete(buf->usbdev->bus.bus_priv, buf->skb, !urb->status); + hgic_usb_enq(buf, &buf->usbdev->tx_freeq); + up(&buf->usbdev->tx_sema); +} + +static int hgic_usb_tx_packet(void *bus, struct sk_buff *skb) +{ + int ret = 0; + int len = ALIGN(skb->len, 4); + struct hgic_usb_buf *buf = NULL; + struct hgic_usb *usbdev = container_of(bus, struct hgic_usb, bus); + + if ((len & (usbdev->ep_out_size - 1)) == 0) { + len += 4; + } + if (usbdev->status & (HGIC_USB_STATUS_STOP | HGIC_USB_STATUS_ERR)) { + ret = -EIO; + goto __fail; + } + + ret = down_timeout(&usbdev->tx_sema, 1000); + if(ret){ + ret = -EIO; + goto __fail; + } + + buf = hgic_usb_deq(usbdev, &usbdev->tx_freeq); + if (buf == NULL) { + ret = -ENOMEM; + goto __fail; + } + buf->skb = skb; + usb_fill_bulk_urb(buf->urb, usbdev->udev, + usb_sndbulkpipe(usbdev->udev, usbdev->ep_out), + skb->data, len, /*ALIGN(skb->len, 4)*/ + hgic_usb_tx_complete, buf); + buf->urb->transfer_flags |= URB_ZERO_PACKET; + ret = usb_submit_urb(buf->urb, GFP_ATOMIC); + if (ret) { + hgic_err("usb_submit_urb failed, ret:%d\n", ret); + goto __fail; + } + + return ret; + +__fail: + if (buf) { + hgic_usb_enq(buf, &usbdev->tx_freeq); + } + usbdev->bus.tx_complete(usbdev->bus.bus_priv, skb, 0); + return ret; +} + +static struct hgic_bus hgic_bus_usb = { + .type = HGIC_BUS_USB, + .drv_tx_headroom = USB_TX_HEADROM, + .tx_packet = hgic_usb_tx_packet, + .bootdl_pktlen = 2048, + .bootdl_cksum = HGIC_BUS_BOOTDL_CHECK_0XFD, +}; + +static int hgic_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) +{ + int i = 0; + int ret = 0; + struct usb_device *udev = NULL; + struct hgic_usb *usbdev = NULL; + struct usb_host_interface *iface_desc = NULL; + struct usb_endpoint_descriptor *endpoint = NULL; + + hgic_dbg("new usb card: vendor:%x, id:%x\n", id->idVendor, id->idProduct); + udev = interface_to_usbdev(intf); + iface_desc = intf->cur_altsetting; + usbdev = kzalloc(sizeof(struct hgic_usb), GFP_KERNEL); + if (!usbdev) { + return -ENOMEM; + } + + memset(usbdev, 0, sizeof(struct hgic_usb)); + usbdev->udev = udev; + usbdev->bus = hgic_bus_usb; + usbdev->bus.dev_id = id->idProduct; + usbdev->status = HGIC_USB_STATUS_STOP; + + for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { + endpoint = &iface_desc->endpoint[i].desc; + if (usb_endpoint_is_bulk_in(endpoint)) { + usbdev->ep_in_size = le16_to_cpu(endpoint->wMaxPacketSize); + usbdev->ep_in = usb_endpoint_num(endpoint); + hgic_dbg("IN BULK: ep_in_size:%x, ep_in:%x\n", usbdev->ep_in_size, usbdev->ep_in); + } else if (usb_endpoint_is_bulk_out(endpoint)) { + usbdev->ep_out_size = le16_to_cpu(endpoint->wMaxPacketSize); + usbdev->ep_out = usb_endpoint_num(endpoint); + hgic_dbg("OUT BULK: ep_out_size:%x, ep_out:%x\n", usbdev->ep_out_size, usbdev->ep_out); + } + } + usbdev->bus.blk_size = usbdev->ep_out_size; + + spin_lock_init(&usbdev->qlock); + INIT_LIST_HEAD(&usbdev->tx_freeq); + INIT_LIST_HEAD(&usbdev->rx_freeq); + INIT_LIST_HEAD(&usbdev->used); + ret = hgic_usb_qinit(usbdev, &usbdev->tx_freeq, txq_cnt); + sema_init(&usbdev->tx_sema, ret); + hgic_dbg("usb txq:%d\r\n", ret); + ret = hgic_usb_qinit(usbdev, &usbdev->rx_freeq, rxq_cnt); + hgic_dbg("usb rxq:%d\r\n", ret); + + hgic_core_probe(&udev->dev, &usbdev->bus); + if (!usbdev->bus.bus_priv) { + ret = -ENOMEM; + goto __failed; + } + + ret = hgic_usb_submit_rx_urbs(usbdev); + if (ret) { + goto __failed; + } + usb_get_dev(udev); + usb_set_intfdata(intf, usbdev); + usbdev->status &= ~HGIC_USB_STATUS_STOP; + hgic_core_probe_post(usbdev->bus.bus_priv); + return ret; + +__failed: + hgic_core_remove(usbdev->bus.bus_priv); + hgic_usb_free(usbdev); + kfree(usbdev); + return -1; +} + +static void hgic_usb_disconnect(struct usb_interface *intf) +{ + struct hgic_usb *usbdev = usb_get_intfdata(intf); + hgic_dbg("hgic_usb_disconnect>>>>\r\n"); + if (usbdev) { + usbdev->status |= HGIC_USB_STATUS_STOP; + hgic_core_remove(usbdev->bus.bus_priv); + hgic_usb_free(usbdev); + kfree(usbdev); + usb_set_intfdata(intf, NULL); + } +} + +static struct usb_driver hgic_usb_driver = { + .name = KBUILD_MODNAME, + .probe = hgic_usb_probe, + .disconnect = hgic_usb_disconnect, + .id_table = hgic_usb_wdev_ids, + .suspend = NULL, + .resume = NULL, + .supports_autosuspend = 1, +}; + +int __init hgic_usb_init(void) +{ + int ret = 0; + + hgic_dbg("Enter\n"); + ret = usb_register(&hgic_usb_driver); + if (ret) { + hgic_err("driver register failed: %d\n", ret); + } + hgic_dbg("Leave\n"); + return ret; +} + +void __exit hgic_usb_exit(void) +{ + hgic_dbg("Enter\n"); + usb_deregister(&hgic_usb_driver); + hgic_dbg("Leave\n"); +} +#ifndef CONFIG_HGIC_USBIN +module_init(hgic_usb_init); +module_exit(hgic_usb_exit); +MODULE_DESCRIPTION("HUGE-IC WLAN Driver"); +MODULE_AUTHOR("Dongyun"); +MODULE_LICENSE("GPL"); +#endif + diff --git a/utils/mmc_spi.c b/utils/mmc_spi.c new file mode 100644 index 0000000..e18d27a --- /dev/null +++ b/utils/mmc_spi.c @@ -0,0 +1,1428 @@ +/* + * mmc_spi.c - Access SD/MMC cards through SPI master controllers + * + * (C) Copyright 2005, Intec Automation, + * Mike Lavender (mike@steroidmicros) + * (C) Copyright 2006-2007, David Brownell + * (C) Copyright 2007, Axis Communications, + * Hans-Peter Nilsson (hp@axis.com) + * (C) Copyright 2007, ATRON electronic GmbH, + * Jan Nikitenko + * + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include /* for R1_SPI_* bit values */ +#include + +#include +#include +#include + +#include + + +/* NOTES: + * + * - For now, we won't try to interoperate with a real mmc/sd/sdio + * controller, although some of them do have hardware support for + * SPI protocol. The main reason for such configs would be mmc-ish + * cards like DataFlash, which don't support that "native" protocol. + * + * We don't have a "DataFlash/MMC/SD/SDIO card slot" abstraction to + * switch between driver stacks, and in any case if "native" mode + * is available, it will be faster and hence preferable. + * + * - MMC depends on a different chipselect management policy than the + * SPI interface currently supports for shared bus segments: it needs + * to issue multiple spi_message requests with the chipselect active, + * using the results of one message to decide the next one to issue. + * + * Pending updates to the programming interface, this driver expects + * that it not share the bus with other drivers (precluding conflicts). + * + * - We tell the controller to keep the chipselect active from the + * beginning of an mmc_host_ops.request until the end. So beware + * of SPI controller drivers that mis-handle the cs_change flag! + * + * However, many cards seem OK with chipselect flapping up/down + * during that time ... at least on unshared bus segments. + */ + + +/* + * Local protocol constants, internal to data block protocols. + */ + +/* Response tokens used to ack each block written: */ +#define SPI_MMC_RESPONSE_CODE(x) ((x) & 0x1f) +#define SPI_RESPONSE_ACCEPTED ((2 << 1)|1) +#define SPI_RESPONSE_CRC_ERR ((5 << 1)|1) +#define SPI_RESPONSE_WRITE_ERR ((6 << 1)|1) + +/* Read and write blocks start with these tokens and end with crc; + * on error, read tokens act like a subset of R2_SPI_* values. + */ +#define SPI_TOKEN_SINGLE 0xfe /* single block r/w, multiblock read */ +#define SPI_TOKEN_MULTI_WRITE 0xfc /* multiblock write */ +#define SPI_TOKEN_STOP_TRAN 0xfd /* terminate multiblock write */ + +#define MMC_SPI_BLOCKSIZE 512 + + +/* These fixed timeouts come from the latest SD specs, which say to ignore + * the CSD values. The R1B value is for card erase (e.g. the "I forgot the + * card's password" scenario); it's mostly applied to STOP_TRANSMISSION after + * reads which takes nowhere near that long. Older cards may be able to use + * shorter timeouts ... but why bother? + */ +#define r1b_timeout (HZ * 3) + +/* One of the critical speed parameters is the amount of data which may + * be transferred in one command. If this value is too low, the SD card + * controller has to do multiple partial block writes (argggh!). With + * today (2008) SD cards there is little speed gain if we transfer more + * than 64 KBytes at a time. So use this value until there is any indication + * that we should do more here. + */ +#define MMC_SPI_BLOCKSATONCE 128 + +/****************************************************************************/ + +/* + * Local Data Structures + */ + +/* "scratch" is per-{command,block} data exchanged with the card */ +struct scratch { + u8 status[29]; + u8 data_token; + __be16 crc_val; +}; + +struct mmc_spi_host { + struct mmc_host *mmc; + struct spi_device *spi; + + unsigned char power_mode; + u16 powerup_msecs; + + struct mmc_spi_platform_data *pdata; + + /* for bulk data transfers */ + struct spi_transfer token, t, crc, early_status; + struct spi_message m; + + /* for status readback */ + struct spi_transfer status; + struct spi_message readback; + + + /* buffer used for commands and for message "overhead" */ + struct scratch *data; + + /* Specs say to write ones most of the time, even when the card + * has no need to read its input data; and many cards won't care. + * This is our source of those ones. + */ + void *ones; +}; + + +/****************************************************************************/ + +/* + * MMC-over-SPI protocol glue, used by the MMC stack interface + */ + +static inline int mmc_cs_off(struct mmc_spi_host *host) +{ + /* chipselect will always be inactive after setup() */ + return spi_setup(host->spi); +} + +static int +mmc_spi_readbytes(struct mmc_spi_host *host, unsigned len) +{ + int status; + + if (len > sizeof(*host->data)) { + WARN_ON(1); + return -EIO; + } + host->status.len = len; + status = spi_sync_locked(host->spi, &host->readback); + return status; +} + +static int mmc_spi_skip(struct mmc_spi_host *host, unsigned long timeout, + unsigned n, u8 byte) +{ + u8 *cp = host->data->status; + unsigned long start = jiffies; + + while (1) { + int status; + unsigned i; + + status = mmc_spi_readbytes(host, n); + if (status < 0) + return status; + + for (i = 0; i < n; i++) { + if (cp[i] != byte) + return cp[i]; + } + + if (time_is_before_jiffies(start + timeout)) + break; + + /* If we need long timeouts, we may release the CPU. + * We use jiffies here because we want to have a relation + * between elapsed time and the blocking of the scheduler. + */ + if (time_is_before_jiffies(start+1)) + schedule(); + } + return -ETIMEDOUT; +} + +static inline int +mmc_spi_wait_unbusy(struct mmc_spi_host *host, unsigned long timeout) +{ + return mmc_spi_skip(host, timeout, sizeof(host->data->status), 0); +} + +static int mmc_spi_readtoken(struct mmc_spi_host *host, unsigned long timeout) +{ + return mmc_spi_skip(host, timeout, 1, 0xff); +} + + +/* + * Note that for SPI, cmd->resp[0] is not the same data as "native" protocol + * hosts return! The low byte holds R1_SPI bits. The next byte may hold + * R2_SPI bits ... for SEND_STATUS, or after data read errors. + * + * cmd->resp[1] holds any four-byte response, for R3 (READ_OCR) and on + * newer cards R7 (IF_COND). + */ + +static char *maptype(struct mmc_command *cmd) +{ + switch (mmc_spi_resp_type(cmd)) { + case MMC_RSP_SPI_R1: return "R1"; + case MMC_RSP_SPI_R1B: return "R1B"; + case MMC_RSP_SPI_R2: return "R2/R5"; + case MMC_RSP_SPI_R3: return "R3/R4/R7"; + default: return "?"; + } +} + +/* return zero, else negative errno after setting cmd->error */ +static int mmc_spi_response_get(struct mmc_spi_host *host, + struct mmc_command *cmd, int cs_on) +{ + u8 *cp = host->data->status; + u8 *end = cp + host->t.len; + int value = 0; + int bitshift; + u8 leftover = 0; + unsigned short rotator; + int i; + char tag[32]; + + snprintf(tag, sizeof(tag), " ... CMD%d response SPI_%s", + cmd->opcode, maptype(cmd)); + + /* Except for data block reads, the whole response will already + * be stored in the scratch buffer. It's somewhere after the + * command and the first byte we read after it. We ignore that + * first byte. After STOP_TRANSMISSION command it may include + * two data bits, but otherwise it's all ones. + */ + cp += 8; + while (cp < end && *cp == 0xff) + cp++; + + /* Data block reads (R1 response types) may need more data... */ + if (cp == end) { + cp = host->data->status; + end = cp+1; + + /* Card sends N(CR) (== 1..8) bytes of all-ones then one + * status byte ... and we already scanned 2 bytes. + * + * REVISIT block read paths use nasty byte-at-a-time I/O + * so it can always DMA directly into the target buffer. + * It'd probably be better to memcpy() the first chunk and + * avoid extra i/o calls... + * + * Note we check for more than 8 bytes, because in practice, + * some SD cards are slow... + */ + for (i = 2; i < 16; i++) { + value = mmc_spi_readbytes(host, 1); + if (value < 0) + goto done; + if (*cp != 0xff) + goto checkstatus; + } + value = -ETIMEDOUT; + goto done; + } + +checkstatus: + bitshift = 0; + if (*cp & 0x80) { + /* Houston, we have an ugly card with a bit-shifted response */ + rotator = *cp++ << 8; + /* read the next byte */ + if (cp == end) { + value = mmc_spi_readbytes(host, 1); + if (value < 0) + goto done; + cp = host->data->status; + end = cp+1; + } + rotator |= *cp++; + while (rotator & 0x8000) { + bitshift++; + rotator <<= 1; + } + cmd->resp[0] = rotator >> 8; + leftover = rotator; + } else { + cmd->resp[0] = *cp++; + } + cmd->error = 0; + + /* Status byte: the entire seven-bit R1 response. */ + if (cmd->resp[0] != 0) { + if ((R1_SPI_PARAMETER | R1_SPI_ADDRESS) + & cmd->resp[0]) + value = -EFAULT; /* Bad address */ + else if (R1_SPI_ILLEGAL_COMMAND & cmd->resp[0]) + value = -ENOSYS; /* Function not implemented */ + else if (R1_SPI_COM_CRC & cmd->resp[0]) + value = -EILSEQ; /* Illegal byte sequence */ + else if ((R1_SPI_ERASE_SEQ | R1_SPI_ERASE_RESET) + & cmd->resp[0]) + value = -EIO; /* I/O error */ + /* else R1_SPI_IDLE, "it's resetting" */ + } + + switch (mmc_spi_resp_type(cmd)) { + + /* SPI R1B == R1 + busy; STOP_TRANSMISSION (for multiblock reads) + * and less-common stuff like various erase operations. + */ + case MMC_RSP_SPI_R1B: + /* maybe we read all the busy tokens already */ + while (cp < end && *cp == 0) + cp++; + if (cp == end) + mmc_spi_wait_unbusy(host, r1b_timeout); + break; + + /* SPI R2 == R1 + second status byte; SEND_STATUS + * SPI R5 == R1 + data byte; IO_RW_DIRECT + */ + case MMC_RSP_SPI_R2: + /* read the next byte */ + if (cp == end) { + value = mmc_spi_readbytes(host, 1); + if (value < 0) + goto done; + cp = host->data->status; + end = cp+1; + } + if (bitshift) { + rotator = leftover << 8; + rotator |= *cp << bitshift; + cmd->resp[0] |= (rotator & 0xFF00); + } else { + cmd->resp[0] |= *cp << 8; + } + break; + + /* SPI R3, R4, or R7 == R1 + 4 bytes */ + case MMC_RSP_SPI_R3: + rotator = leftover << 8; + cmd->resp[1] = 0; + for (i = 0; i < 4; i++) { + cmd->resp[1] <<= 8; + /* read the next byte */ + if (cp == end) { + value = mmc_spi_readbytes(host, 1); + if (value < 0){ + printk("i=%d, resp=%x\r\n", i, cmd->resp[1]); + goto done; + } + cp = host->data->status; + end = cp+1; + } + if (bitshift) { + rotator |= *cp++ << bitshift; + cmd->resp[1] |= (rotator >> 8); + rotator <<= 8; + } else { + cmd->resp[1] |= *cp++; + } + } + break; + + /* SPI R1 == just one status byte */ + case MMC_RSP_SPI_R1: + break; + + default: + dev_dbg(&host->spi->dev, "bad response type %04x\n", + mmc_spi_resp_type(cmd)); + if (value >= 0) + value = -EINVAL; + goto done; + } + + if (value < 0) + dev_dbg(&host->spi->dev, "%s: resp %04x %08x\n", + tag, cmd->resp[0], cmd->resp[1]); + + /* disable chipselect on errors and some success cases */ + if (value >= 0 && cs_on) + return value; +done: + if (value < 0) + cmd->error = value; + mmc_cs_off(host); + return value; +} + +/* Issue command and read its response. + * Returns zero on success, negative for error. + * + * On error, caller must cope with mmc core retry mechanism. That + * means immediate low-level resubmit, which affects the bus lock... + */ +static int +mmc_spi_command_send(struct mmc_spi_host *host, + struct mmc_request *mrq, + struct mmc_command *cmd, int cs_on) +{ + struct scratch *data = host->data; + u8 *cp = data->status; + int status; + + u8 tx[29]; + cp = tx; + + struct spi_transfer *t; + + /* We can handle most commands (except block reads) in one full + * duplex I/O operation before either starting the next transfer + * (data block or command) or else deselecting the card. + * + * First, write 7 bytes: + * - an all-ones byte to ensure the card is ready + * - opcode byte (plus start and transmission bits) + * - four bytes of big-endian argument + * - crc7 (plus end bit) ... always computed, it's cheap + * + * We init the whole buffer to all-ones, which is what we need + * to write while we're reading (later) response data. + */ + memset(cp, 0xff, sizeof(data->status)); + + cp[1] = 0x40 | cmd->opcode; + put_unaligned_be32(cmd->arg, cp+2); + cp[6] = crc7_be(0, cp+1, 5) | 0x01; + cp += 7; + + /* Then, read up to 13 bytes (while writing all-ones): + * - N(CR) (== 1..8) bytes of all-ones + * - status byte (for all response types) + * - the rest of the response, either: + * + nothing, for R1 or R1B responses + * + second status byte, for R2 responses + * + four data bytes, for R3 and R7 responses + * + * Finally, read some more bytes ... in the nice cases we know in + * advance how many, and reading 1 more is always OK: + * - N(EC) (== 0..N) bytes of all-ones, before deselect/finish + * - N(RC) (== 1..N) bytes of all-ones, before next command + * - N(WR) (== 1..N) bytes of all-ones, before data write + * + * So in those cases one full duplex I/O of at most 21 bytes will + * handle the whole command, leaving the card ready to receive a + * data block or new command. We do that whenever we can, shaving + * CPU and IRQ costs (especially when using DMA or FIFOs). + * + * There are two other cases, where it's not generally practical + * to rely on a single I/O: + * + * - R1B responses need at least N(EC) bytes of all-zeroes. + * + * In this case we can *try* to fit it into one I/O, then + * maybe read more data later. + * + * - Data block reads are more troublesome, since a variable + * number of padding bytes precede the token and data. + * + N(CX) (== 0..8) bytes of all-ones, before CSD or CID + * + N(AC) (== 1..many) bytes of all-ones + * + * In this case we currently only have minimal speedups here: + * when N(CR) == 1 we can avoid I/O in response_get(). + */ + if (cs_on && (mrq->data->flags & MMC_DATA_READ)) { + cp += 2; /* min(N(CR)) + status */ + /* R1 */ + } else { + cp += 10; /* max(N(CR)) + status + min(N(RC),N(WR)) */ + if (cmd->flags & MMC_RSP_SPI_S2) /* R2/R5 */ + cp++; + else if (cmd->flags & MMC_RSP_SPI_B4) /* R3/R4/R7 */ + cp += 4; + else if (cmd->flags & MMC_RSP_BUSY) /* R1B */ + cp = tx + sizeof(tx); + /* else: R1 (most commands) */ + } + + dev_dbg(&host->spi->dev, " mmc_spi: CMD%d, resp %s\n", + cmd->opcode, maptype(cmd)); + + /* send command, leaving chipselect active */ + spi_message_init(&host->m); + + t = &host->t; + memset(t, 0, sizeof(*t)); + t->tx_buf = tx; + t->rx_buf = data->status; + t->len = cp - tx; + t->cs_change = 1; + spi_message_add_tail(t, &host->m); + + status = spi_sync_locked(host->spi, &host->m); + if (status < 0) { + dev_dbg(&host->spi->dev, " ... write returned %d\n", status); + cmd->error = status; + return status; + } + + /* after no-data commands and STOP_TRANSMISSION, chipselect off */ + return mmc_spi_response_get(host, cmd, cs_on); +} + +/* Build data message with up to four separate transfers. For TX, we + * start by writing the data token. And in most cases, we finish with + * a status transfer. + * + * We always provide TX data for data and CRC. The MMC/SD protocol + * requires us to write ones; but Linux defaults to writing zeroes; + * so we explicitly initialize it to all ones on RX paths. + * + * We also handle DMA mapping, so the underlying SPI controller does + * not need to (re)do it for each message. + */ +static void +mmc_spi_setup_data_message( + struct mmc_spi_host *host, + int multiple, + enum dma_data_direction direction) +{ + struct spi_transfer *t; + struct scratch *scratch = host->data; + + spi_message_init(&host->m); + + /* for reads, readblock() skips 0xff bytes before finding + * the token; for writes, this transfer issues that token. + */ + if (direction == DMA_TO_DEVICE) { + t = &host->token; + memset(t, 0, sizeof(*t)); + t->len = 1; + if (multiple) + scratch->data_token = SPI_TOKEN_MULTI_WRITE; + else + scratch->data_token = SPI_TOKEN_SINGLE; + t->tx_buf = &scratch->data_token; + spi_message_add_tail(t, &host->m); + } + + /* Body of transfer is buffer, then CRC ... + * either TX-only, or RX with TX-ones. + */ + t = &host->t; + memset(t, 0, sizeof(*t)); + t->tx_buf = host->ones; + /* length and actual buffer info are written later */ + spi_message_add_tail(t, &host->m); + + t = &host->crc; + memset(t, 0, sizeof(*t)); + t->len = 2; + if (direction == DMA_TO_DEVICE) { + /* the actual CRC may get written later */ + t->tx_buf = &scratch->crc_val; + } else { + t->tx_buf = host->ones; + t->rx_buf = &scratch->crc_val; + } + spi_message_add_tail(t, &host->m); + + /* + * A single block read is followed by N(EC) [0+] all-ones bytes + * before deselect ... don't bother. + * + * Multiblock reads are followed by N(AC) [1+] all-ones bytes before + * the next block is read, or a STOP_TRANSMISSION is issued. We'll + * collect that single byte, so readblock() doesn't need to. + * + * For a write, the one-byte data response follows immediately, then + * come zero or more busy bytes, then N(WR) [1+] all-ones bytes. + * Then single block reads may deselect, and multiblock ones issue + * the next token (next data block, or STOP_TRAN). We can try to + * minimize I/O ops by using a single read to collect end-of-busy. + */ + if (multiple || direction == DMA_TO_DEVICE) { + t = &host->early_status; + memset(t, 0, sizeof(*t)); + t->len = (direction == DMA_TO_DEVICE) + ? sizeof(scratch->status) + : 1; + t->tx_buf = host->ones; + t->rx_buf = scratch->status; + t->cs_change = 1; + spi_message_add_tail(t, &host->m); + } +} + +/* + * Write one block: + * - caller handled preceding N(WR) [1+] all-ones bytes + * - data block + * + token + * + data bytes + * + crc16 + * - an all-ones byte ... card writes a data-response byte + * - followed by N(EC) [0+] all-ones bytes, card writes zero/'busy' + * + * Return negative errno, else success. + */ +static int +mmc_spi_writeblock(struct mmc_spi_host *host, struct spi_transfer *t, + unsigned long timeout) +{ + struct spi_device *spi = host->spi; + int status, i; + struct scratch *scratch = host->data; + u32 pattern; + + if (host->mmc->use_spi_crc) + scratch->crc_val = cpu_to_be16( + crc_itu_t(0, t->tx_buf, t->len)); + + status = spi_sync_locked(spi, &host->m); + + if (status != 0) { + dev_dbg(&spi->dev, "write error (%d)\n", status); + return status; + } + + /* + * Get the transmission data-response reply. It must follow + * immediately after the data block we transferred. This reply + * doesn't necessarily tell whether the write operation succeeded; + * it just says if the transmission was ok and whether *earlier* + * writes succeeded; see the standard. + * + * In practice, there are (even modern SDHC-)cards which are late + * in sending the response, and miss the time frame by a few bits, + * so we have to cope with this situation and check the response + * bit-by-bit. Arggh!!! + */ + pattern = get_unaligned_be32(scratch->status); + + /* First 3 bit of pattern are undefined */ + pattern |= 0xE0000000; + + /* left-adjust to leading 0 bit */ + while (pattern & 0x80000000) + pattern <<= 1; + /* right-adjust for pattern matching. Code is in bit 4..0 now. */ + pattern >>= 27; + + switch (pattern) { + case SPI_RESPONSE_ACCEPTED: + status = 0; + break; + case SPI_RESPONSE_CRC_ERR: + /* host shall then issue MMC_STOP_TRANSMISSION */ + status = -EILSEQ; + break; + case SPI_RESPONSE_WRITE_ERR: + /* host shall then issue MMC_STOP_TRANSMISSION, + * and should MMC_SEND_STATUS to sort it out + */ + status = -EIO; + break; + default: + status = -EPROTO; + break; + } + if (status != 0) { + dev_dbg(&spi->dev, "write error %02x (%d)\n", + scratch->status[0], status); + return status; + } + + t->tx_buf += t->len; + + /* Return when not busy. If we didn't collect that status yet, + * we'll need some more I/O. + */ + for (i = 4; i < sizeof(scratch->status); i++) { + /* card is non-busy if the most recent bit is 1 */ + if (scratch->status[i] & 0x01) + return 0; + } + return mmc_spi_wait_unbusy(host, timeout); +} + +/* + * Read one block: + * - skip leading all-ones bytes ... either + * + N(AC) [1..f(clock,CSD)] usually, else + * + N(CX) [0..8] when reading CSD or CID + * - data block + * + token ... if error token, no data or crc + * + data bytes + * + crc16 + * + * After single block reads, we're done; N(EC) [0+] all-ones bytes follow + * before dropping chipselect. + * + * For multiblock reads, caller either reads the next block or issues a + * STOP_TRANSMISSION command. + */ +static int +mmc_spi_readblock(struct mmc_spi_host *host, struct spi_transfer *t, + unsigned long timeout) +{ + struct spi_device *spi = host->spi; + int status; + struct scratch *scratch = host->data; + unsigned int bitshift; + u8 leftover; + + /* At least one SD card sends an all-zeroes byte when N(CX) + * applies, before the all-ones bytes ... just cope with that. + */ + status = mmc_spi_readbytes(host, 1); + if (status < 0) + return status; + status = scratch->status[0]; + if (status == 0xff || status == 0) + status = mmc_spi_readtoken(host, timeout); + + if (status < 0) { + printk("read error %02x (%d)\n", status, status); + return status; + } + + /* The token may be bit-shifted... + * the first 0-bit precedes the data stream. + */ + bitshift = 7; + while (status & 0x80) { + status <<= 1; + bitshift--; + } + leftover = status << 1; + + status = spi_sync_locked(spi, &host->m); + + if (bitshift) { + /* Walk through the data and the crc and do + * all the magic to get byte-aligned data. + */ + u8 *cp = t->rx_buf; + unsigned int len; + unsigned int bitright = 8 - bitshift; + u8 temp; + for (len = t->len; len; len--) { + temp = *cp; + *cp++ = leftover | (temp >> bitshift); + leftover = temp << bitright; + } + cp = (u8 *) &scratch->crc_val; + temp = *cp; + *cp++ = leftover | (temp >> bitshift); + leftover = temp << bitright; + temp = *cp; + *cp = leftover | (temp >> bitshift); + } + + //print_hex_dump(KERN_ERR, "READ DATA: ", 0, 16, 1, + // host->t.rx_buf, host->t.len, 0); + if (host->mmc->use_spi_crc) { + u16 crc = crc_itu_t(0, t->rx_buf, t->len); + + be16_to_cpus(&scratch->crc_val); + if (scratch->crc_val != crc) { + gpio_set_value(66, 1); + gpio_set_value(66, 0); + printk("read - crc error: crc_val=0x%04x, " + "computed=0x%04x len=%d\n", + scratch->crc_val, crc, t->len); + return -EILSEQ; + }else{ + //printk("read - crc OK, len=%d\r\n", t->len); + } + } + + t->rx_buf += t->len; + + return 0; +} + +/* + * An MMC/SD data stage includes one or more blocks, optional CRCs, + * and inline handshaking. That handhaking makes it unlike most + * other SPI protocol stacks. + */ +static void +mmc_spi_data_do(struct mmc_spi_host *host, struct mmc_command *cmd, + struct mmc_data *data, u32 blk_size) +{ + struct spi_device *spi = host->spi; + struct spi_transfer *t; + enum dma_data_direction direction; + struct scatterlist *sg; + unsigned n_sg; + int multiple = (data->blocks > 1); + u32 clock_rate; + unsigned long timeout; + + if (data->flags & MMC_DATA_READ) + direction = DMA_FROM_DEVICE; + else + direction = DMA_TO_DEVICE; + mmc_spi_setup_data_message(host, multiple, direction); + t = &host->t; + + if (t->speed_hz) + clock_rate = t->speed_hz; + else + clock_rate = spi->max_speed_hz; + + timeout = data->timeout_ns + + data->timeout_clks * 1000000 / clock_rate; + timeout = usecs_to_jiffies((unsigned int)(timeout / 1000)) + 1; + + /* Handle scatterlist segments one at a time, with synch for + * each 512-byte block + */ + for (sg = data->sg, n_sg = data->sg_len; n_sg; n_sg--, sg++) { + int status = 0; + void *kmap_addr; + unsigned length = sg->length; + enum dma_data_direction dir = direction; + + /* set up dma mapping for controller drivers that might + * use DMA ... though they may fall back to PIO + */ + /* allow pio too; we don't allow highmem */ + kmap_addr = kmap(sg_page(sg)); + if (direction == DMA_TO_DEVICE) + t->tx_buf = kmap_addr + sg->offset; + else + t->rx_buf = kmap_addr + sg->offset; + + /* transfer each block, and update request status */ + while (length) { + t->len = min(length, blk_size); + + dev_dbg(&host->spi->dev, + " mmc_spi: %s block, %d bytes\n", + (direction == DMA_TO_DEVICE) + ? "write" + : "read", + t->len); + + if (direction == DMA_TO_DEVICE) + status = mmc_spi_writeblock(host, t, timeout); + else + status = mmc_spi_readblock(host, t, timeout); + if (status < 0) + break; + + data->bytes_xfered += t->len; + length -= t->len; + + if (!multiple) + break; + } + + /* discard mappings */ + if (direction == DMA_FROM_DEVICE) + flush_kernel_dcache_page(sg_page(sg)); + kunmap(sg_page(sg)); + + if (status < 0) { + data->error = status; + dev_dbg(&spi->dev, "%s status %d\n", + (direction == DMA_TO_DEVICE) + ? "write" : "read", + status); + break; + } + } + + /* NOTE some docs describe an MMC-only SET_BLOCK_COUNT (CMD23) that + * can be issued before multiblock writes. Unlike its more widely + * documented analogue for SD cards (SET_WR_BLK_ERASE_COUNT, ACMD23), + * that can affect the STOP_TRAN logic. Complete (and current) + * MMC specs should sort that out before Linux starts using CMD23. + */ + if (direction == DMA_TO_DEVICE && multiple) { + struct scratch *scratch = host->data; + int tmp; + const unsigned statlen = sizeof(scratch->status); + + dev_dbg(&spi->dev, " mmc_spi: STOP_TRAN\n"); + + /* Tweak the per-block message we set up earlier by morphing + * it to hold single buffer with the token followed by some + * all-ones bytes ... skip N(BR) (0..1), scan the rest for + * "not busy any longer" status, and leave chip selected. + */ + INIT_LIST_HEAD(&host->m.transfers); + list_add(&host->early_status.transfer_list, + &host->m.transfers); + + memset(scratch->status, 0xff, statlen); + scratch->status[0] = SPI_TOKEN_STOP_TRAN; + + host->early_status.tx_buf = host->early_status.rx_buf; + host->early_status.len = statlen; + + tmp = spi_sync_locked(spi, &host->m); + + if (tmp < 0) { + if (!data->error) + data->error = tmp; + return; + } + + /* Ideally we collected "not busy" status with one I/O, + * avoiding wasteful byte-at-a-time scanning... but more + * I/O is often needed. + */ + for (tmp = 2; tmp < statlen; tmp++) { + if (scratch->status[tmp] != 0) + return; + } + tmp = mmc_spi_wait_unbusy(host, timeout); + if (tmp < 0 && !data->error) + data->error = tmp; + } +} + +/****************************************************************************/ + +/* + * MMC driver implementation -- the interface to the MMC stack + */ + +static void mmc_spi_request(struct mmc_host *mmc, struct mmc_request *mrq) +{ + struct mmc_spi_host *host = mmc_priv(mmc); + int status = -EINVAL; + int crc_retry = 5; + struct mmc_command stop; + +#ifdef DEBUG + /* MMC core and layered drivers *MUST* issue SPI-aware commands */ + { + struct mmc_command *cmd; + int invalid = 0; + + cmd = mrq->cmd; + if (!mmc_spi_resp_type(cmd)) { + dev_dbg(&host->spi->dev, "bogus command\n"); + cmd->error = -EINVAL; + invalid = 1; + } + + cmd = mrq->stop; + if (cmd && !mmc_spi_resp_type(cmd)) { + dev_dbg(&host->spi->dev, "bogus STOP command\n"); + cmd->error = -EINVAL; + invalid = 1; + } + + if (invalid) { + dump_stack(); + mmc_request_done(host->mmc, mrq); + return; + } + } +#endif + + /* request exclusive bus access */ + spi_bus_lock(host->spi->master); + +crc_recover: + /* issue command; then optionally data and stop */ + status = mmc_spi_command_send(host, mrq, mrq->cmd, mrq->data != NULL); + if (status == 0 && mrq->data) { + mmc_spi_data_do(host, mrq->cmd, mrq->data, mrq->data->blksz); + + /* + * The SPI bus is not always reliable for large data transfers. + * If an occasional crc error is reported by the SD device with + * data read/write over SPI, it may be recovered by repeating + * the last SD command again. The retry count is set to 5 to + * ensure the driver passes stress tests. + */ + if (mrq->data->error == -EILSEQ && crc_retry && mmc->card->type != MMC_TYPE_SDIO) { + stop.opcode = MMC_STOP_TRANSMISSION; + stop.arg = 0; + stop.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC; + status = mmc_spi_command_send(host, mrq, &stop, 0); + crc_retry--; + mrq->data->error = 0; + goto crc_recover; + } + + if (mrq->stop) + status = mmc_spi_command_send(host, mrq, mrq->stop, 0); + else + mmc_cs_off(host); + } + + /* release the bus */ + spi_bus_unlock(host->spi->master); + + mmc_request_done(host->mmc, mrq); +} + +/* See Section 6.4.1, in SD "Simplified Physical Layer Specification 2.0" + * + * NOTE that here we can't know that the card has just been powered up; + * not all MMC/SD sockets support power switching. + * + * FIXME when the card is still in SPI mode, e.g. from a previous kernel, + * this doesn't seem to do the right thing at all... + */ +static void mmc_spi_initsequence(struct mmc_spi_host *host) +{ + /* Try to be very sure any previous command has completed; + * wait till not-busy, skip debris from any old commands. + */ + mmc_spi_wait_unbusy(host, r1b_timeout); + mmc_spi_readbytes(host, 10); + + /* + * Do a burst with chipselect active-high. We need to do this to + * meet the requirement of 74 clock cycles with both chipselect + * and CMD (MOSI) high before CMD0 ... after the card has been + * powered up to Vdd(min), and so is ready to take commands. + * + * Some cards are particularly needy of this (e.g. Viking "SD256") + * while most others don't seem to care. + * + * Note that this is one of the places MMC/SD plays games with the + * SPI protocol. Another is that when chipselect is released while + * the card returns BUSY status, the clock must issue several cycles + * with chipselect high before the card will stop driving its output. + */ + host->spi->mode |= SPI_CS_HIGH; + if (spi_setup(host->spi) != 0) { + /* Just warn; most cards work without it. */ + dev_warn(&host->spi->dev, + "can't change chip-select polarity\n"); + host->spi->mode &= ~SPI_CS_HIGH; + } else { + mmc_spi_readbytes(host, 18); + + host->spi->mode &= ~SPI_CS_HIGH; + if (spi_setup(host->spi) != 0) { + /* Wot, we can't get the same setup we had before? */ + dev_err(&host->spi->dev, + "can't restore chip-select polarity\n"); + } + } +} + +static char *mmc_powerstring(u8 power_mode) +{ + switch (power_mode) { + case MMC_POWER_OFF: return "off"; + case MMC_POWER_UP: return "up"; + case MMC_POWER_ON: return "on"; + } + return "?"; +} + +static void mmc_spi_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) +{ + struct mmc_spi_host *host = mmc_priv(mmc); + + if (host->power_mode != ios->power_mode) { + int canpower; + + canpower = host->pdata && host->pdata->setpower; + + dev_dbg(&host->spi->dev, "mmc_spi: power %s (%d)%s\n", + mmc_powerstring(ios->power_mode), + ios->vdd, + canpower ? ", can switch" : ""); + + /* switch power on/off if possible, accounting for + * max 250msec powerup time if needed. + */ + if (canpower) { + switch (ios->power_mode) { + case MMC_POWER_OFF: + case MMC_POWER_UP: + host->pdata->setpower(&host->spi->dev, + ios->vdd); + if (ios->power_mode == MMC_POWER_UP) + msleep(host->powerup_msecs); + } + } + + /* See 6.4.1 in the simplified SD card physical spec 2.0 */ + if (ios->power_mode == MMC_POWER_ON) + mmc_spi_initsequence(host); + + /* If powering down, ground all card inputs to avoid power + * delivery from data lines! On a shared SPI bus, this + * will probably be temporary; 6.4.2 of the simplified SD + * spec says this must last at least 1msec. + * + * - Clock low means CPOL 0, e.g. mode 0 + * - MOSI low comes from writing zero + * - Chipselect is usually active low... + */ + if (canpower && ios->power_mode == MMC_POWER_OFF) { + int mres; + u8 nullbyte = 0; + + host->spi->mode &= ~(SPI_CPOL|SPI_CPHA); + mres = spi_setup(host->spi); + if (mres < 0) + dev_dbg(&host->spi->dev, + "switch to SPI mode 0 failed\n"); + + if (spi_write(host->spi, &nullbyte, 1) < 0) + dev_dbg(&host->spi->dev, + "put spi signals to low failed\n"); + + /* + * Now clock should be low due to spi mode 0; + * MOSI should be low because of written 0x00; + * chipselect should be low (it is active low) + * power supply is off, so now MMC is off too! + * + * FIXME no, chipselect can be high since the + * device is inactive and SPI_CS_HIGH is clear... + */ + msleep(10); + if (mres == 0) { + host->spi->mode |= (SPI_CPOL|SPI_CPHA); + mres = spi_setup(host->spi); + if (mres < 0) + dev_dbg(&host->spi->dev, + "switch back to SPI mode 3" + " failed\n"); + } + } + + host->power_mode = ios->power_mode; + } + + if (host->spi->max_speed_hz != ios->clock && ios->clock != 0) { + int status; + + host->spi->max_speed_hz = ios->clock; + status = spi_setup(host->spi); + dev_dbg(&host->spi->dev, + "mmc_spi: clock to %d Hz, %d\n", + host->spi->max_speed_hz, status); + } +} + +static const struct mmc_host_ops mmc_spi_ops = { + .request = mmc_spi_request, + .set_ios = mmc_spi_set_ios, + .get_ro = mmc_gpio_get_ro, + .get_cd = mmc_gpio_get_cd, +}; + + +/****************************************************************************/ + +/* + * SPI driver implementation + */ + +static irqreturn_t +mmc_spi_detect_irq(int irq, void *mmc) +{ + struct mmc_spi_host *host = mmc_priv(mmc); + u16 delay_msec = max(host->pdata->detect_delay, (u16)100); + + mmc_detect_change(mmc, msecs_to_jiffies(delay_msec)); + return IRQ_HANDLED; +} + +static int mmc_spi_probe(struct spi_device *spi) +{ + void *ones; + struct mmc_host *mmc; + struct mmc_spi_host *host; + int status; + bool has_ro = false; + + gpio_request(66, "gpio66"); + gpio_direction_output(66, 0); + + /* We rely on full duplex transfers, mostly to reduce + * per-transfer overheads (by making fewer transfers). + */ + if (spi->master->flags & SPI_MASTER_HALF_DUPLEX) + return -EINVAL; + + /* MMC and SD specs only seem to care that sampling is on the + * rising edge ... meaning SPI modes 0 or 3. So either SPI mode + * should be legit. We'll use mode 0 since the steady state is 0, + * which is appropriate for hotplugging, unless the platform data + * specify mode 3 (if hardware is not compatible to mode 0). + */ + if (spi->mode != SPI_MODE_3) + spi->mode = SPI_MODE_0; + spi->bits_per_word = 8; + + status = spi_setup(spi); + if (status < 0) { + dev_info(&spi->dev, "needs SPI mode %02x, %d KHz; %d\n", + spi->mode, spi->max_speed_hz / 1000, + status); + return status; + } + + /* We need a supply of ones to transmit. This is the only time + * the CPU touches these, so cache coherency isn't a concern. + * + * NOTE if many systems use more than one MMC-over-SPI connector + * it'd save some memory to share this. That's evidently rare. + */ + status = -ENOMEM; + ones = kmalloc(MMC_SPI_BLOCKSIZE, GFP_KERNEL); + if (!ones) + goto nomem; + memset(ones, 0xff, MMC_SPI_BLOCKSIZE); + + mmc = mmc_alloc_host(sizeof(*host), &spi->dev); + if (!mmc) + goto nomem; + + mmc->ops = &mmc_spi_ops; + mmc->max_blk_size = MMC_SPI_BLOCKSIZE; + mmc->max_segs = MMC_SPI_BLOCKSATONCE; + mmc->max_req_size = MMC_SPI_BLOCKSATONCE * MMC_SPI_BLOCKSIZE; + mmc->max_blk_count = MMC_SPI_BLOCKSATONCE; + + mmc->caps = MMC_CAP_SPI; + + /* SPI doesn't need the lowspeed device identification thing for + * MMC or SD cards, since it never comes up in open drain mode. + * That's good; some SPI masters can't handle very low speeds! + * + * However, low speed SDIO cards need not handle over 400 KHz; + * that's the only reason not to use a few MHz for f_min (until + * the upper layer reads the target frequency from the CSD). + */ + mmc->f_min = 400000; + mmc->f_max = spi->max_speed_hz; + + host = mmc_priv(mmc); + host->mmc = mmc; + host->spi = spi; + + host->ones = ones; + + /* Platform data is used to hook up things like card sensing + * and power switching gpios. + */ + host->pdata = mmc_spi_get_pdata(spi); + if (host->pdata) + mmc->ocr_avail = host->pdata->ocr_mask; + if (!mmc->ocr_avail) { + dev_warn(&spi->dev, "ASSUMING 3.2-3.4 V slot power\n"); + mmc->ocr_avail = MMC_VDD_32_33|MMC_VDD_33_34; + } + if (host->pdata && host->pdata->setpower) { + host->powerup_msecs = host->pdata->powerup_msecs; + if (!host->powerup_msecs || host->powerup_msecs > 250) + host->powerup_msecs = 250; + } + + dev_set_drvdata(&spi->dev, mmc); + + /* preallocate dma buffers */ + host->data = kmalloc(sizeof(*host->data), GFP_KERNEL); + if (!host->data) + goto fail_nobuf1; + + + /* setup message for status/busy readback */ + spi_message_init(&host->readback); + host->readback.is_dma_mapped = 0; + + spi_message_add_tail(&host->status, &host->readback); + host->status.tx_buf = host->ones; + host->status.rx_buf = &host->data->status; + host->status.cs_change = 1; + + /* register card detect irq */ + if (host->pdata && host->pdata->init) { + status = host->pdata->init(&spi->dev, mmc_spi_detect_irq, mmc); + if (status != 0) + goto fail_glue_init; + } + + /* pass platform capabilities, if any */ + if (host->pdata) { + mmc->caps |= host->pdata->caps; + mmc->caps2 |= host->pdata->caps2; + } + mmc->caps &= ~MMC_CAP_NEEDS_POLL; + + status = mmc_add_host(mmc); + if (status != 0) + goto fail_add_host; + + if (host->pdata && host->pdata->flags & MMC_SPI_USE_CD_GPIO) { + status = mmc_gpio_request_cd(mmc, host->pdata->cd_gpio, + host->pdata->cd_debounce); + if (status != 0) + goto fail_add_host; + + /* The platform has a CD GPIO signal that may support + * interrupts, so let mmc_gpiod_request_cd_irq() decide + * if polling is needed or not. + */ + mmc->caps &= ~MMC_CAP_NEEDS_POLL; + mmc_gpiod_request_cd_irq(mmc); + } + mmc_detect_change(mmc, 0); + + if (host->pdata && host->pdata->flags & MMC_SPI_USE_RO_GPIO) { + has_ro = true; + status = mmc_gpio_request_ro(mmc, host->pdata->ro_gpio); + if (status != 0) + goto fail_add_host; + } + + dev_info(&spi->dev, "SD/MMC host %s%s%s%s%s\n", + dev_name(&mmc->class_dev), + ", no DMA", + has_ro ? "" : ", no WP", + (host->pdata && host->pdata->setpower) + ? "" : ", no poweroff", + (mmc->caps & MMC_CAP_NEEDS_POLL) + ? ", cd polling" : ""); + return 0; + +fail_add_host: + mmc_remove_host (mmc); +fail_glue_init: +fail_data_dma: +fail_ones_dma: + kfree(host->data); + +fail_nobuf1: + mmc_free_host(mmc); + mmc_spi_put_pdata(spi); + dev_set_drvdata(&spi->dev, NULL); + +nomem: + kfree(ones); + return status; +} + + +static int mmc_spi_remove(struct spi_device *spi) +{ + struct mmc_host *mmc = dev_get_drvdata(&spi->dev); + struct mmc_spi_host *host; + + if (mmc) { + host = mmc_priv(mmc); + + /* prevent new mmc_detect_change() calls */ + if (host->pdata && host->pdata->exit) + host->pdata->exit(&spi->dev, mmc); + + mmc_remove_host(mmc); + + kfree(host->data); + kfree(host->ones); + + spi->max_speed_hz = mmc->f_max; + mmc_free_host(mmc); + mmc_spi_put_pdata(spi); + dev_set_drvdata(&spi->dev, NULL); + } + return 0; +} + +static const struct of_device_id mmc_spi_of_match_table[] = { + { .compatible = "mmc-spi-slot", }, + {}, +}; +MODULE_DEVICE_TABLE(of, mmc_spi_of_match_table); + +static struct spi_driver mmc_spi_driver = { + .driver = { + .name = "mmc_spi", + .of_match_table = mmc_spi_of_match_table, + }, + .probe = mmc_spi_probe, + .remove = mmc_spi_remove, +}; + +module_spi_driver(mmc_spi_driver); + +MODULE_AUTHOR("Mike Lavender, David Brownell, " + "Hans-Peter Nilsson, Jan Nikitenko"); +MODULE_DESCRIPTION("SPI SD/MMC host driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("spi:mmc_spi"); diff --git a/utils/ota.c b/utils/ota.c new file mode 100644 index 0000000..54e3372 --- /dev/null +++ b/utils/ota.c @@ -0,0 +1,309 @@ + +#ifdef __RTOS__ +#include +#include +#include +#else +#include +#include +#include +#include +#include +#include +#include +#include +#endif + +#include "../hgic_def.h" +#include "fwinfo.h" +#include "fwctrl.h" +#include "ota.h" +#include "utils.h" + +static u16 hgic_ota_check_sum(u8 *addr, s32 count) +{ + s32 sum = 0; + while (count > 1) { + sum = sum + *(u16 *)addr; + addr += 2; + count -= 2; + } + if (count > 0) { + sum += *addr; + } + while (sum >> 16) { + sum = (sum & 0xffff) + (sum >> 16); + } + return (u16)~sum; +} + +static struct sk_buff *hgic_ota_send_packet_tmo(struct hgic_ota *ota, struct sk_buff *skb, u32 tmo) +{ + struct hgic_cmd_response resp; + struct hgic_hdr *hdr = NULL; + + hdr = (struct hgic_hdr *)skb_push(skb, sizeof(struct hgic_hdr)); + hdr->magic = HGIC_HDR_TX_MAGIC; + hdr->type = HGIC_HDR_TYPE_OTA; + hdr->ifidx = HGIC_WDEV_ID_STA; + hdr->length = skb->len; + hdr->cookie = hgic_ctrl_cookie(ota->ctrl); + memset(&resp, 0, sizeof(resp)); + resp.cookie = hdr->cookie; + hgic_fwctrl_send_data(ota->ctrl, skb, &resp, tmo); + return resp.skb; +} + +static void hgic_ota_release_data(struct hgic_ota *ota) +{ + hgic_dbg("Enter ...\n"); + hgic_clear_queue(&ota->fw_dataq); + hgic_dbg("Leave ...\n"); +} + +void hgic_ota_release(struct hgic_ota *ota) +{ + hgic_dbg("Enter ...\n"); + if (ota->inited) { + hgic_clear_queue(&ota->fw_dataq); +#ifdef __RTOS__ + skb_queue_head_deinit(&ota->fw_dataq); +#endif + } + hgic_dbg("Leave ...\n"); +} + +s32 hgic_ota_init(struct hgic_ota *ota, struct hgic_fwctrl *ctrl, struct hgic_fw_info *fw_info) +{ + hgic_dbg("Enter ...\n"); + memset(ota, 0, sizeof(struct hgic_ota)); + ota->frag_size = HG_OTA_FW_FLAGMENT_SIZE; + ota->ctrl = ctrl; + ota->ori_fw_info = fw_info; + ota->inited = 1; + skb_queue_head_init(&ota->fw_dataq); + hgic_dbg("Leave ...\n"); + return 0; +} + +static s32 hgic_ota_fill_hdr(struct hgic_ota *ota, struct sk_buff *skb, u32 offset) +{ + struct hgic_ota_hdr *hdr = NULL; + u32 payload_len = 0; + + if (ota == NULL || skb == NULL || skb->len > ota->frag_size) { + hgic_err("Input para error!\r\n"); + return -EINVAL; + } + if (ota->fw_len == 0) { + hgic_err("ERROR:fw len is zero!\r\n"); + return -EINVAL; + } + payload_len = skb->len; + + hdr = (struct hgic_ota_hdr *)skb_push(skb, sizeof(struct hgic_ota_hdr)); + memset(hdr, 0, sizeof(struct hgic_ota_hdr)); + hdr->chipid = ota->ori_fw_info->chip_id; + hdr->len = payload_len; + hdr->tot_len = ota->fw_len; + hdr->off = offset; + hdr->version = ota->ori_fw_info->version; + hdr->checksum = hgic_ota_check_sum(hdr->data, hdr->len); + return 0; +} + +static s32 hgic_ota_check_hdr_info(struct hgic_ota *ota, const u8 *data) +{ + u32 sdk_version = 0; + u32 svn_version = 0; + u16 chip_id = 0; + u8 cpu_id = 0; + s32 err_code = 0; + + hgic_dbg("%s:Original fw info:%d.%d.%d.%d\r\n", __FUNCTION__, + (ota->ori_fw_info->version >> 24) & 0xff, (ota->ori_fw_info->version >> 16) & 0xff, + (ota->ori_fw_info->version >> 8) & 0xff, (ota->ori_fw_info->version & 0xff)); + + hgic_dbg("%s:Chip id:%x,cpu id:%d\r\n", __FUNCTION__, + ota->ori_fw_info->chip_id, ota->ori_fw_info->cpu_id); + + sdk_version = hgic_get_fw_sdk_version(data, &err_code); + if (err_code != 0) { + hgic_err("%s:Get ota fw sdk version error!ret:%d\n", __FUNCTION__, err_code); + return err_code; + } + if ((sdk_version & 0xff) != (ota->ori_fw_info->version & 0xff)) { + hgic_err("%s:firmware version not match!%x vs %x\n", __FUNCTION__, sdk_version & 0xff, ota->ori_fw_info->version & 0xff); + return -EFAULT; + } + + svn_version = hgic_get_fw_svn_version(data, &err_code); + if (err_code != 0) { + hgic_err("%s:Get ota fw svn version error!ret:%d\n", __FUNCTION__, err_code); + return err_code; + } + + chip_id = hgic_get_fw_chipid(data, &err_code); + if (err_code != 0) { + hgic_err("%s:Get ota fw chip_id error!ret:%d\n", __FUNCTION__, err_code); + return err_code; + } + if (chip_id != ota->ori_fw_info->chip_id) { + hgic_err("%s:firmware version not match!\n", __FUNCTION__); + return -EFAULT; + } + + cpu_id = hgic_get_fw_cpuid(data, &err_code); + if (err_code != 0) { + hgic_err("%s:Get ota fw CPU id error!ret:%d\n", __FUNCTION__, err_code); + return err_code; + } + if (cpu_id != ota->ori_fw_info->cpu_id) { + hgic_err("%s:firmware CPU id not match!\n", __FUNCTION__); + return -EFAULT; + } + + return 0; +} + +static s32 hgic_ota_parse_fw(struct hgic_ota *ota, char *fw_name) +{ + struct sk_buff *skb = NULL; + const struct firmware *fw; + u32 copy_len = 0; + u32 xfer_len = 0; + u32 addr_offset = 0; + s32 ret = 0; + const u8 *data; + const u32 hdr_len = sizeof(struct hgic_ota_hdr) + sizeof(struct hgic_hdr); + u32 fw_hdr_len = 0; + s32 err_code = 0; + s32 crc_result = 0; + + if (ota == NULL) { + hgic_err("Input para error!\r\n"); + return -EINVAL; + } + + hgic_dbg("Check para:fw_name:%s,hdr_len:%d\n", fw_name, hdr_len); + + ret = request_firmware(&fw, (const char *)fw_name, ota->ctrl->dev); + if (ret) { + hgic_dbg("request_firmware failed!ret:%d\n", ret); + return -ENODEV; + } + + ret = hgic_ota_check_hdr_info(ota, fw->data); + if (ret != 0) { + hgic_err("hgic_ota_check_info error!\r\n"); + return ret; + } + + crc_result = hgic_get_fw_code_checksum(fw->data, fw->size); + if (crc_result) { + hgic_err("hgic_get_fw_code_checksum error!\r\n"); + return crc_result; + } + + fw_hdr_len = hgic_get_fw_code_offset(fw->data, &err_code); + if (err_code != 0) { + hgic_err("hgic_get_fw_code_offset error!\r\n"); + return err_code; + } + + //ota->fw_len = fw->size - fw_hdr_len; + ota->fw_len = fw->size; + copy_len = ota->fw_len; + data = (char *)fw->data; + hgic_dbg("Check fw para:fw_len:%d,each fragment size:%d\n", ota->fw_len, ota->frag_size); + + while (copy_len) { + xfer_len = copy_len > ota->frag_size ? ota->frag_size : copy_len; + //hgic_dbg("Check xfer para:xfer_len:%d,offset:%d\n", xfer_len, addr_offset); + skb = dev_alloc_skb(xfer_len + hdr_len + 4); + if (skb == NULL) { + printk("%s: no memory\r\n", __FUNCTION__); + release_firmware(fw); + return -ENOMEM; + } + skb_reserve(skb, hdr_len); + memcpy(skb->data, data + addr_offset, xfer_len); + skb_put(skb, xfer_len); + + ret = hgic_ota_fill_hdr(ota, skb, addr_offset); + if (ret) { + hgic_err("hgic_ota_fill_frame_hdr error!\n"); + release_firmware(fw); + return -EFAULT; + } + + skb_queue_tail(&ota->fw_dataq, skb); + copy_len -= xfer_len; + addr_offset += xfer_len; + } + release_firmware(fw); + return 0; +} + +s32 hgic_ota_send_fw(struct hgic_ota *ota, char *fw_name, u32 tmo) +{ + s32 ret = 0; + struct sk_buff *resp = NULL; + struct hgic_ota_hdr *hdr = NULL; + struct sk_buff *skb = NULL; + + if (ota == NULL) { + printk("%s: Input para error!\r\n", __FUNCTION__); + ret = -EINVAL; + goto __failed; + } + + ret = hgic_ota_parse_fw(ota, fw_name); + if (ret) { + hgic_err("Firmware parse error!\n"); + goto __failed; + } + + while (!skb_queue_empty(&ota->fw_dataq)) { + skb = skb_dequeue(&ota->fw_dataq); + resp = hgic_ota_send_packet_tmo(ota, skb, tmo); + if (resp) { + skb_pull(resp, sizeof(struct hgic_hdr)); + hdr = (struct hgic_ota_hdr *)resp->data; + if (hdr->err_code) { + hgic_err("Responce Error:Error code:%d\n", hdr->err_code); + ret = hdr->err_code; + goto __failed; + } else { + hgic_dbg("OTA write to flash success!\n"); + } + } else { + hgic_err("Get responce timeout or no responce!\n"); + ret = -ENODEV; + goto __failed; + } + } + hgic_ota_release_data(ota); + hgic_dbg("hgic_ota_send_fw success!!!\n"); + return 0; + +__failed: + hgic_ota_release_data(ota); + hgic_dbg("hgic_ota_send_fw failed!!!\n"); + return ret; +} + +#ifdef __RTOS__ +int hgic_ota_start(char *ifname, char *fw_name) +{ + struct net_device *ndev = net_device_get_by_name(ifname); + + if (ndev) { + return hgic_ota_send_fw(hgic_devota(ndev), fw_name, HG_OTA_WRITE_MEM_TMO); + } else { + hgic_dbg("Can not find netdev name:%s\n",ifname); + } + return -ENODEV; +} +#endif + diff --git a/utils/ota.h b/utils/ota.h new file mode 100644 index 0000000..4821665 --- /dev/null +++ b/utils/ota.h @@ -0,0 +1,29 @@ +#ifndef _HGIC_OTA_H_ +#define _HGIC_OTA_H_ + +#include "../hgic_def.h" + +#define HG_OTA_WRITE_MEM_TMO 5000 +#define HG_OTA_NORMAL_TMO 100 +#define HG_OTA_FW_FLAGMENT_SIZE 1500 + +enum HGIC_OTA_RESP_ERR_CODE{ + HGIC_OTA_RESP_ERR_OK=0, + HGIC_OTA_RESP_ERR_CHECKSUM, + HGIC_OTA_RESP_ERR_WRITE, +}; + +struct hgic_ota { + u32 inited; + struct sk_buff_head fw_dataq; + u32 frag_size; + u32 fw_len; + struct hgic_fwctrl *ctrl; + struct hgic_fw_info *ori_fw_info; +}; + +s32 hgic_ota_init(struct hgic_ota *ota, struct hgic_fwctrl *ctrl, struct hgic_fw_info *fw_info); +void hgic_ota_release(struct hgic_ota *ota); +s32 hgic_ota_send_fw(struct hgic_ota *hg, char *fw_name, u32 tmo); + +#endif diff --git a/utils/utils.c b/utils/utils.c new file mode 100644 index 0000000..e99ddb9 --- /dev/null +++ b/utils/utils.c @@ -0,0 +1,269 @@ +#ifdef __RTOS__ +#include +#include +#include +#include +#ifdef HGIC_SMAC +#include "umac_config.h" +#endif +#else +#include +#include +#include +#include +#include +#include +#endif +#include "utils.h" +#include "../hgic_def.h" + +#define aSymbolLength 40 +#define STATBUF_SIZE (64*1024) +#define SAFE_DIV(a, b) (((b) == 0) ? 0 : ((a) / (b))) + +int hgic_skip_padding(struct sk_buff *skb) +{ + int i = 0; + + for (i = 0; i < 3 && skb->data[i] == 0xFF; i++); + if (i) { + if (skb->len > 0) { + skb_pull(skb, i); + } else { + skb->data += i; + skb->tail += i; + } + } + return i; +} +EXPORT_SYMBOL(hgic_skip_padding); + +#if 0 +int hgic_aligned_padding(struct sk_buff *skb) +{ + uint32_t i = 0; + uint32_t count = 0; + uint8_t *data = skb->data - 4; + if (!IS_ALIGNED((uint32_t)skb->data, 4)) { + count = (uint32_t)skb->data - (uint32_t)ALIGN((uint32_t)data, 4); + if (count > 0) { + skb_push(skb, count); + for (i = 0; i < count; i++) { + skb->data[i] = 0xFF; + } + } + } + return count; +} +EXPORT_SYMBOL(hgic_aligned_padding); +#endif + +void hgic_print_hex(char *buf, int len) +{ + int i = 0; + + for (i = 0; i < len; i++) { + if (i > 0 && i % 16 == 0) { printk("\r\n"); } + else if (i > 0 && i % 8 == 0) { printk(" "); } + printk("%02x ", buf[i] & 0xff); + } + printk("\r\n\r\n"); +} +EXPORT_SYMBOL(hgic_print_hex); + +int hgic_config_read_int(char *conf, char *field) +{ + char *ptr = strstr(conf, field); + if (ptr) { + return simple_strtol(ptr + strlen(field) + 1, 0, 10); + } + return 0; +} +EXPORT_SYMBOL(hgic_config_read_int); + +int hgic_config_read_str(char *conf, char *field, char *str, int size) +{ + char *ptr = strstr(conf, field); + if (ptr) { + ptr += strlen(field) + 1; + while (*ptr && *ptr != '\r' && *ptr != '\n' && size > 0) { + *str++ = *ptr++; + size--; + } + return 0; + } + return -1; +} +EXPORT_SYMBOL(hgic_config_read_str); + +int hgic_config_read_u32_array(char *conf, char *field, u32 *arr, int count) +{ + int cnt = 0; + int val = 0; + char *ptr = strstr(conf, field); + + if (ptr) { + ptr += strlen(field) + 1; + while (cnt < count) { + while (*ptr >= '0' && *ptr <= '9') { + val *= 10; + val += (*ptr - 0x30); + ptr++; + } + if (val) { + arr[cnt++] = val; + } + + if (*ptr != ',' || val == 0) { + break; + } + ptr++; + val = 0; + } + } + + return cnt; +} +EXPORT_SYMBOL(hgic_config_read_u32_array); + +int hgic_config_read_u16_array(char *conf, char *field, u16 *arr, int count) +{ + int cnt = 0; + int val = 0; + char *ptr = strstr(conf, field); + + if (ptr) { + ptr += strlen(field) + 1; + while (cnt < count) { + while (*ptr >= '0' && *ptr <= '9') { + val *= 10; + val += (*ptr - 0x30); + ptr++; + } + if (val) { + arr[cnt++] = val; + } + + if (*ptr != ',' || val == 0) { + break; + } + ptr++; + val = 0; + } + } + + return cnt; +} +EXPORT_SYMBOL(hgic_config_read_u16_array); + +void hgic_clear_queue(struct sk_buff_head *q) +{ + ulong flags = 0; + struct sk_buff *skb = NULL; + struct sk_buff *tmp = NULL; + + spin_lock_irqsave(&q->lock, flags); + if (!skb_queue_empty(q)) { + skb_queue_walk_safe(q, skb, tmp) { + __skb_unlink(skb, q); + kfree_skb(skb); + } + } + spin_unlock_irqrestore(&q->lock, flags); +} + +int hgic_hex2num(char c) +{ + if (c >= '0' && c <= '9') { + return c - '0'; + } + if (c >= 'a' && c <= 'f') { + return c - 'a' + 10; + } + if (c >= 'A' && c <= 'F') { + return c - 'A' + 10; + } + return -1; +} + + +int hgic_hex2byte(const char *hex) +{ + int a, b; + a = hgic_hex2num(*hex++); + if (a < 0) { + return -1; + } + b = hgic_hex2num(*hex++); + if (b < 0) { + return -1; + } + return (a << 4) | b; +} + +int hgic_pick_macaddr(char *mac_str, u8 *addr) +{ + int i = 0; + int val = 0; + const char *ptr = (const char *)mac_str; + + memset(addr, 0, 6); + while (ptr && i < 6 && strlen(mac_str) >= 17) { + if (i < 5 && ptr[2] != ':') { break; } + val = hgic_hex2byte(ptr); + if (val < 0) { break; } + addr[i++] = (u8)val; + ptr += 3; + } + return (i == 6); +} + + +#if defined(__RTOS__) && defined(HGIC_SMAC) +int umac_config_read(const char *name, char *buff, int size) +{ + int ret = 0; + struct umac_config *cfg = sys_get_umaccfg(); + struct net_device *ndev = net_device_get_by_name(name); + + if (ndev == NULL) { + PRINTF("dev:%s is not exist!\r\n", name); + return -1; + } + if (ndev->ifindex == 0) { + strcpy(buff, (const char *)cfg->hg0); + buff[strlen(cfg->hg0)] = 0; + } else if (ndev->ifindex == 1) { + strcpy(buff, (const char *)cfg->hg1); + buff[strlen(cfg->hg1)] = 0; + } else { + ret = -1; + } + PRINTF("read %s:\r\n%s\r\n", name, buff); + return ret; +} + +int umac_config_write(const char *name, char *buff, int size) +{ + int ret = 0; + struct umac_config *cfg = sys_get_umaccfg(); + struct net_device *ndev = net_device_get_by_name(name); + + PRINTF("write %s:\r\n%s\r\n", name, buff); + if (ndev == NULL) { + PRINTF("dev:%s is not exist!\r\n", name); + return -1; + } + if ((ndev->ifindex == 0) && size < sizeof(cfg->hg0)) { + strcpy((char *)cfg->hg0, buff); + } else if ((ndev->ifindex == 1) && size < sizeof(cfg->hg1)) { + strcpy((char *)cfg->hg1, buff); + } else { + ret = -1; + } + if (!ret) { + ret = sys_save_umaccfg(cfg); + } + return ret; +} +#endif diff --git a/utils/utils.h b/utils/utils.h new file mode 100644 index 0000000..9714d96 --- /dev/null +++ b/utils/utils.h @@ -0,0 +1,180 @@ +#ifndef _HGIC_UTILS_H_ +#define _HGIC_UTILS_H_ + +#define AC_NUM 4 +#define MAX_CHANS_NUM 16 + +struct hgic_fwstat_chaninfo { + u32 freq; + u8 pri_chan; + s8 bgrssi_min; + s8 bgrssi_max; + s8 bgrssi_avg; + s32 bgrssi_acc; + s32 cnt; + s32 rxsync_cnt; + s32 noise_factor; +}; + +struct hgic_fwstat_testmode { + u32 test_tx_start : 1, bss_freq : 24; + s32 freq_dev; + s32 chip_temp; + s32 tx_frms; + s32 tx_fail; + s32 tx_mcs; + s32 tx_sig_bw; + s32 rx_pkts; + s32 rx_firm; + s32 rx_err; + s32 rx_rssi; + s32 agc; + s32 rx_evm; + u8 chip_vcc[8]; +}; + +struct hgic_fwstat_qa { + u8 dut_mac[6]; + u16 svn_version; + s8 result_per; + s8 cfg_per; + s8 rssi; + s8 tssi; + s8 rx_evm; + s8 tx_evm; + s8 rx_freq_dev; + s8 tx_freq_dev; + s8 rx_rssi_th; + s8 tx_tssi_th; + s8 rx_evm_th; + s8 tx_evm_th; + s8 rx_freq_dev_th; + s8 tx_freq_dev_th; +}; + +struct hgic_fwstat_stainfo { + u8 addr[6]; + s32 tx_frms; + s32 tx_frms_success; + s32 tx_cnt; + s32 tx_apep; + s32 tx_cca; + s32 tx_apep_success; + s32 tx_apep_droped; + s32 tx_frms_droped; + s32 tx_symbols; + s32 freq_offset; + u32 rx_cnt; + u32 rx_pkts; + u32 rx_bytes; + u32 rx_fcs_err; + u32 rx_symbols; + + u8 tx_bw : 3, tx_mcs : 4; + u8 rx_bw : 4, rx_mcs : 4; + s8 evm_avg, evm_std, rssi, tx_snr; + u16 aid, agc; + u16 fade_bw_ind[4]; + u64 tx_jiffies; +}; + +struct hgic_fwstat { + u8 mode; + u8 mac_address[6]; + u16 aid; + u32 bss_freq_idx : 8, bss_freq : 24; + u32 pri1m_start : 16, pri2m_start : 16; + u32 pri1m_mid : 16, pri2m_mid : 16; + u32 sec2m_mid : 16, sec4m_mid : 16; + u32 pri1m_ed : 16, pri2m_ed : 16; + u32 sec2m_ed : 16, sec4m_ed : 16; + u8 tx_power_auto_adjust_en : 1, tx_pwr_adj : 5; + u8 chan_cnt; + u8 rx_duty_cycle; + u8 tx_pwr; + s8 bg_rssi; + s32 bgrssi_iqacc; + s32 demod_dly_max; + s32 sifs_dly_max; + s32 resp_dly_max; + s32 resp_sifs_to; + s32 resp_ack_to; + s32 frms_ack_to; + s32 rx_ovf_cnt; + s32 rx_nobuf_cnt; + s32 rx_bus_max; + s16 bgrssi_spur_thd; + s16 bgrssi_spur_det; + s16 bgrssi_spurs; + s16 bgrssi_iqmax; + s16 rx_dc_i; + s16 rx_dc_q; + u32 lo_kick_cnt : 16, chan_switch_cnt : 16; + s32 soft_rest; + u32 lmac_txsq_count; + u32 lmac_txq_count; + u32 lmac_acq_count[AC_NUM]; + u32 lmac_txagg_count[AC_NUM]; + u32 lmac_statq_count; + s32 lmac_rx_data_queue_count; + s32 ac_irq_cnt; + s32 ac_dly_max; + s32 tx_irq_rts; + s32 bo_irq_cnt[AC_NUM]; + s32 cts_tmo_cnt; + s32 resp_tmo_cnt; + s32 rx_irq_cnt; + s32 tx_cnt; + s32 tx_cts_bw_acc; + s32 tx_cts_cnt; + s32 tx_cts_evm_acc; + s32 tx_frms; + s32 tx_sq_empty; + s32 agg_no_data; + s32 agg_check_fail; + s32 tx_apep; + s32 tx_symbols; + s32 tx_cca; + s32 tx_fail; + s32 tx_drop; + s32 rx_cnt; + s32 rx_cts_bw_acc; + s32 rx_cts_cnt; + s32 rx_cts_mcs_acc; + s32 rx_pkts; + s32 rx_bytes; + s32 rx_symbols; + s32 rx_phy_err; + s32 rx_fcs_err; + s32 phy_err_code; + s32 tx_irq_bkn; + + u8 lmac_doze : 1, cca_obsv_dur : 3; + + s32 sta_tx_syms; + s32 sta_rx_syms; + u32 est_tx_bitrate; + u32 sta_num; + u8 fixed_tx_mcs; + int skb_free_count; + + struct hgic_fwstat_chaninfo chan_list[MAX_CHANS_NUM]; + struct hgic_fwstat_testmode test_mode; + struct hgic_fwstat_qa qa_stat; + struct hgic_fwstat_stainfo sta_info[0]; +}; + +char *hgic_fwstat_print(u8 *stat_buf); +int hgic_skip_padding(struct sk_buff *skb); +int hgic_aligned_padding(struct sk_buff *skb); +void hgic_print_hex(char *buf, int len); +int hgic_config_read_u32_array(char *conf, char *field, u32 *arr, int count); +int hgic_config_read_u16_array(char *conf, char *field, u16 *arr, int count); +int hgic_config_read_str(char *conf, char *field, char *str, int size); +int hgic_config_read_int(char *conf, char *field); +void hgic_clear_queue(struct sk_buff_head *q); +int hgic_hex2num(char c); +int hgic_hex2byte(const char *hex); +int hgic_pick_macaddr(char *mac_str, u8 *addr); + +#endif diff --git a/version.h b/version.h new file mode 100644 index 0000000..c8226e5 --- /dev/null +++ b/version.h @@ -0,0 +1 @@ +#define SVN_VERSION "14988"